feat(runtime): restore indexed Control provider query
Docs Deploy / Build and Deploy MkDocs (push) Successful in 37s

This commit is contained in:
Valentin Popov committed 2026-10-31 09:22:41 +04:00
1 parent 8c11e0f470
commit 3e3ab0e0c6
4 files changed
+313 -19

No files matched your search

+42 -6
View File
@@ -532,12 +532,48 @@ callbacks. Licensed двухстадийный узел шахты подтве
новый API при доле `0.5`, без FX и window path. Гибель root/critical узла и
неизвестные flags/aggregate также остаются явными ошибками.
Rust query обращается к загруженному объекту и поддерживает `0xe00` только для
текущего descriptor tag `26` с factory tag `26`; другие активные typed tags и
factory paths остаются явными границами. Запрос читает текущее type-1 значение.
Licensed `min_l_n1.dat` подтверждает изменение `0xe00` и Behavior `0x1002` с
`1.0` до `0.5` после явного уменьшения количества вдвое; чтение получает
текущее значение provider, который также меняет явный бюджетный шаг.
Rust query обращается к загруженному объекту и поддерживает `0xe00` для пар
текущего descriptor tag и factory tag `(5,5)`, `(5,26)`, `(26,5)` и `(26,26)`;
прочие специализированные tag/factory пути остаются явными границами. Запрос
читает текущее type-1 значение provider. Licensed `min_l_n1.dat` подтверждает
изменение `0xe00` и Behavior `0x1002` с `1.0` до `0.5` после явного уменьшения
количества вдвое; чтение получает текущее значение provider, который также
меняет явный бюджетный шаг.
Indexed selector `0x400` в том же Control IID `0x204` возвращает текущее type-1
значение строки provider, выбранной через key устройства, при любом descriptor
и factory tag. Rust принимает конечное normal-or-zero значение, включая оба
знака нуля, и сохраняет его биты; недоступная таблица, отсутствующий key и
значения вне этой численной области дают типизированную ошибку. В оригинальном
`Control.dll` с SHA-256
`bcb86318089028548a178b03a21accdce15df0cc85ddd91674cf3c2f55012c6e` ветвь
`FUN_1002bb40` (RVA `0x2bc3c`) читает provider pointer `device+0x44` и key
`device+0x04`, вызывает provider slot `+0x0c` в режиме `1` и не проверяет
descriptor/factory tag. Rust-проекция читает сохранённый снимок; она не вычисляет
value из amount, maximum, coefficients или множителя `M`. Native selector-cell
alias/fallback за пределами валидной записи и поведение native при невалидном
key остаются неисследованными; Rust не подставляет для них `0.0` или `None`.
Это покрывает перечисленные query-пути. Полный интерфейс IID `0x204`, его
взаимодействие со всеми Behavior-вызовами и полный игровой tick остаются за
границей подтверждённого поведения.
Объём свидетельств для `0x400` ограничен и состоит из трёх частей. Native
oracle на настоящем `Control.dll` проверил шесть выходов по raw bits для
ограждённого синтетического owner/provider table; он вызвал indexed query и
реальный provider getter, но не создавал оригинальные factory-объекты. Матрица
выходов находится в [техническом разборе](script-vm.md#индексный-control-getter-сырой-селектор-0x400).
Независимый разбор AutoDemo обнаружил восемь таблиц и 100 устройств с
доступными ключами, но все выбранные provider values равны `1.0`; такой набор
подтверждает загрузку и область ключей, однако не различает, какую именно
строку выбирает алгоритм. Игнорируемый Rust-тест опрашивает эти 100 устройств и
проверяет те же биты, не выдавая равенство ресурсных данных за доказательство
индексирования.
Вместе native-векторы и resource acceptance не подтверждают состояние
оригинальных factory instances, полный контракт IID `0x204`, полный
Behavior-call flow или живую миссию/gameplay. Поведение при ошибочных native
указателях и ключах, а также selector-cell alias/fallback вне валидной строки
по-прежнему не описано; Rust сохраняет typed errors на этой границе.
Native oracle проверил изолированные пути распределения, обновления класса `4`,
стадийную арифметику `0x11220`, обход дочерних provider-строк `0x11130` на
interleaved parents `[-1, 0, 0, 1, 3]` с callback-порядком `1, 3, 4, 2` и
+74
View File
@@ -1429,6 +1429,80 @@ key из поля устройства `+0x04`. Provider getter `FUN_1000dc40` (
строку `table + key*0x2c`; текущее type-1 значение находится по `+0x14` строки.
Индекс устройства и ключ provider row — разные значения с разными задачами.
#### Индексный Control getter: сырой селектор `0x400`
В той же сборке `Control.dll` с SHA-256
`bcb86318089028548a178b03a21accdce15df0cc85ddd91674cf3c2f55012c6e`
индексный query для `QueryInterface(0x204)` входит через слот `+0x18` vtable
`0x3d1b4`: `FUN_1002bb40` (RVA `0x2bb40`, `RET 8`, `ECX=owner+0x0c`, ordinal
устройства в `EDX`, selector и output pointer на стеке). При сыром selector
`0x400` функция попадает в ветвь RVA `0x2bc3c`.
Ветка получает provider pointer из `device+0x44`, отдельно читает provider key
из `device+0x04` и вызывает provider vtable slot `+0x0c` в режиме `1`, передавая
адрес ячейки selector как storage результата. После вызова значение переносится
в выходной `f32` caller-а; ветвь возвращает `AL=1` без проверки текущего
descriptor tag или factory tag.
Поле `device+0x44` указывает на provider interface `owner+0x04` с vtable
`0x3d254`; slot `+0x0c` ведёт к `FUN_1000dc40` (RVA `0x0dc40`). Getter
использует key для строки общей таблицы, а не ordinal устройства. Для валидной
строки selector `1` читает её текущее type-1 поле `+0x14` напрямую:
`0x400` поэтому не пересчитывает долю из amount и maximum и не умножает её на
коэффициент устройства, `M` или descriptor word. В Rust тот же маршрут выглядит
так:
```text
device = devices[device_index]
value = providers[device.key].current_type1_value
return Some(value)
```
Индекс устройства сначала выбирает запись, её key затем выбирает provider row;
этот indexed-вызов не является глобальным запросом `0x0f` и не является
metadata getter IID `0x16`. Проекция Rust принимает конечные нормальные значения
и точные `+0`/`-0`, сохраняя биты нулевого значения; остальные float-диапазоны и
недоступные table/key остаются типизированными ошибками. Native selector-cell
alias/fallback вне валидной записи provider не реализован: Rust не подставляет
для такого пути `0.0` или `None`.
Шесть изолированных вызовов оригинала проверяют именно этот валидный
indexed-вызов.
В каждом ordinal устройства равен `2`, а ключ provider равен `1`, `3` или `4`;
все значения строк различаются, поэтому ключ не совпадает с ordinal и не
подменяется соседней строкой.
| Вектор | Ordinal | Key | Raw output | AL |
| --- | ---: | ---: | ---: | ---: |
| Значение из корпуса `1.0` | 2 | 1 | `0x3f800000` | 1 |
| Доля `0.625` | 2 | 1 | `0x3f200000` | 1 |
| Отрицательное конечное `-2.5` | 2 | 3 | `0xc0200000` | 1 |
| `+0` | 2 | 1 | `0x00000000` | 1 |
| `-0` | 2 | 3 | `0x80000000` | 1 |
| Отличающаяся строка `0.0625` | 2 | 4 | `0x3d800000` | 1 |
Вектор `0.625` специально задаёт native word `device+0x50 = 0`; он всё равно
возвращает текущее поле provider. Таблица устройства, provider и остальные
строки были синтетическими, ограждёнными входами; исполнены оригинальные
`FUN_1002bb40` и provider getter `FUN_1000dc40`, без constructors, ControlSystem
или mock callbacks. Все входные записи остались неизменными, проверены canary
области, а изменился только DWORD выхода. Для DLL с SHA-256
`bcb86318089028548a178b03a21accdce15df0cc85ddd91674cf3c2f55012c6e` входной
control word `0x027f` был временно заменён на `0x007f` и восстановлен после
вызова; у всех шести ветвей `AL=1`. Выходы совпали с прямыми значениями выбранных
synthetic rows. Это не снимок состояния оригинальной фабрики или миссии: полный
IID `0x204`, интеграция Behavior и gameplay-трасса не проверены.
Независимый разбор лицензированного AutoDemo дал восемь таблиц и 100 пар
устройство/key, но все выбранные type-1 значения там равны `1.0`. Такой набор
проверяет загрузку таблицы, ключи и доступность запросов, но не позволяет
различить выбор строки по key и по ordinal. Игнорируемый Rust acceptance test
проверяет все 100 запросов на этом корпусе и ожидает `0x3f800000`. В тесте явно
заданы `MissionStartupInput { queue_time_ms: 100_000,
research_creation_gate: false }`; эти значения являются входами Rust-проверки,
а не доказательством штатного gate оригинала. Native синтетические строки дают
отдельное доказательство выбора key. Оба результата имеют ограниченную область
и не являются сравнением полного поведения игры.
Конструктор owner `FUN_10006bf0` (RVA `0x6bf0`) задаёт scalar `+0x1a8 = 1.0`
и вызывает `FUN_10014260`, который записывает `1.0` в `+0x354/+0x358`.
Проверенные ветви канонического event `4/null` и FAC0 не меняют эти три поля
+25 -2
View File
@@ -1089,8 +1089,31 @@ NDP, а `FUN_1000a100` (RVA `0xa100`) добавляет их в общую та
последовательность provider-строк: её длина берётся из NDP, она не дополняется
до числа узлов MSH и не участвует во второй перенумерации ключа.
Так этот селектор включён в цепочку добычи руды оригинала. В той же сборке
`Behavior.dll` (SHA-256
Индексный selector `0x400` выдаёт значение выбранной строки provider как есть.
Сначала общий индекс устройств выбирает дескриптор, затем его ключ выбирает
строку NDP; это разные номера. Результатом становится текущее type-1 значение
этой строки, а не запас `CurrentOre`, предел или значение после умножения на
коэффициент и `M`. Например, provider-строка с `0.625` возвращает `0.625`, а
записанный в ней ноль тоже остаётся успешным результатом. Rust использует уже
подготовленную таблицу и сообщает отдельную ошибку, если provider-таблица или
строка по ключу недоступна. Этот запрос читает снимок состояния и сам не меняет
таблицу и не запускает её обновление.
У правила есть два разных подтверждения. Разбор AutoDemo нашёл восемь таблиц
Control и 100 устройств; все 100 значений по ключам устройств в этом корпусе
равны `1.0`, поэтому Rust-проверка подтверждает реальные ключи и доступность
строк, но сама по себе не различает выбор по ключу и по номеру устройства.
Отдельная x86-проверка исходного `Control.dll` включала шесть вызовов на
синтетической таблице, где значения строк были заданы вручную и различались.
Например, при номере устройства `2`, ключе `1` и нулевом слове
`device+0x50` оригинал вернул `0.625` из выбранной строки. Точные биты ответов
приведены в [техническом разборе](../appendices/script-vm.md#индексный-control-getter-сырой-селектор-0x400).
Этот опыт не создавал оригинальные factory-объекты и не исполнял полную миссию:
полный интерфейс IID `0x204`, интеграция Behavior и игровое поведение остаются
непроверенными.
В цепочке добычи руды оригинал использует описанный выше selector `0x300`.
В той же сборке `Behavior.dll` (SHA-256
`ddfbdbde07832358fe6e35e01c8dba9b58e7313789498b6049ed51d4ed9baf99`)
`FUN_100186b0` (RVA `0x186b0`) перечисляет устройства типа `0x1a`, а
`FUN_100198e0` (RVA `0x198e0`) суммирует успешные запросы IID `0x204` с selector