40 KiB
Границы знания
Этот раздел перечисляет области, где контракт ещё не закрыт полностью. Они не мешают безопасному чтению и lossless сохранению, но не должны превращаться в authoring API без динамического подтверждения.
Render state
Доказаны frame boundaries, world traversal, material resolve и крупные проходы. Не доказаны символами точные имена renderer vtable slots, полный набор CShade state transitions и окончательный порядок части transparent/FX/shadow subpasses.
Закрывающий эксперимент: запустить оригинал в совместимой Windows/DirectX среде, перехватить DirectDraw/Direct3D calls и surface flips, сохранить state log на минимальных сценах с одним типом материала.
FXID field-level semantics
Размеры команд, resource references, lifecycle, flags families и используемые time modes известны. Не закрыто значение каждого поля body opcodes 1--10, отсутствующий во всех проверенных каталогах opcode 6 и точные формулы редких time modes.
Закрывающий эксперимент: изменять по одному полю копии эффекта, воспроизводить
его в контролируемой сцене и логировать runtime command object, emitted
primitives, sound events и reads в Effect.dll.
Script VM
Доступны packages, symbols, event sections, variable declarations и version
checks. Полная instruction grammar .scr, semantics opcodes и serialization
state ещё не восстановлены.
Для GOG ai.dll статически восстановлены пять ветвей evaluator-а 16-byte
записей условий по RVA 0x5180, включая проверки типов, поиск объектов,
короткие выходы из обхода списков и запись байта завершения. Однако не найден
код-производитель этого массива и контракт его владельца с runtime. Reader
FUN_10011b20 хранит описатели событий и вложенных записей .scr во внутренних
контейнерах с шагом 0x14 и 0x20; сами дисковые структуры имеют переменную
длину из-за length-prefixed имён и списков ссылок. Связь этих контейнеров с
массивом evaluator-а не доказана. Виртуальные методы,
через которые проходят проверки, остаются внешней границей, а сам evaluator
не сравнивался с работающим оригиналом. Описание этих ветвей — статический
вывод по одному известному ai.dll, а не утверждение о совпадении во время
исполнения.
Для начального сценарного события закрыт более узкий путь. В проверенной GOG
сборке ai.dll SHA-256
c19cd6cd31cd2896f8c4ba0f0e700dd3408aec8182daaf4863d98a7d233a2abc функции
FUN_10011f40 (RVA 0x11f40) и FUN_10011f10 (RVA 0x11f10) выбирают первое
имя-событие C-string Init и запускают его инструкции по порядку. Для координат
из TMA найден и проверен ещё один участок: iron3d.dll SHA-256
d1dc4ea8535e2069b0a05c9e8be2724dd438ba44cca7a83007d8b2e50f9e35fa,
FUN_100392e0 (RVA 0x392e0) выполняет x87 FISTP qword и передаёт младшие
DWORD координат пятым и шестым аргументами CreateSuperAI. Затем
ai.dll!FUN_10005c70 (RVA 0x5c70) преобразует DWORD в float, а Handler(19)
возвращает его к DWORD усечением.
Две x86-проверки вызвали эти оригинальные DLL-функции на изолированной
синтетической памяти. Caller oracle подтвердил AutoDemo-координаты
(500.2857, 751.8217) -> (500, 752), ties-to-even в nearest-режиме и переход
-0.6 -> 0xffffffff. AI oracle проверил оба набора AutoDemo anchor-ов,
дублирующий Init, имя Init\\0suffix\\0, отсутствие события, а также
нативную связку FUN_10005c70 → Handler(19): DWORD (0xffffffff, 10001) стал
полями (2^32, 10001.0) и varset-записями (0, 10001, 2). Эти проверки
подтверждают только изолированные участки ABI.
В одном read-only snapshot запущенной GOG AutoDemo были связаны записи кланов
Iron3D и указатели GetSuperAI из ai.dll (таблица RVA 0x55398). Клан 0 имел
исходные float-координаты (500.2857, 751.8217) и поля AI
(+0x80, +0x84, +0x7c) = (500.0, 752.0, 0); клан 1 имел (728.0, 449.0) и
(728.0, 449.0, 1). Первый набор связывает те же значения в одном процессе и
выбирает ближайшее округление среди направлений округления x87. Синтетические
tie-векторы проверяют, как сам FISTP реагирует на каждый RC, но не измеряют
control word процесса.
Отдельно проверены загрузка лицензированного AutoDemo в Rust и preview
лицензированной кампании. AutoDemo даёт raw TMA anchor-ы
[(500.2857, 751.8217), (728.0, 449.0)] и записи [(500, 752, 0), (728, 449, 1)]; preview кампании сохраняет package и статус Unsupported,
не применяя частичные записи. Это подтверждает загрузчик и узкий Init resolver
Rust.
Нативный конструктор показывает предупреждение при координате выше 10000, но
продолжает работу. Rust моделирует числовую запись, а не это UI-предупреждение;
на первом FISTP qword он явно завершает загрузку ошибкой для NaN, infinity и
значений вне signed-qword диапазона. В первом событии Init Rust поддерживает
только action-форму Handler(19) (control word и result reference равны
0xffffffff). Неизвестная инструкция даёт Unsupported в preview, не применяя
накопленные частичные записи; при полной загрузке это ошибка. Это ограничение
Rust исполнителя, не правило оригинального dispatcher-а. Ошибочные ссылки и
типы остаются ошибками в обоих режимах.
Для GOG-сборки также найден статический host call path в Init. В
iron3d.dll!FUN_1005ff40 (RVA 0x5ff40) одна ветвь сначала проходит записи
кланов и вызывает для них FUN_100392e0, затем вызывает FUN_10039440 (RVA
0x39440). Эта функция передаёт AI callback-у пару явных аргументов через
vtable slot +0x14. В ai.dll!FUN_10001ae0 (RVA 0x1ae0) аргумент callback-а
записывается в AI+0x3fc; затем объект AI+0x438 получает вызов Behavior
vslot +0x2c. В проверенной Behavior.dll (SHA-256
ddfbdbde07832358fe6e35e01c8dba9b58e7313789498b6049ed51d4ed9baf99)
FUN_1001adb0 (RVA 0x1adb0) выполняет начальную подготовку расписания.
Метод ищет первый Init только при нулевом latch
AI+0x8a8, а при dispatch временно ставит и снимает recursion guard
AI+0x78. В этой ветви поэтому известны callback ingress и порядок
warm-start → Init, но смысл аргумента callback-а здесь не сопоставлен с Rust
MissionMode.
Изолированный x86 oracle вызывает исходные iron3d.dll!FUN_10039440,
ai.dll!FUN_10001ae0 и script dispatcher на синтетических записях и проверяет
режим, guard, latch, Handler(19) writes и повторный вызов. Отдельный oracle
исполняет Behavior.dll!FUN_1001adb0 с искусственно заданной очередью, сверяя
срок, шаг LCG и очистку полей. Это подтверждает ABI и локальные переходы исходных
функций, но не доказывает, что полная штатная загрузка выбранной миссии в
оригинальном процессе доходит до этой ветви в том же порядке. Метод
FUN_10001ae0 оставляет latch +0x8a8 сброшенным, когда Init не найдено;
если оно найдено при уже занятом guard +0x78, dispatch пропускается, но latch
всё равно устанавливается. Rust пока запускает обработанный поднабор при каждой
полной загрузке и не моделирует аргумент callback-а AI+0x3fc, guard +0x78 или
latch +0x8a8.
Rust load_mission принимает явный queue_time_ms, а не читает оригинальное
время очереди. Новый Engine начинает локальный Behavior RNG с seed 1 и
учитывает шаги генератора этой стартовой последовательности; не восстановлены
другие потребители исходного генератора, поэтому равенство полной случайной
последовательности не утверждается. Начальная фаза выполняется после регистрации
объектов в создаваемом мире Rust;
это интеграционный выбор runtime, а не подтверждённая эквивалентность всех
host construction phases. Не проверена другая лицензированная миссия с
непустым Init. Вызов Behavior.dll!FUN_10019e80 (RVA 0x19e80, SHA-256
ddfbdbde07832358fe6e35e01c8dba9b58e7313789498b6049ed51d4ed9baf99) исполнялся
на синтетических интерфейсах. Путь членства в реальных distributor-списках
связан с полями TMA и Unit DAT: World3D!CreateObject передаёт selector
и class word через дескриптор в Behavior (RVA 0x7e55, 0x7e62), а
Behavior.dll!FUN_10005e60 (RVA 0x5e60) проверяет высокий бит registration
key, AI и distributor. Selector из первого DWORD после имени ресурса выбирает
слот GetSuperAI; таблица этой функции находится в ai.dll RVA 0x55398.
Хеши MisLoad.dll, iron3d.dll, ArealMap.dll, World3D.dll и
Behavior.dll, использованных для цепочки, перечислены в
справочнике TMA.
Отдельный режимный бит Behavior нельзя выводить из MissionMode. Конструктор
Behavior (Behavior.dll, RVA 0x3600) вызывает World3D!stdGetGameMode (RVA
0x14750), который читает host-global по RVA 0x795130 в World3D.dll (SHA-256
17e4a3089b2583a8cf2356c9db0390b1aba138356a09130d79b4e7e4791da61e). В
обычном пути stdInitGame (RVA 0x13e90, запись режима около 0x14040)
формируется отдельное host-значение; в наблюдавшемся запуске AutoDemo getter
вернул 4, и бит 0x2 был снят. Это объясняет соответствующий gate при
создании Behavior, но не устанавливает отображение между host-режимом и Rust
MissionMode.
Наблюдение обычного запуска лицензированной GOG AutoDemo подтвердило четыре
добавления с ключами 0x80000003..0x80000006 в два клановых списка. Полная
загрузка Rust сохраняет соответствующие ObjectHandle в точном доступном слоте
GetSuperAI до стартового Init; preview не создаёт членств. Лицензированный
тест связывает эти записи с TMA ID 4–7, selector и Unit DAT class. Это
подтверждает только состав списков, не точную эквивалентность всей загрузки или
регулярного шага Behavior.
Нативный Behavior.dll!Add (RVA 0x1aac0) и normalize (RVA 0x1ac90)
проверены прямыми x86-вызовами на синтетических записях при x87 control word
0x007f, измеренном также у игры на входах обоих методов. Rust повторяет
начальные доли и нормализацию отдельными операциями f32, а повторную запись
сопоставляет по ObjectHandle, меняя только долю существующего участника.
Пять DWORD после доли хранятся сырыми; ненулевое первое слово закрепляет вес,
смысл четырёх следующих не установлен. Лицензированный AutoDemo-тест проверяет
список TMA-объектов и получившиеся начальные доли по кланам, но это не сравнение
живых весов миссии с полями исходных runtime-записей. Не восстановлено соединение
с реальными интерфейсами ArealMap, нужными FUN_10019e80; полный Behavior tick
flow и общий dispatcher событий также остаются открытыми.
Начальные свойства CurrentOre/MaximumOre восстановлены отдельно от
регулярного обмена. В проверенных Behavior.dll (SHA-256
ddfbdbde07832358fe6e35e01c8dba9b58e7313789498b6049ed51d4ed9baf99) RVA
0x8790 выбирает начальные значения по class word, 0xb110 регистрирует
дескрипторы кодов 4/5, 0xb470 пишет только код 5, а 0xb5b0 читает код 4
как дополнительное слово и код 5 как основное. MisLoad.dll (SHA-256
c607762bb67db14ca530927c8e1347339b62b0a163ac90c708ee83142292e2bf) RVA
0x40b0 выбирает первое свойство TMA с точным именем CurrentOre и передаёт
его raw_value[1]; setter сохраняет биты напрямую. В AutoDemo snapshot на этих
входах было восемь объектов Unit DAT, у всех raw CurrentOre равен нулю;
четыре объекта класса 0x01008000 не вошли в distributor, но остались среди
инициализированных Rust-состояний. Поэтому путь нулевого значения наблюдался
наживую, но ненулевое TMA-значение в живой миссии пока не проверено.
Синтетический x86 oracle вызвал Behavior.dll!FUN_10008790 напрямую для классов
0x80000004, 0x80000008, 0x01002000, 0x01004000 и 0x01008000; перед
вызовом в записи были значения CurrentOre=123 и MaximumOre=-1, так что
проверено их перезаписывание. Тест выставляет a64=1, чтобы отключить отложенный
side effect обёртки setter-а. Это подтверждает ветви initializer-а, но не весь
путь его запуска и связи с остальными фазами объекта. Другой изолированный x86
oracle проверяет ненулевое raw-слово CurrentOre, только чтение MaximumOre и
перестановку строк дескрипторов на синтетических записях свойств.
Не проверен селектор 4 для внешнего config-like ресурса: в наблюдавшемся
AutoDemo его вызова нет, а установленный Behavior.ini не содержит
Mine_MaxOre, Storage_MaxOre или Transport_MaxOre. Поэтому Rust пока
воспроизводит доказанные class defaults (500, 4000 и 2000) и не заявляет
поддержку конфигурационного переопределения. Отдельно не доказано, что каждый
Unit DAT создаёт тот же Behavior-компонент во всех host factory paths. Rust
ограничивает новую запись полными TMA-объектами с Unit DAT; preview, прямые
prototype roots без Unit DAT и отложенные spawn после исходной загрузки вне
этой модели. Путь в ArealMap.dll (SHA-256
a25f9254ebb339d70e2e07c8121950191e9584ca0858d8e9c2e2b6c671edf6ea) к
CreateObject по RVA 0x14fd0/0x15290 показывает выбор World3D class 3 или 4
по старшему биту. Для ветки GOG AniMesh.dll (SHA-256
65659bf67f20e5be77d67a5b2f59968f61b94293143807fa59ca451c560c4f7e) RVA
0x2ff0 показывает одну ветку создания: после QueryInterface для IID 0x204
выполняется отдельный импорт GetQueue(), а на возвращённом объекте вызывается
метод через vtable slot +0x30. CreateBehaviour вызывается только при
ненулевом результате этого метода и host class 3 или 4; class 4 получает
адреса полей prototype +0x140 и +0x160. Семантика метода slot +0x30 не
установлена. Другие factory paths, нулевой результат и spawn после исходной
загрузки остаются за границами подтверждённой ветки.
Для Handler(30) отдельно подтверждены обратный вызов iron3d.dll RVA
0x611d0 (SHA-256 d1dc4ea8535e2069b0a05c9e8be2724dd438ba44cca7a83007d8b2e50f9e35fa),
начальные поля IGame и проверки режима/состояния. Обработчик выбора миссии
выставляет режим 1 (RVA 0x29142), а обработчик таймера — режим 3 (RVA 0x70f3).
Небольшая тестовая x86-программа сравнивает этот обратный вызов с исходной DLL
на синтетических структурах. Пока не восстановлен сквозной путь от выбора и
инициализации миссии к первому событию Handler(30): Rust загружает миссию, но
ещё не выполняет штатный stdInitGame, переход состояния 5 -> 4, запуск и
игровой цикл с полной диспетчеризацией событий. Поэтому
совпадение изолированного обратного вызова не доказывает совместимость живого
сценария.
Закрывающий эксперимент: найти dispatcher loop в ai.dll, сопоставить jump
table с instruction sizes, построить disassembler и сравнить выполнение
коротких scripts с оригиналом. Для evaluator-а условий отдельно проследить
создание записей this + 0x34, выполнить те же входные данные в оригинале и
сверить байт завершения, порядок поиска, ранние выходы и граничный случай
payload == count.
Saves and campaign state
Найдены saveslots.cfg и missions/dispatcher.ini, но binary savegame payload,
serialization World3D/AI/script/RNG и migration rules не закрыты.
Нужны сохранения оригинала в контролируемых состояниях: старт миссии, изменение позиции, здоровья, order/path, FX/timer, script variable, research/economy, mission completion, pause и non-default game time.
Physical/control formats
CTLD и связанные resources структурно читаются, count patterns и variants известны. Не названы все секции, shape types, coefficients и точный contact solver. То же относится к редким MSH auxiliary streams и части CTPT/NDPR flags.
Одна цепочка Unit DAT → AniMesh descriptor → Control теперь восстановлена
статически в главе о Behavior и Control: raw
parent_or_link копируется в descriptor. В non-EXTO registry path parent ID
разрешается в индекс устройства и сдвигает локальный индекс только когда parent
ID не равен -1 и IID 0x202 доступен; иначе используется raw локальный индекс.
В этом потоке нулевой raw kind пропускает узел вместе с потомками, поэтому общий
assigned ID нельзя получать из preorder-позиции записи. Полная загрузка миссии
в FParkan строит упорядоченную базовую проекцию таблицы
Control для объектов с Unit DAT: корневые записи добавляются, EXTO добавляет
строки с ключами глобальной MSH-иерархии, а INTO обновляет первую строку
выбранного индекса либо добавляет список при sentinel -1. Проверка полной
загрузки на оригинальном min_l_n1.dat получает 11 строк: 10 корневых записей,
четыре замены INTO и одну добавленную строку дочернего EXTO. Ветви C4/C8 и
добавление EXTO подтверждены машинными инструкциями оригинальных модулей, но
полный результат FParkan не сравнивался с таблицей из запущенной игры. Тест на
искусственном дереве также проверяет ID после пропущенного поддерева с raw kind
0. Проекция хранит общие поля и происхождение записей; она не исполняет все
обработчики Control.
Control различает добавление устройства и применение одной записи к уже
выбранному устройству. Исполнение оригинальной 32-битной DLL на искусственных
данных подтверждает общую ветвь переопределения для получателей tags 9, 15,
19 и 25: остаются прежними слот, ключ, ID компонента, множитель и класс,
а коэффициенты копируются из новой записи. Искусственный tag 9-объект создан
без FX-хоста (+0x3c == 0), а запись не содержит строк таблицы map; поэтому
работа FX-хоста здесь не проверялась. В Rust parser-е
Unsupported { typed_record_index: Some(_) } означает, что разбор остановился
внутри списка typed records; без индекса остановки unsupported может относиться
к effect-tail после уже разобранного списка. Эти случаи нужно различать.
Ветка default native-фабрики вызывает конструктор по RVA 0x20800, поэтому
неизвестный Rust parser-у tag не доказывает отказ оригинального loader-а. Подготовка assets
сейчас пропускает Control refs у non-geometric prototype, поэтому отсутствие
подготовленного CTL должно оставаться явным unavailable result, а не
трактоваться как пустой список.
Global Control getters 1/2 и setter 1 проверены в native x86 oracle для
искусственных factory/descriptor pairs 9, 15 и 19, а в Rust реализованы
для текущего descriptor tag 19 с factory tags 9, 15, 19 и 25. Для tag
25 общий getter slot +0x10 подтверждён чтением vtable, но агрегатный вызов
этого класса native oracle не запускал. Rust API обращается к строкам проекции
конкретного живого ObjectHandle; это запрос к уже загруженной таблице, а не
исполнение Control или Behavior tick. Setter 17, остальные factory tags и
внешние эффекты их методов не включены. Для tag 9 selector 0 использует
отдельный scale +0x11c; остальные поддержанные классы используют коэффициент
напрямую. Пустой набор и нулевой знаменатель остаются native no-result, а
неподдержанный factory tag и расчётный диапазон произведения/отношения за
нормальным диапазоном f32 становятся явной ошибкой Rust.
Проекция полного mission load недоступна для неполного списка typed records,
Control resource у non-geometric prototype и некоторых неразрешённых EXTO node
mappings; preview и объекты без Unit DAT root её не строят. Её соответствие
полной загрузке игры для всех типов компонентов и factory paths ещё не
установлено. Setter 1 меняет multiplier +0x4c выбранных строк, но расписание
и lifetime этих объектов после конструктора и в отложенной загрузке остаются
непроверенными.
Для Control.dll (SHA-256
bcb86318089028548a178b03a21accdce15df0cc85ddd91674cf3c2f55012c6e) tag-26
getter selector 0x300 восстановлен локально: RVA 0x21d00 читает provider по
ключу fixed_words[1], затем умножает provider, CTLD coefficient fixed_words[11]
и поле объекта +0x4c именно в таком порядке. Provider getter RVA 0xdc40
читает type-1 поле +0x14 строки, которую FUN_10008b10 (RVA 0x8b10)
добавляет из NDP в таблицу с шагом 0x2c; FUN_1000b240 (RVA 0xb240)
инициализирует это поле как 1.0. Начальный Rust snapshot повторяет порядок
NDP строк корневого ресурса и примонтированных EXTO (без первой строки
последнего), сохраняя исходные таблицу и строку как provenance. Ключ устройства
сначала меняется по подтверждённому MSH mapping для EXTO, затем это готовое
значение применяется напрямую как индекс provider sequence. Длина этой
sequence определяется NDP: она не дополняется по числу узлов модели и ключ не
пересчитывается ещё раз по числу provider rows. INTO provider строк не
добавляет. Для примонтированного EXTO с пустым NDP невозможно безопасно
применить native skip-first путь, поэтому Rust явно сообщает unavailable;
таблица из одной строки допустима и даёт нулевой вклад. Если обязательный NDP
sidecar недоступен, запрос 0xe00 сообщает это отдельно, оставляя независимые
агрегатные selectors 1/2 доступными.
Поля parent_index, Control flags и начальный stage count для NDP-строк
проецируются из AniMesh по глобальному ordinal; точные правила, native ABI
векторы и лицензированная root/EXTO fixture описаны в
техническом подразделе.
Проверки подтверждают только начальный снимок: сборка runtime parent-ов
остаётся статическим выводом по RVA AniMesh.dll+0xa460, а последующие
изменения в полном Control/animation tick-е не проверялись.
Для provider-строки Rust также хранит одно initial_amount_bits. Нативный
Control.dll FUN_1000f940 (RVA 0xf940) кладёт результат selector 3 в
текущее и обработанное значения строки; при подтверждённых единичных множителях
это raw max_volume NDP. Последующее обновление меняет оба native поля, поэтому
снимок не моделирует вместимость или изменяемый Control amount.
Rust query обращается к загруженному объекту и поддерживает 0xe00 только для
текущего descriptor tag 26 с factory tag 26; другие активные typed tags и
factory paths остаются явными границами. Этот запрос возвращает начальный
type-1 provider value (1.0); дальнейшее изменение provider amount в Rust не
реализовано.
Native oracle проверил изолированные пути распределения, обновления класса 4,
стадийную арифметику 0x11220, обход дочерних provider-строк 0x11130 и
visibility transition 0x11920 с исходными AniMesh callbacks. Эти сценарии
используют синтетические NDP/Node38-строки; они подтверждают локальные переходы
и порядок вызовов, но не полную загрузку или выполнение Control на миссии. Вызовы
0x11220 используют updateEffects = false, что исключает ветвь Control FX и
0x11af0; в отдельном сценарии класса 9 0xfca0 возвращается сразу после
смены видимости, поэтому связанное с ним движение не проверено. Хотя у строки
Control выставлен flag 0x102, слово флагов ANI +0x14 и производные байты
+0x111..+0x113 изначально равны нулю. Вызовы AniMesh 0x93c0 и 0xa2a0 обновляют производную геометрию и
видимость, это отдельная непроверенная граница, а не FX callbacks. Активную
clock-ветвь не запускали; внешний источник времени и порядок вызовов в игре
остаются незакрытыми. Источник контактного коэффициента прослежен статически,
но не исполнен. Подробные векторы и статические границы приведены в
техническом подразделе о provider-состоянии Control.
Отдельный API advance_loaded_mission_resources принимает явное время очереди
и обновляет представленные поля CurrentOre, строк распределителя и Control
selector 1. API не подключён к игровому циклу и не обновляет Control amount,
профили, special object-state gate или жизненный цикл объектов. Если обязательный
запрос позднего объекта не поддержан, транзакция возвращает ошибку без применения
подготовленных изменений. AutoDemo-тест подтверждает связь настоящих профилей с
этим шагом, но не полный паритет миссии; отдельный aggregate getter Behavior RVA
0xa490 в oracle не исполнен.
Behavior selectors 0x1002 и 0x1005 используют значения выбранного
профиля из behpsp.res, разобранного до создания нового мира. 0x1002 складывает
только текущие descriptor tag 26 запросы 0xe00, затем применяет
Transfer_Ore_OffBoard; 0x1005 возвращает Transfer_Power_Out. Это начальный
снимок профиля и provider-строк. Полная загрузка сохраняет результат у
ObjectHandle и возвращает
ошибку, если известный код класса не имеет корректной записи; класс,
отсутствующий в таблице выбора, сохраняет начальные значения конструктора.
Preview и объекты без полного Unit DAT не получают профильное состояние.
Native oracle подтвердил соответствия кода класса выбранной записи, разбор
.var и применение второй области данных;
оригинальный aggregate getter Behavior RVA 0xa490 остаётся статически
исследованным, но не исполнен в oracle.
В x86 oracle при x87 control word 0x007f проверены порядок, signed zero,
отрицательные входы, расширенный промежуточный диапазон и subnormal return.
Rust запрос ограничивает себя normal-or-zero значениями, поэтому явно отклоняет
промежуточные случаи, где результат выходит за поддерживаемый диапазон f32,
даже если позднее умножение вернуло бы его в диапазон. Это ограничение
поддержки Rust snapshot, а не неверный native-результат.
В Unit DAT fixture UNITS/BUILDS/MINE/min_l_n1.dat проверена реальная связка
fr_l_mine.ctl + fr_l_mine.ndp: tag 26, ключ 2 в локальной NDP таблице
из 22 строк, коэффициент 1.0. Игнорируемый runtime-тест выполняет полный
загрузочный путь, подтверждает root/EXTO размеры 22 и 4 и итоговые 25
provider rows после пропуска первого EXTO row. Он вызывает Rust query по
восстановленному native правилу адресации и получает начальное значение 1.0;
getter оригинальной DLL здесь не исполняется. Это не запись живого
resource-device значения во время игры.
Закрывающий эксперимент: трассировать LoadControlSystem,
LoadPhysicalModel, CreateCollManager и создание collision objects; связать
каждый изменяемый field с созданным shape, contact или реакцией на движение.
DirectPlay wire
DirectPlay lifecycle и имена игровых messages известны. Wire framing, payload
schema, reliability flags и netZipData требуют записи обмена двух
оригинальных клиентов.
Native interoperability подтверждается только успешным обменом original client <-> compatibility implementation в обе стороны.
Shell, HUD, шрифты и локализация
Граница shell подтверждена exports createShell/getIShell, IGUIServer,
верхнеуровневым UI-pass и файлами ui/*.cfg, DATA/TextRes.cfg,
gamefont.rlb и sprites.lib. RsLi framing библиотек закрыт, но widget tree,
layout rules, glyph metrics, sprite command semantics, focus/navigation и HUD
state machine пока не восстановлены до field-level спецификации.
Закрывающий эксперимент: трассировать загрузку shell_ctrls.cfg,
menu_resources.cfg, cursor.cfg, game_resources.cfg и hq.cfg, сопоставить
GUI object factories и снять 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
не закрыты.
Закрывающий эксперимент: сопоставить research functions с ресурсами и UI, снять изменения state на контролируемых покупках/исследованиях и построить typed schema свойств по consumers, а не по одному имени.
Rare branches
Land.map poly_count > 0;- RsLi adaptive methods
0x080и0x0A0; - Texm formats 556 и 88;
- FX opcode 6;
- редкие material flags и MSH auxiliary streams.
Такие ветки реализуются по бинарному коду и synthetic tests, а статус corpus-verified получают только после реального файла или runtime trace.