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

+65
View File
@@ -1060,6 +1060,71 @@ factory-класса. В Rust неизвестный текущий тег за
CPU-вызов, ни локальные векторы не означают включения бюджетного шага в Control
clock.
#### Глобальный Control getter: сырой селектор `0x0f`
В `Control.dll` SHA-256
`bcb86318089028548a178b03a21accdce15df0cc85ddd91674cf3c2f55012c6e`
`FUN_1002b410` (RVA `0x2b410`) принимает сырой селектор в `EDX` и указатель
результата из `[ESP+4]`; общий выход использует `RET 4`. Сырой селектор `0x0f`
попадает в case RVA `0x2b8f5`. Владелец хранит ссылки на объекты, добавленные
фабриками `9` (`owner+0x5cc`) и `21` (`owner+0x5d0`).
Фабрика `21` вызывает общий getter `FUN_10021d00` (RVA `0x21d00`): selectors
`0..5` читают шесть текущих `coefficient_bits` по `this+0x54`. Общий загрузчик
RVA `0x21d50` первоначально копирует descriptor words `[11..16]` в это поле. Фабрика `9`
использует getter `FUN_100255c0` (RVA `0x255c0`), который для selector `0`
умножает коэффициент `[this+0x54]` на множитель `[this+0x11c]`. Поэтому Rust
выбирает последнюю строку каждого `factory_tag` `9` и `21` в исходном порядке
таблицы; текущий `descriptor.tag` не меняет выбранную фабрику. Если любой строки нет,
результат отсутствует до проверки полей другой строки.
Арифметика case `0x0f` сохраняет порядок и отдельное округление каждого шага:
```text
sum = f32(D5)
for k = 4, 3, 2, 1, 0:
sum = f32(sum + Dk)
out = f32(C0 * sum)
```
`Dk` — текущий коэффициент `k` из `coefficient_bits` фабрики `21`; `C0` —
selector `0` фабрики `9`, то есть
`coefficient_bits[0] * class9_selector0_scale_bits`. Общие `multiplier_bits`, `node_provider_status` и
`native_word_50_bits` в этом обратном вызове не читаются. Rust проверяет, что
входы и промежуточные результаты конечны и являются обычными числами либо
точным нулём; переполнение, денормализованные числа и отсутствующие поля
остаются типизированными ошибками. Диапазон
проверяется через `f64`, а затем операция выполняется в `f32` с округлением
после каждого сложения. Это воспроизводит описанные записи `f32`, но не
моделирует полное управляющее состояние x87. Этот getter не реализует весь Control
metadata `0x36`, Handler `43` или каталог свойств.
Лицензионный acceptance-test загружает `MISSIONS/Autodemo.00/data.tma` с явным
`MissionStartupInput { queue_time_ms: 100_000, research_creation_gate: false }`
и вызывает публичный `query_loaded_mission_control_resource(..., 15)`. Native
вызов исходного `Control.dll` того же SHA выполнен с `executionCW=0x007f` и
получил следующие биты на Rust-подготовленном снимке входных полей:
| `original_id` | Результат selector `0x0f` |
| --- | --- |
| `0` | `0x46572800` |
| `1` | `0x4636d000` |
| `2` | `0x467b4001` |
| `3` | `0x468ca000` |
| `4` | `0x46ec4001` |
| `5` | `0x46ec4001` |
Для `original_id` `6` и `7` таблица Rust не содержит обоих требуемых объектов
фабрик (`factory_tag` `9` и `21`), поэтому публичный запрос возвращает `None`.
Исходный обратный вызов для этих двух объектов отдельно не запускался; ветвь
неуспеха native getter-а проверена на отдельном векторе с отсутствующим указателем.
Сравнение шести значений проверяет формулу getter-а на исходных игровых ресурсах
и снимке входных полей, подготовленном Rust. Оно не снимает состояние фабрик,
созданных оригиналом. Время и флаг `queue_time_ms` и
`research_creation_gate` задаёт тест, поэтому эти значения не подтверждают
начальное состояние запуска AutoDemo в оригинале.
#### Фабричный tag 26: изменяемая добавка
В этой сборке `Control.dll` SHA-256