From 11dccef6ac6238668bb7eb775614092392e34a14 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Sat, 31 Oct 2026 05:40:52 +0400 Subject: feat(assets): restore ordered research candidate selection --- docs/tomes/03-resources.md | 9 +++++++++ docs/tomes/06-behavior.md | 43 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 43 insertions(+), 9 deletions(-) (limited to 'docs/tomes') diff --git a/docs/tomes/03-resources.md b/docs/tomes/03-resources.md index a349a79..6668e95 100644 --- a/docs/tomes/03-resources.md +++ b/docs/tomes/03-resources.md @@ -471,6 +471,15 @@ struct UnitDatRecord112 { Требование обязательного terminator применяется только к полям, где оно доказано форматом. +У AI есть отдельный потребитель Unit DAT: исследовательский chooser читает +заголовок с magic `F0F1`, сохраняет второй DWORD как непрозрачный и завершает +разбор на границе первого полного корневого дерева. `immediate_child_count` +закрывает это дерево по preorder; байты после него, включая неполную запись или +orphan-record, chooser уже не читает. Поэтому Rust для этого пути находит +bounded 112-byte prefix и передаёт его существующим `decode_unit_dat` и +`unit_component_tree`. Общий декодер остаётся строгим к размеру всего файла +`8 + 112 * N`; частный framing AI не меняет этот контракт. + Проверено 425 файлов / 5 219 records Части 1 и 676 файлов / 8 145 records Части 2. Все соответствуют формуле размера, `kind == 1` и `archive_name == "objects.rlb"`. diff --git a/docs/tomes/06-behavior.md b/docs/tomes/06-behavior.md index b4bddb7..2f7b62c 100644 --- a/docs/tomes/06-behavior.md +++ b/docs/tomes/06-behavior.md @@ -523,15 +523,40 @@ let cost = calc_full_research_cost(&candidate, &table)?; Две проверки суммы показали чувствительность округления: одна и та же сумма `1, 2^24, -2^24` даёт `0`, а в другом порядке `2^24, -2^24, 1` даёт `1`. -Эта проверка использовала синтетический исследовательский контекст и заданное -для опыта x87-слово управления `0x007f`; она не запускала штатный `Handler(43)`. -Локальный Rust-core принимает таблицу и одного Unit DAT, но пока не выбирает -кандидата в AI-каталоге и не связывает таблицу с владельцем полного старта -миссии. Поэтому `Single.01` всё ещё останавливается на неподдержанном -`Handler(43)`. Разбор с настоящими `scream.trf` и `12hel1.dat` проверяет -совместимость ресурса с API, но сам по себе не сравнивает стоимость с -оригинальным живым контекстом. Проверки границ байтов защищают Rust от плохих -входных данных и не приписываются нативному загрузчику. +Эти результаты относятся к синтетическому исследовательскому контексту; +штатный Handler(43) и запуск миссии здесь не проверялись. + +Отдельная frozen-матрица непосредственно вызвала chooser GOG `ai.dll` SHA-256 +`c19cd6cd31cd2896f8c4ba0f0e700dd3408aec8182daaf4863d98a7d233a2abc`: функция +`FUN_10010f90` (RVA `0x10f90`) прошла три guard-case и все 15 проверок. +Конструктор находится в RVA `0x10480`, raw DAT parser — `0x102c0`, +предикаты — `0x115f0` и `0x11530`. Матрица передавала кандидатов в заданном +порядке. Обёртка `Handler(43)` по RVA `0xd54a` и загрузчик AI-каталога по RVA +`0x104e0` не вызывались. Cost принадлежит `ArealMap.dll` SHA-256 +`a25f9254ebb339d70e2e07c8121950191e9584ca0858d8e9c2e2b6c671edf6ea`, +`CalcFullResearchCost` RVA `0x16280`; для сравнения x87 был явно задан как +`0x007f`. + +`ResearchCandidateSet` держит упорядоченный каталог, выбранный индекс, байт +scan-status и флаг `loaded` каталога; TRF-контекст передаётся отдельно. +При наличии каталога и контекста set-status начинает со значения `1`, затем +перед чтением каждого entry со status `0` становится `0`. Ненулевой entry +пропускается. Первый предикат требует info words 1 и 2, соответствующие битам +TRF1 0 и 2; второй требует words 0 и 2, то есть биты 1 и 2. Второй status +сохраняется в entry, а cost вызывается даже при status `1`. Выбор записывается +сразу, когда second predicate ложен и ore строго ниже текущего минимума, +изначально `f32::from_bits(0x4b18967f)`. Равенство сохраняет первого, NaN индекс не меняет. + +В Rust ошибка VFS-чтения и невалидный DAT пропускают entry; неизвестный TRF- +ключ даёт false predicate. Ошибка стоимости `EmptyResearchTable` тоже +пропускает entry и сохраняет его второй status. Остальные ошибки таблицы и +`UnsupportedNonAsciiLookup` возвращаются явно, причём уже сделанные изменения +индекса и status остаются. Общий Rust-предикат может закончить обход раньше +native callback `0x11530`, который продолжает накапливать неиспользуемые float +sums. Первый проход уже проверил enabled keys в той же неизменной таблице, +поэтому подтверждается эквивалентность состояния и выбора для этого входа, +но не callback-trace parity. Это не подтверждение цены на живой миссии или +порядка AI-каталога. ### Стабильность AI-слоя -- cgit v1.2.3