aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-10-20 00:31:26 +0300
committerValentin Popov <valentin@popov.link>2026-10-20 00:31:26 +0300
commita970974eb086372dc292a69b52b97b343da41e30 (patch)
treef46e41c2fe6edc6f31e62095b687a8fe946ed39c /docs
parentda0672bdf04b497df064e685bd1e2da9f9c4d2ca (diff)
downloadfparkan-a970974eb086372dc292a69b52b97b343da41e30.tar.xz
fparkan-a970974eb086372dc292a69b52b97b343da41e30.zip
fix(script): preserve scheduler counter increment
Diffstat (limited to 'docs')
-rw-r--r--docs/appendices/script-vm.md37
1 files changed, 37 insertions, 0 deletions
diff --git a/docs/appendices/script-vm.md b/docs/appendices/script-vm.md
index 64dc4fc..0e0217f 100644
--- a/docs/appendices/script-vm.md
+++ b/docs/appendices/script-vm.md
@@ -143,6 +143,43 @@ slot2 одновременно в `+0x24/+0x28`, slot3 в `+0x2c` и slot6 в `+
изолированную часть как `Handler2RecordScheduler`; он не выполняет bytecode,
не назначает игровых имён и не делает event lookup за original VM.
+Шаг прибавления проверен прямым вызовом оригинального helper-а
+`FUN_10005070` (RVA `0x5070`) из GOG `ai.dll`, SHA-256
+`c19cd6cd31cd2896f8c4ba0f0e700dd3408aec8182daaf4863d98a7d233a2abc`.
+Исходник x86 test harness-а хранится в
+[`handler2_native_oracle.cs`](../../crates/fparkan-script/tests/handler2_native_oracle.cs):
+он сначала сверяет hash DLL, затем вызывает helper с 100-byte record и
+проверяет оба поля. Пример воспроизведения в Windows PowerShell:
+
+```powershell
+$env:FPARKAN_ORIGINAL_AI_DLL = 'C:\GOG Games\Parkan - Iron Strategy\ai.dll'
+& "$env:WINDIR\Microsoft.NET\Framework\v4.0.30319\csc.exe" /nologo /platform:x86 /out:target\Handler2NativeOracle.exe crates\fparkan-script\tests\handler2_native_oracle.cs
+.\target\Handler2NativeOracle.exe
+```
+
+Полученные значения:
+
+| `+0x24` до вызова | сохранённый `+0x28` | `+0x24` после вызова | `+0x28` после вызова |
+| ---: | ---: | ---: | ---: |
+| `0x00000004` | `0x00000004` | `0x00000008` | `0x00000004` |
+| `0x00000008` | `0x00000004` | `0x0000000C` | `0x00000004` |
+| `0xFFFFFFFF` | `0xFFFFFFFF` | `0xFFFFFFFE` | `0xFFFFFFFF` |
+| `0xFFFFFFFE` | `0xFFFFFFFF` | `0xFFFFFFFD` | `0xFFFFFFFF` |
+
+Rust-тест повторяет эти начальные значения. Для первой пары обновлений входы
+несут slot 2, равный `99` и `7`, однако сохранённый при создании шаг равен `4`;
+для второй пары оба счётчика и шаг начинаются с `0xFFFFFFFF`, а новые входы
+несут `2` и `17`. Правило refresh читается прямо из модели:
+
+```rust
+record.counter = record.counter.wrapping_add(record.increment);
+```
+
+Native helper получает только указатель на record, поэтому напрямую проверяет
+сложение текущего `+0x24` с сохранённым `+0x28`; свежий slot 2 принадлежит
+сценарию Rust-теста, а не аргументам helper-а. Проверка не охватывает полный
+scheduler path внутри запущенной игры.
+
На границе mission runtime выбранный TMA clan `first_resource`
материализуется как отдельный `MissionScriptBundle`: loader нормализует
`<base>.scr`, декодирует его тем же bounded reader-ом и публикует immutable