feat(runtime): restore Control selector 15
Docs Deploy / Build and Deploy MkDocs (push) Successful in 36s

This commit is contained in:
Valentin Popov committed 2026-10-31 07:47:32 +04:00
1 parent 042be27c00
commit 6412c12c09
3 files changed
+415 -14

No files matched your search

+31 -11
View File
@@ -992,21 +992,41 @@ descriptor tag `19` и положительным selector-0 ответом; п
его vtable slot `+0x10` статически указывает на общий getter RVA `0x21d00`, но
отдельный aggregate oracle этот класс не запускал. Rust хранит начальный scale
factory `9` и предоставляет `query_loaded_mission_control_resource` для selector
`1`/`2` и `set_loaded_mission_control_multiplier` для setter-а `1`. Вызовы
сначала проверяют живой `ObjectHandle`, затем обращаются к таблице этого объекта
из полной загрузки миссии. Синтетические тесты проверяют границы пустой таблицы,
нулевого знаменателя, неподдержанных factory tags и чисел за диапазоном
нормального `f32`; лицензированный тест сверяет реальные descriptor tags и
получает для `min_l_n1.dat` сумму `16` и отношение `1` до setter-а.
`1`, `2` и `15`, а также `set_loaded_mission_control_multiplier` для setter-а
`1`. Вызовы сначала проверяют живой `ObjectHandle`, затем обращаются к таблице
этого объекта из полной загрузки миссии. Синтетические тесты проверяют границы
пустой таблицы, нулевого знаменателя, отсутствующих объектов фабрик и чисел за
диапазоном нормального `f32`; лицензированный тест сверяет реальные descriptor
tags и получает для `min_l_n1.dat` сумму `16` и отношение `1` до setter-а.
Сырой селектор `0x0f` соединяет два ранее созданных объекта владельца: у строки
с `factory_tag = 9` берётся коэффициент и его текущий множитель, а у строки с
`factory_tag = 21` — шесть текущих `coefficient_bits`. `INTO` может поменять
`descriptor.tag`, не заменяя объект фабрики, поэтому query выбирает последние
строки по `factory_tag`, а не по текущему tag. Если любой объект фабрики
отсутствует, ответ — `None`. Иначе шесть коэффициентов складываются с индекса `5`
к `0` с округлением до `f32` после каждого сложения; результат умножается на
selector `0` фабрики `9` (`coefficient_bits[0] * class9_selector0_scale_bits`).
Общие `multiplier_bits`, `node_provider_status` и `native_word_50_bits` в этой
формуле не участвуют.
Лицензионная проверка загружает AutoDemo с явно переданными
`MissionStartupInput { queue_time_ms: 100_000, research_creation_gate: false }`
и вызывает `query_loaded_mission_control_resource(..., 15)` для исходных
объектов `0..5`; для `6` и `7` Rust возвращает `None`, потому что не хватает как
минимум одного объекта фабрики. Native-биты для `0..5` приведены в технической
главе. Проверка сверяет результат исходного геттера на снимке входных полей,
подготовленном Rust. Время и флаг заданы самим тестом; они не доказывают
начальный режим запуска AutoDemo в оригинале и не являются захватом его фабрик
или состояния объекта Control.
В этом API пока реализованы только getter selectors `1`/`2` и setter `1`.
Отдельный native setter selector `17`, tag-specific методы прочих factory tags,
их внешние эффекты и вызов из регулярного шага Behavior здесь не моделируются.
Числа должны оставаться конечными и нормальными либо точным нулём; произведения
и отношения за поддерживаемым диапазоном `f32` возвращают явную ошибку, а не
подменяются нулём или `None`. При проверке диапазона Rust оценивает операцию в
`f64`, затем выполняет её в `f32`; это ограниченная проверка диапазона, а не
эмуляция полного x87 accumulator-а.
и суммы за поддерживаемым диапазоном `f32` возвращают явную ошибку, а не
подменяются нулём или `None`. При проверке диапазона Rust сначала оценивает
границы в `f64`, затем выполняет каждую операцию в `f32`; это сохраняет
пошаговое округление этого getter-а, но не эмулирует весь x87 execution state.
Отдельная ветка того же GOG `AniMesh.dll` показывает, когда создаётся Behavior
для host object. В `FUN_10002ff0` (RVA `0x2ff0`, SHA-256