diff --git a/campaign/board.md b/campaign/board.md index d106913..6a388bc 100644 --- a/campaign/board.md +++ b/campaign/board.md @@ -9,11 +9,11 @@ Status flow: `backlog → in-progress → mapped → verified` (or `blocked`). | `.gob` format | subsystem | mapped | high | 100% | 2026-09-07 | renamed uncompressed ZIP (community-known) | | `Mars::AppStartup` (entry) | control-flow | mapped | high | 100% | 2026-09-07 | entry 0x00925794 -> WinMain 0x0089dd30 -> DemoApp ctor 0x0089c950 -> Mars::Application::Initialize 0x008a0e50 (config/affinity, D3D9, window, sound thread, OnStartup -> net thread). findings/control-flow/turn-spine.md | | main loop / tick dispatch | control-flow | mapped | high | 90% | 2026-09-07 | Application::Run 0x0089f5b0: FrameTimer, PanelManager, DemoApp::OnUpdate 0x00898800 / OnTick 0x0089a640 / OnRender 0x00899210. Turn pipeline: EndTurn 0x00783be0 -> BeginProcessTurn 0x007d98e0 -> StrategyServer::ProcessTurn 0x007dc6c0 -> RunCombatRound 0x007cbe80 -> SETurnResults -> ResumePlaying 0x007ddc90. Lockstep on every machine | -| `Game::ClientPlayer` / `AIPlayer` | object | mapped | high | 80% | 2026-09-07 | empire STATE = Game::ServerPlayer (~110 members, abs 0x28..0x3dc; IStreamable +0x3a0; read FUN_008804d0 / write FUN_008563e0). ClientPlayer = client view. findings/objects/struct-recovery.md. Verify vs real save pending | -| `Game::StarSystem` | object | mapped | high | 80% | 2026-09-07 | Game::ServerSystem = star system + colony (~90 members, abs 0x18..0x2d4; read FUN_0075d4b0 / write FUN_00749630). PlayerView per-player fog view. Verify vs real save pending | +| `Game::ClientPlayer` / `AIPlayer` | object | verified | high | 85% | 2026-09-07 | ServerPlayer layout confirmed vs real saves (names, species, home, savings, designs coherent across turns 1-3). Gaps: none in player block; findings/objects/struct-recovery.md | +| `Game::StarSystem` | object | verified | med | 75% | 2026-09-07 | ServerSystem confirmed vs real saves EXCEPT a conditional layout in 7/28 systems: `VFlags` appears where `Name`/`vnh` expected; `halt[2].haltv` is a 31/35-byte struct not bool; optional `indi` before `NVE`/`PID`. Round-4 Ghidra target | | `Game::Planet` / `DOPlanet` | object | mapped | high | 100% | 2026-09-07 | Planet : Actor is a RENDER actor, not streamed; all colony state is in ServerSystem (+PlayerView in NVs map) | | `Game::ShipDesignDef` / `DesignList` | object | in-progress | low | 30% | 2026-09-07 | vft@0x009fef64 (3), leaf (0 bases); mostly data-defined via .shipsection. fields TBD | -| `Game::FleetLayout` | object | mapped | high | 80% | 2026-09-07 | StarFleet (read FUN_00702470/write FUN_00701070) + StarShip (FUN_00853fa0/FUN_008291f0) layouts recovered incl. FlightPlan/Waypoint/NodeRoute; FleetLayout = UI. Verify vs real save pending | +| `Game::FleetLayout` | object | verified | high | 85% | 2026-09-07 | StarFleet/StarShip confirmed vs real saves; gap: PrisonerHold `prisoners` entries lack `PrNSp` (conditional/absent). Round-4 target | | Mars entity chain | object | mapped | high | 100% | 2026-09-07 | EntityBase@0x00a36fd0 (10) -> PhysicalEntity@0x00a3710c (12) -> TargetableEntity -> Actor@0x009e2114 (36); mixins RigidBody/Body/NetworkObject/HandleObject/IScriptable | | `Game::GameCombatSim` + `CombatCommand*` | subsystem | backlog | — | 0% | 2026-09-07 | tactical combat (~40 command types) | | D3D9 device init | subsystem | backlog | — | 0% | 2026-09-07 | `Mars::DrawDevice`/`Camera` → Direct3DCreate9/CreateDevice | @@ -25,10 +25,10 @@ Status flow: `backlog → in-progress → mapped → verified` (or `blocked`). | string / config intel | meta | mapped | high | 100% | 2026-09-07 | findings/subsystems/strings-and-config.md | | Mars brace-block parser | subsystem | verified | high | 100% | 2026-09-07 | verify/parsers/ (mars_data, flat_kv, manifest, effect_txt, verify.py): 1595/1595 files parse, 0 dangling cross-links; catalogs in verify/results/data-catalogs/ | | networking (SNM/FNM + GameSpy) | subsystem | backlog | — | 0% | 2026-09-07 | ~60 SNM strategy msgs, FNM file-xfer/host-migration; lockstep sim | -| battle-load: thread contention | subsystem | mapped | med | 50% | 2026-09-07 | 3 CreateThread sites CLEARED (net watchdog 0x00901f40, TIME_CRITICAL audio streaming 0x008ef1d0, starmap blob mesh 0x00735bb0). Sim+combat load are on the MAIN thread -> candidates: audio thread CS (g_musicCS) or D3D9 driver threads. Next: dynamic profile under x32dbg once the game runs | +| battle-load: thread contention | subsystem | in-progress | med | 50% | 2026-09-07 | game now runs on VM140 -> dynamic profiling unblocked (round 4) | | UI screen & flow map | meta | mapped | high | 100% | 2026-09-07 | findings/subsystems/ui-screen-map.md - 36 screens; screens are C++ on Mars controls (NOT data); turn state machine recovered | -| `Game::StrategyServer` (sim block) | object | mapped | high | 80% | 2026-09-07 | top-level sim state; read FUN_007d27a0 / write FUN_0079fa70; IStreamable +0 | +| `Game::StrategyServer` (sim block) | object | mapped | med | 60% | 2026-09-07 | 41 fields; real save shows missing optional `zdsi`/`zdst` at end, `turnstats.hist.stats.tch` is int not string, CreateParameters `key` is int. Round-4 completion target | | stream primitive API | subsystem | mapped | high | 100% | 2026-09-07 | IStreamable vft: +0x18 string, +0x1c bool, +0x20 float, +0x24 int, +0x28 nested, +0x30 raw; FUN_00816490 = NetworkObject handle id | -| real save for verification | verify | in-progress | — | 0% | 2026-09-07 | need the game running (DXVK+lavapipe on VM140) -> autosave -> parse with recovered layouts | -| save_reader.py | verify | mapped | high | 90% | 2026-09-07 | verify/save-reader/: walker+schema, 26 tests pass on synthetic fixtures (both padding modes); SAVE_FORMAT.md. 'verified' only after a real save parses --strict | +| real save for verification | verify | verified | high | 100% | 2026-09-07 | 6 saves, 3 distinct turn states in verify/results/saves/ (byte-deterministic per state). Game runs on VM140 via DXVK 3.1 + lavapipe; recipe findings/subsystems/running-the-game.md | +| save_reader.py | verify | verified | high | 90% | 2026-09-07 | parses all 3 real saves end-to-end (padding=joint confirmed); --strict exit 2 with a precise gap list: verify/results/saves/turn2-strict-issues.txt | | Ghidra type write-back | meta | verified | high | 100% | 2026-09-07 | structs saved in project (ServerSystem 87f, ServerPlayer 110f, StarFleet, StarShip, StrategyServer partial, 22 nested); 52 serializers + primitives + ~60 spine fns renamed; decompile shows field names | diff --git a/campaign/open-questions.md b/campaign/open-questions.md index c1de6bb..d9dae22 100644 --- a/campaign/open-questions.md +++ b/campaign/open-questions.md @@ -23,3 +23,4 @@ Each links to the finding that raised it. Promoted to backlog or closed by **re- - **Battle-load, narrowed** — sim/combat load run on the main thread; the only other threads are net watchdog, TIME_CRITICAL audio streaming (`g_musicCS`), and a star-map mesh builder. Hypothesis: audio-thread critical-section contention or D3D9 runtime/driver threads on many cores. Needs a dynamic profile (x32dbg / ETW) under the software-GPU stack. (from [[turn-spine]]) - **Spine leftovers** — `StrategyServer` struct partial (41 fields); static-initialiser region 0x009be000–0x009c1400 undisassembled; `Mars::Stream` vftable not located; several small ProcessTurn phase fns unnamed. (from [[turn-spine]]) - **Save framing ambiguities (settle on first real save)** — padding joint `[len][name][value][pad]` vs split; bool vs int for names with len%4==0 (no type byte); on-disk tags for Summary/CreateParameters and count/element tags inside framed arrays unknown (positional for now). A real 3-char bool tag (`NPC`,`Dep`,`hsp`) settles padding. (from [[SAVE_FORMAT]]) +- **Strict-parse gaps vs real save (verifier output, round 3)** — (1) `ServerSystem`: 7/28 systems carry `VFlags` where `Name`/`vnh` were expected — a conditional layout (uncolonized/special systems?); (2) `halt[].haltv` is a 31/35-byte struct, not a bool; (3) optional `indi` (IndependenceInfo?) precedes `NVE`/`PID` in some systems; (4) `PrisonerHold.prisoners[]` entries lack `PrNSp`; (5) `/createParams/key` is int; (6) `turnstats.hist.stats.tch` is int; (7) optional trailing `zdsi`/`zdst`. Padding = **joint** (settled). 7 resyncs / 2728 raw bytes ≈ RNG blob. (from [[turn2-strict-issues]]) diff --git a/findings/subsystems/running-the-game.md b/findings/subsystems/running-the-game.md new file mode 100644 index 0000000..f213cba --- /dev/null +++ b/findings/subsystems/running-the-game.md @@ -0,0 +1,125 @@ +# SotS (2006, GOG v1.8.1) running on GPU-less Win10 VM — save-game capture + +Date: 2026-09-07. VM140 `sots-re-win10` (Win10 22H2, 16 vCPU, 12 GB, `vga: std`, no GPU, no audio device). +Game: `C:\SOTS\Sword of the Stars.exe` (GOG build, in-game version string `ver.1.8.1 Wed Dec 13 03:38:31 2017`). +Result: **game reaches the main menu, a custom game was created, End Turn was pressed twice (turn 1 -> 3), and the +game wrote autosaves + a manual save.** Rendering is DXVK 3.1 (d3d9 -> Vulkan) on Mesa lavapipe (CPU Vulkan). + +## Save files (this directory) + +| File | Size | What | +|---|---|---| +| `Autosave EndTurn - turn2.sav` | 64,967 | written by End Turn #1; dialog lists it as "Turn 1" (pre-turn state) | +| `Autosave - turn2.sav` | 66,739 | written by End Turn #1; dialog lists it as "Turn 2" (post-turn state) | +| `verify1.sav` | 66,732 | manual save from the Save Game dialog on turn 2 (copy of `MyGameverify1verify1.sav`, see gotchas) | +| `MyGameverify1verify1.sav` | 66,732 | same file under the name the game actually wrote | +| `Autosave EndTurn - turn3.sav` | 66,732 | written by End Turn #2 | +| `Autosave - turn3.sav` | 67,219 | written by End Turn #2 (turn 3 state) | +| `Autosave Backup - turn2.sav` | 66,739 | `(Autosave Backup).sav` — previous `(Autosave).sav` rotated by End Turn #2 | +| `Profile_re.ini` | 4,181 | player profile the game created (`C:\SOTS\Profiles\Profile_re.ini`) | + +Game facts for the verifier: 2 players (human profile `re`, random race -> **Morrigi**, homeworld **Gamma Cephei**, +plus 1 AI "Computer"), map shape Sphere, **28 stars**, distance 7 ly, size 0 %, resources 0 %, initial treasury +50,000, 1 colony, 0 techs, econ/research 100 %, random encounters 100 %, AI Normal, strategic turn unlimited, +combat turn 240 s. Game name string inside the saves is `MyGameverify1`. Turn-2 planet panel: Size 10, +Resources 5,000, Infrastructure 200.00, Imperial pop 2,000,000,000, Civilian 520,000,000, Industrial Output 16,818, +Income 239,189, Imperial Savings 289,688 (turn 3: Civilian 540,000,000, IO 16,842, income 239,785, savings 532,369). + +Saves live in **`C:\SOTS\SavedGames\`** (path is `SavedGames/` relative to the game's working dir; nothing is +written under Documents / AppData / registry). Naming format in the exe: `%s%s%02i.sav`; autosave names come from +string ids `SOTS_GAME_AUTOSAVE`, `SOTS_GAME_AUTOSAVEBACKUP`, `SOTS_GAME_ENDTURN_AUTOSAVE`. + +`recipe/` holds the exact files used: `display.cfg`, `launch.cmd`, `lvp_icd.x86.json`, `click_helper.ps1`, +`session.log` (the game's own log), `dxvk_d3d9.log`, `run.txt` (stdout of the game). `screenshots/` has the +console captures described at the bottom. + +## Recipe (reproducible) + +All downloads were done on the Linux side and pushed with `scp`, installs run over SSH (`re@192.168.10.139`, +PowerShell). Hashes are sha256 prefixes. + +1. **Runtime deps the GOG install does not provide** (game exited with `0xC0000135 STATUS_DLL_NOT_FOUND`): + - VC++ 2010 x86 redist `vcredist_x86.exe` (99dce3c8…): `Start-Process vcredist2010_x86.exe -ArgumentList /q,/norestart -Wait` + -> `SysWOW64\msvcr100.dll` / `msvcp100.dll` 10.00.40219.325. Imports: `MSVCR100.dll`, `MSVCP100.dll`. + - DirectX End-User Runtime June 2010 `directx_Jun2010_redist.exe` (053f76dc…): extract with `/Q /T:C:\Users\re\dxredist` + then `DXSETUP.exe /silent`. Needed for `d3dx9_42.dll` (import) **and** the legacy shader compiler: the game's + `.fx` effects compile `vs_1_1`/`ps_1_1` and D3DX 42 falls back to `d3dx9_31.dll` for those. With only + `d3dx9_42.dll` + `d3dcompiler_42.dll` app-local, every effect failed (`Warning: [Render\SimplePainter2.fx] + Failed to create D3DXEffect`) and the game crashed with "Mars: Application error encountered" writing `C:\SOTS\Mars.dmp`. + The full redist fixed it. (Copies of `d3dx9_42.dll` f8b9cfab… and `d3dcompiler_42.dll` 6c976311… are also in `C:\SOTS`; harmless.) +2. **Vulkan loader**: LunarG `vulkan-runtime.exe` (58a9edee…, SDK 1.4.357) installed with `/S` -> + `SysWOW64\vulkan-1.dll` + `System32\vulkan-1.dll` (and `vulkaninfo.exe`). Nothing on the VM provided a loader before. +3. **CPU Vulkan ICD**: mesa-dist-win 26.2.0 `mesa3d-26.2.0-release-msvc.7z` (dcb2719e…) -> `x86\vulkan_lvp.dll` + (f3234859…, 48 MB) + `x86\lvp_icd.x86.json` copied to `C:\SOTS\lvp\`. Registered system-wide for 32-bit: + `HKLM\SOFTWARE\WOW6432Node\Khronos\Vulkan\Drivers`, DWORD value named `C:\SOTS\lvp\lvp_icd.x86.json` = 0. + Verified: `C:\Windows\SysWOW64\vulkaninfo.exe --summary` shows `GPU0 llvmpipe (LLVM 22.1.8, 256 bits)`, Vulkan 1.4.354, `PHYSICAL_DEVICE_TYPE_CPU`. + `launch.cmd` additionally sets `VK_DRIVER_FILES`/`VK_ICD_FILENAMES` to the same json (belt and braces). +4. **DXVK**: `dxvk-3.1.tar.gz` (30f9cc32…) -> `x32/d3d9.dll` (2efb4c00…, 7.5 MB) copied to `C:\SOTS\d3d9.dll`. + No `dxvk.conf` needed. DXVK log confirms: `Found device: llvmpipe … D3D9: Hiding actual GPU, reporting AMD Radeon RX 6700 XT`; + game log then says `Adapter: 0: AMD Radeon RX 6700 XT, Vertex/Pixel Shader 3.0, 3055 MB texture mem`. +5. **Windowed 1024x768**: the video settings file is **`C:\SOTS\display.cfg`** (NOT `sots.ini`/`sots.cfg`, and not the + registry — those strings are for other things). Format is one `key value` per line, keys matched with `_stricmp`, + written back with `fprintf("%s %d")` by the Options screen. Keys (from the exe): `adapter width height + fullscreenWidth fullscreenHeight refresh windowed objectDetail shaderLevel textureQuality filtering antialiasing + maxLights numBackgroundStarsModifier(%f) specularEnabled badgesEnabled decalsEnabled brightness(%.2f) + particleLODScale(%.2f)`. File used is in `recipe/display.cfg` (`windowed 1`, `width 1024`, `height 768`, low detail). + The file does not exist until Options are saved, so the first run was fullscreen (also worked, but any focus + change minimizes an exclusive-fullscreen D3D9 window, which broke UI automation). +6. **Launch as the interactive user** (SSH sessions have no desktop): `recipe/launch.cmd` (cd `C:\SOTS`, sets + `DXVK_LOG_LEVEL=info`, `DXVK_LOG_PATH=C:\SOTS\dxvklog`, `VK_DRIVER_FILES`, runs the exe with stdout to + `dxvklog\run.txt`), started through a scheduled task: + `schtasks /Create /TN SOTS /TR "cmd.exe /c C:\SOTS\launch.cmd" /SC ONCE /ST 00:00 /RL HIGHEST /IT /F` then + `schtasks /Run /TN SOTS`. (`Register-ScheduledTask` kept rejecting the XML; `schtasks /IT` just works.) + Intro movies (Bink) render fine; skip with 3x `qm sendkey 140 esc`. Main menu in ~30 s. Process sits at + ~100-220 % CPU (llvmpipe), ~350-400 MB RSS. Game runs silent: `Failed to initialize DirectSound manager` (no audio device) — harmless. +7. **UI automation** (`recipe/click_helper.ps1`): a second interactive scheduled task `SOTSUI` + (`powershell -WindowStyle Hidden -File C:\SOTS\ui\click_helper.ps1`, also `/IT /RL HIGHEST`) reads + `C:\SOTS\ui\cmd.txt` lines `click X Y | move X Y | key | type | sleep ms | fg` and drives + `SetCursorPos` + `mouse_event`, logging the game window rect to `C:\SOTS\ui\log.txt`. Screen coordinates map 1:1 to + the QEMU screendump (window rect -8,-39..1032,749, client 1024x749 at 0,-8 — the game still hit-tests fine). + Screenshots: `ssh spicy 'echo "screendump /tmp/x.ppm" | qm monitor 140'` + ImageMagick `convert`. + QEMU `mouse_move`/`mouse_button` did NOT register in the game (no USB tablet in the VM config); `SendKeys` text + input works but **Backspace/Esc are ignored** by the game's text fields (hence the concatenated game/save names). + The in-game menu (Save / Options / Quit) is the round button at the bottom-right of the turn bar (1000,714 at 1024x768). + +Click path used: Single Player -> (forced Profile Manager: create profile `re`) -> Done -> Single Player -> Custom -> +OK -> Number of Stars slider to min (28), Players slider to min (2) -> Create Game -> name -> OK -> Launch -> +End Turn -> menu -> Save -> Save/OK/Done -> End Turn. + +## What failed / gotchas + +- `sots.cfg` with `windowed 1` did nothing — wrong file; `display.cfg` is the one (found via xrefs in the exe: + writer at 0x8f0e40, reader at 0x8f0f90 parsing `[esi+0x1c]=windowed`, filename string at 0xa3bed8). +- Effect-compile failure was **not** DXVK/lavapipe: identical failure under Microsoft's own `d3d9.dll` + (which, note, also gives a working SM3.0 HAL device on this VM via `d3d10warp.dll` "Microsoft Basic Render Driver"; + DXVK+lavapipe was kept because it is the stack we want for later, but plain d3d9/WARP is a viable fallback). +- Crash artefacts: the engine ("Mars") pops "Application error encountered. A diagnostic file will be saved." and + writes a minidump to `C:\SOTS\Mars.dmp` — useful for later dynamic work. +- The game exe is 32-bit; imports: `IPHLPAPI d3dx9_42 KERNEL32 USER32 ADVAPI32 SHELL32 WINMM binkw32 MSVCR100 + WSOCK32 MSVCP100 SHLWAPI d3d9 DSOUND GDI32 ole32`. `sots.gob`/`sots_local_en.gob` are plain ZIP archives + (8352 / 2035 entries; text data under `Data/`, shaders under `Render/*.fx`). +- Game window class names from strings (`Kerberos_SwordOfTheStars_WndCls`, `Kerberos_Mars_WndCls`) did not match + `FindWindow`; use the process `MainWindowHandle` from the interactive session. + +## Screenshots (`screenshots/`) + +- `01-main-menu.png` — main menu rendered via DXVK+lavapipe (first run, fullscreen 1024x768, ver.1.8.1 bottom right). +- `02-custom-game-setup.png` — Custom Game Setup with defaults (200 stars, 8 players) before adjusting. +- `03-lobby.png` — lobby: `re` (random) vs `Computer`. +- `04-strategy-map-turn1.png` — strategy map, Turn 1, Gamma Cephei panel. +- `05-turn2-after-endturn.png` — Turn 2 after the first End Turn (autosaves written at this moment). +- `06-save-dialog.png` — Save Game dialog listing the two autosaves. +- `07-game-saved.png` — "Game Successfully Saved" for the manual save. +- `08-turn3.png` — Turn 3 after the second End Turn. + +State left behind: the game is still running windowed on VM140 (task `SOTS`), profile `re`, game on turn 3. + +## Hash note (useful for the verifier) + +sha256 (16-hex prefix): `Autosave EndTurn - turn3.sav` = `verify1.sav` = `MyGameverify1verify1.sav` = bb4fd9ac89f41e3b; +`Autosave - turn2.sav` = `Autosave Backup - turn2.sav` = ab4ac2d7e2977260; `Autosave EndTurn - turn2.sav` = +a3f9dc4b49fc669c; `Autosave - turn3.sav` = 978041acd168b56e. So: serialization is byte-deterministic for the same +game state (no timestamp/random salt in the file), the "EndTurn" autosave is the *pre*-turn state (same as a manual +save made on that turn) and `(Autosave)` is the *post*-turn state. Three distinct states are available: turn 1 +(a3f9…), turn 2 (bb4f…/ab4a… — note these two differ: 66,732 vs 66,739 bytes, i.e. the manual/EndTurn save on turn 2 +is not identical to the post-turn autosave of turn 2 — good diff pair), turn 3 (9780…). diff --git a/verify/harness/run-recipe/click_helper.ps1 b/verify/harness/run-recipe/click_helper.ps1 new file mode 100644 index 0000000..6be76f5 --- /dev/null +++ b/verify/harness/run-recipe/click_helper.ps1 @@ -0,0 +1,42 @@ +Add-Type -AssemblyName System.Windows.Forms +Add-Type @" +using System; using System.Runtime.InteropServices; +public struct RECT { public int L,T,R,B; } +public struct PT { public int X,Y; } +public class W { + [DllImport("user32.dll")] public static extern bool SetCursorPos(int x,int y); + [DllImport("user32.dll")] public static extern void mouse_event(uint f,uint x,uint y,uint d,UIntPtr e); + [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); + [DllImport("user32.dll")] public static extern int GetWindowTextW(IntPtr h, System.Text.StringBuilder s, int n); + [DllImport("user32.dll")] public static extern IntPtr FindWindowW(string c, string t); + [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out RECT r); + [DllImport("user32.dll")] public static extern bool GetClientRect(IntPtr h, out RECT r); + [DllImport("user32.dll")] public static extern bool ClientToScreen(IntPtr h, ref PT p); + [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h); +} +"@ +$log = 'C:\SOTS\ui\log.txt' +$out = @() +$gp = Get-Process | Where-Object {$_.Name -like 'Sword*'} | Select -First 1; $hw = if ($gp) { $gp.MainWindowHandle } else { [IntPtr]::Zero } +if ($hw -eq [IntPtr]::Zero) { $hw = [W]::FindWindowW('Kerberos_Mars_WndCls',$null) } +if ($hw -ne [IntPtr]::Zero) { + $r = New-Object RECT; [W]::GetWindowRect($hw,[ref]$r) | Out-Null + $c = New-Object RECT; [W]::GetClientRect($hw,[ref]$c) | Out-Null + $p = New-Object PT; [W]::ClientToScreen($hw,[ref]$p) | Out-Null + $out += "game hwnd=$hw win=$($r.L),$($r.T)-$($r.R),$($r.B) client=$($c.R)x$($c.B) clientOrigin=$($p.X),$($p.Y)" +} else { $out += "game window not found" } +foreach ($line in Get-Content C:\SOTS\ui\cmd.txt) { + $p2 = $line -split ' ',3 + switch ($p2[0]) { + 'fg' { if ($hw -ne [IntPtr]::Zero) { [W]::SetForegroundWindow($hw) | Out-Null; Start-Sleep -m 500 } } + 'move' { [W]::SetCursorPos([int]$p2[1],[int]$p2[2]); Start-Sleep -m 300 } + 'click' { [W]::SetCursorPos([int]$p2[1],[int]$p2[2]); Start-Sleep -m 600; [W]::mouse_event(2,0,0,0,[UIntPtr]::Zero); Start-Sleep -m 250; [W]::mouse_event(4,0,0,0,[UIntPtr]::Zero); Start-Sleep -m 700 } + 'dbl' { [W]::SetCursorPos([int]$p2[1],[int]$p2[2]); Start-Sleep -m 600; 1..2 | % { [W]::mouse_event(2,0,0,0,[UIntPtr]::Zero); Start-Sleep -m 60; [W]::mouse_event(4,0,0,0,[UIntPtr]::Zero); Start-Sleep -m 100 } } + 'key' { [System.Windows.Forms.SendKeys]::SendWait($p2[1]); Start-Sleep -m 400 } + 'type' { [System.Windows.Forms.SendKeys]::SendWait(($line.Substring(5))); Start-Sleep -m 400 } + 'sleep' { Start-Sleep -m ([int]$p2[1]) } + } +} +$sb = New-Object System.Text.StringBuilder 256; [W]::GetWindowTextW([W]::GetForegroundWindow(),$sb,256) | Out-Null +$out += "done $(Get-Date -Format HH:mm:ss) fg='$($sb.ToString())'" +$out | Set-Content $log diff --git a/verify/harness/run-recipe/display.cfg b/verify/harness/run-recipe/display.cfg new file mode 100644 index 0000000..3db56eb --- /dev/null +++ b/verify/harness/run-recipe/display.cfg @@ -0,0 +1,19 @@ +adapter 0 +width 1024 +height 768 +fullscreenWidth 1024 +fullscreenHeight 768 +refresh 0 +windowed 1 +objectDetail 0 +shaderLevel 0 +textureQuality 0 +filtering 0 +antialiasing 0 +maxLights 1 +specularEnabled 0 +badgesEnabled 1 +decalsEnabled 0 +brightness 1.00 +particleLODScale 0.50 + diff --git a/verify/harness/run-recipe/dxvk_d3d9.log b/verify/harness/run-recipe/dxvk_d3d9.log new file mode 100644 index 0000000..3b6b670 --- /dev/null +++ b/verify/harness/run-recipe/dxvk_d3d9.log @@ -0,0 +1,328 @@ +info: Game: Sword of the Stars.exe +info: DXVK: v3.1 +info: Build: x86 gcc 16.2.0 +info: Vulkan: Found vkGetInstanceProcAddr in vulkan-1.dll @ 0x6a3c1750 +info: Extension providers: +info: Platform WSI +info: OpenVR +info: OpenVR: could not open registry key, status 2 +info: OpenVR: Failed to locate module +info: OpenXR +info: Enabled instance extensions: +info: VK_KHR_get_surface_capabilities2 +info: VK_KHR_surface +info: VK_KHR_surface_maintenance1 +info: VK_KHR_win32_surface +info: Found device: llvmpipe (LLVM 22.1.8, 256 bits) (llvmpipe 26.2.0) +info: D3D9: Detected nonclassical vendor ID: 0x10005 +info: D3D9: Hiding actual GPU, reporting: +info: vendor ID: 0x1002 +info: device ID: 0x73df +info: device description: AMD Radeon RX 6700 XT +info: D3D9: VK_FORMAT_D16_UNORM_S8_UINT -> VK_FORMAT_D24_UNORM_S8_UINT +info: Process set as DPI aware +info: Creating device: +info: llvmpipe (LLVM 22.1.8, 256 bits): +info: Driver : llvmpipe 26.2.0 +info: Queues: +info: Graphics : (0, 0) +info: Transfer : (0, 0) +info: Sparse : (4294967295, 0) +info: Memory: +info: Heap 0: 3.0 GiB (DEVICE_LOCAL) +info: Budget: 3.0 GiB +info: Type 0: DEVICE_LOCAL | HOST_VISIBLE | HOST_COHERENT | HOST_CACHED +info: Enabled extensions: +info: VK_EXT_attachment_feedback_loop_layout +info: VK_EXT_border_color_swizzle +info: VK_EXT_custom_border_color +info: VK_EXT_depth_clip_enable +info: VK_EXT_depth_bias_control +info: VK_EXT_descriptor_heap +info: VK_EXT_dynamic_rendering_unused_attachments +info: VK_EXT_extended_dynamic_state3 +info: VK_EXT_fragment_shader_interlock +info: VK_EXT_graphics_pipeline_library +info: VK_EXT_hdr_metadata +info: VK_EXT_line_rasterization +info: VK_EXT_multi_draw +info: VK_EXT_non_seamless_cube_map +info: VK_EXT_robustness2 +info: VK_EXT_sample_locations +info: VK_EXT_shader_stencil_export +info: VK_EXT_transform_feedback +info: VK_EXT_vertex_attribute_divisor +info: VK_KHR_dynamic_rendering_local_read +info: VK_KHR_incremental_present +info: VK_KHR_load_store_op_none +info: VK_KHR_maintenance5 +info: VK_KHR_maintenance6 +info: VK_KHR_maintenance7 +info: VK_KHR_maintenance8 +info: VK_KHR_maintenance9 +info: VK_KHR_maintenance10 +info: VK_KHR_maintenance11 +info: VK_KHR_pipeline_library +info: VK_KHR_shader_float_controls2 +info: VK_KHR_shader_untyped_pointers +info: VK_KHR_swapchain +info: VK_KHR_swapchain_maintenance1 +info: VK_KHR_swapchain_mutable_format +info: VK_KHR_unified_image_layouts +info: Enabled features: +info: depthBiasClamp : 1 +info: depthBounds : 1 +info: depthClamp : 1 +info: drawIndirectFirstInstance : 1 +info: dualSrcBlend : 1 +info: fillModeNonSolid : 1 +info: fragmentStoresAndAtomics : 1 +info: fullDrawIndexUint32 : 1 +info: geometryShader : 1 +info: imageCubeArray : 1 +info: independentBlend : 1 +info: largePoints : 1 +info: logicOp : 1 +info: multiDrawIndirect : 1 +info: multiViewport : 1 +info: occlusionQueryPrecise : 1 +info: pipelineStatisticsQuery : 1 +info: robustBufferAccess : 1 +info: sampleRateShading : 1 +info: samplerAnisotropy : 1 +info: shaderClipDistance : 1 +info: shaderCullDistance : 1 +info: shaderFloat64 : 1 +info: shaderImageGatherExtended : 1 +info: shaderInt16 : 1 +info: shaderInt64 : 1 +info: shaderUniformBufferArrayDynamicIndexing : 1 +info: shaderSampledImageArrayDynamicIndexing : 1 +info: shaderStorageBufferArrayDynamicIndexing : 1 +info: shaderStorageImageArrayDynamicIndexing : 1 +info: sparseBinding : 0 +info: sparseResidencyBuffer : 0 +info: sparseResidencyImage2D : 0 +info: sparseResidencyImage3D : 0 +info: sparseResidency2Samples : 0 +info: sparseResidency4Samples : 0 +info: sparseResidency8Samples : 0 +info: sparseResidency16Samples : 0 +info: sparseResidencyAliased : 0 +info: shaderResourceResidency : 0 +info: shaderResourceMinLod : 1 +info: tessellationShader : 1 +info: textureCompressionBC : 1 +info: variableMultisampleRate : 1 +info: vertexPipelineStoresAndAtomics : 1 +info: wideLines : 1 +info: shaderDrawParameters : 1 +info: storageBuffer16BitAccess : 1 +info: storagePushConstant16 : 1 +info: bufferDeviceAddress : 1 +info: descriptorIndexing : 1 +info: storageBuffer8BitAccess : 1 +info: storagePushConstant8 : 1 +info: shaderUniformTexelBufferArrayDynamicIndexing : 1 +info: shaderStorageTexelBufferArrayDynamicIndexing : 1 +info: shaderUniformBufferArrayNonUniformIndexing : 1 +info: shaderSampledImageArrayNonUniformIndexing : 1 +info: shaderStorageBufferArrayNonUniformIndexing : 1 +info: shaderStorageImageArrayNonUniformIndexing : 1 +info: shaderUniformTexelBufferArrayNonUniformIndexing : 1 +info: shaderStorageTexelBufferArrayNonUniformIndexing : 1 +info: descriptorBindingSampledImageUpdateAfterBind : 1 +info: descriptorBindingUpdateUnusedWhilePending : 1 +info: descriptorBindingPartiallyBound : 1 +info: drawIndirectCount : 1 +info: hostQueryReset : 1 +info: runtimeDescriptorArray : 1 +info: samplerFilterMinmax : 1 +info: samplerMirrorClampToEdge : 1 +info: scalarBlockLayout : 1 +info: shaderFloat16 : 1 +info: shaderInt8 : 1 +info: shaderOutputViewportIndex : 1 +info: shaderOutputLayer : 1 +info: timelineSemaphore : 1 +info: uniformBufferStandardLayout : 1 +info: vulkanMemoryModel : 1 +info: inlineUniformBlock : 1 +info: computeFullSubgroups : 1 +info: dynamicRendering : 1 +info: maintenance4 : 1 +info: robustImageAccess : 0 +info: pipelineCreationCacheControl : 1 +info: shaderDemoteToHelperInvocation : 1 +info: shaderZeroInitializeWorkgroupMemory : 1 +info: subgroupSizeControl : 1 +info: synchronization2 : 1 +info: VK_EXT_attachment_feedback_loop_layout: +info: attachmentFeedbackLoopLayout : 1 +info: VK_EXT_border_color_swizzle: +info: borderColorSwizzle : 1 +info: borderColorSwizzleFromImage : 1 +info: VK_EXT_conservative_rasterization: +info: extConservativeRasterization : 0 +info: VK_EXT_custom_border_color: +info: customBorderColors : 1 +info: customBorderColorWithoutFormat : 1 +info: VK_EXT_depth_clip_enable: +info: depthClipEnable : 1 +info: VK_EXT_depth_bias_control: +info: depthBiasControl : 1 +info: leastRepresentableValueForceUnormRepresentation : 1 +info: floatRepresentation : 1 +info: depthBiasExact : 1 +info: VK_EXT_descriptor_buffer: +info: descriptorBuffer : 0 +info: VK_EXT_descriptor_heap: +info: descriptorHeap : 1 +info: VK_EXT_dynamic_rendering_unused_attachments: +info: dynamicRenderingUnusedAttachments : 1 +info: VK_EXT_extended_dynamic_state3: +info: extendedDynamicState3AlphaToCoverageEnable : 1 +info: extendedDynamicState3DepthClipEnable : 1 +info: extendedDynamicState3RasterizationSamples : 1 +info: extendedDynamicState3SampleMask : 1 +info: extendedDynamicState3LineRasterizationMode : 1 +info: extendedDynamicState3SampleLocationsEnable : 1 +info: VK_EXT_fragment_shader_interlock: +info: fragmentShaderSampleInterlock : 1 +info: fragmentShaderPixelInterlock : 1 +info: VK_EXT_full_screen_exclusive: +info: extFullScreenExclusive : 0 +info: VK_EXT_graphics_pipeline_library: +info: graphicsPipelineLibrary : 1 +info: VK_EXT_hdr_metadata: +info: extHdrMetadata : 1 +info: VK_EXT_line_rasterization: +info: rectangularLines : 1 +info: smoothLines : 1 +info: VK_EXT_memory_budget: +info: extMemoryBudget : 1 +info: VK_EXT_memory_priority: +info: memoryPriority : 0 +info: VK_EXT_multi_draw: +info: multiDraw : 1 +info: VK_EXT_non_seamless_cube_map: +info: nonSeamlessCubeMap : 1 +info: VK_EXT_pageable_device_local_memory: +info: pageableDeviceLocalMemory : 0 +info: VK_EXT_robustness2: +info: robustBufferAccess2 : 1 +info: robustImageAccess2 : 1 +info: nullDescriptor : 1 +info: VK_EXT_sample_locations: +info: extSampleLocations : 1 +info: VK_EXT_shader_module_identifier: +info: shaderModuleIdentifier : 0 +info: VK_EXT_shader_stencil_export: +info: extShaderStencilExport : 1 +info: VK_EXT_swapchain_colorspace: +info: extSwapchainColorSpace : 0 +info: VK_EXT_swapchain_maintenance1: +info: swapchainMaintenance1 : 0 +info: VK_EXT_transform_feedback: +info: transformFeedback : 1 +info: geometryStreams : 1 +info: VK_EXT_vertex_attribute_divisor: +info: vertexAttributeInstanceRateDivisor : 1 +info: vertexAttributeInstanceRateZeroDivisor : 1 +info: VK_KHR_device_fault: +info: deviceFault : 0 +info: deviceFaultVendorBinary : 0 +info: VK_KHR_dynamic_rendering_local_read: +info: dynamicRenderingLocalRead : 1 +info: VK_KHR_external_memory_win32: +info: khrExternalMemoryWin32 : 0 +info: VK_KHR_external_semaphore_win32: +info: khrExternalSemaphoreWin32 : 0 +info: VK_KHR_incremental_present: +info: khrIncrementalPresent : 1 +info: VK_KHR_load_store_op_none: +info: khrLoadStoreOpNone : 1 +info: VK_KHR_maintenance5: +info: maintenance5 : 1 +info: VK_KHR_maintenance6: +info: maintenance6 : 1 +info: VK_KHR_maintenance7: +info: maintenance7 : 1 +info: VK_KHR_maintenance8: +info: maintenance8 : 1 +info: VK_KHR_maintenance9: +info: maintenance9 : 1 +info: VK_KHR_maintenance10: +info: maintenance10 : 1 +info: VK_KHR_maintenance11: +info: maintenance11 : 1 +info: VK_KHR_pipeline_library: +info: khrPipelineLibrary : 1 +info: VK_KHR_present_id: +info: presentId : 0 +info: VK_KHR_present_id2: +info: presentId2 : 0 +info: VK_KHR_present_wait: +info: presentWait : 0 +info: VK_KHR_present_wait2: +info: presentWait2 : 0 +info: VK_KHR_shader_float_controls2: +info: shaderFloatControls2 : 1 +info: VK_KHR_shader_subgroup_uniform_control_flow: +info: shaderSubgroupUniformControlFlow : 0 +info: VK_KHR_shader_untyped_pointers: +info: shaderUntypedPointers : 1 +info: VK_KHR_swapchain: +info: khrSwapchain : 1 +info: VK_KHR_swapchain_maintenance1: +info: swapchainMaintenance1 : 1 +info: VK_KHR_swapchain_mutable_format: +info: khrSwapchainMutableFormat : 1 +info: VK_KHR_unified_image_layouts: +info: unifiedImageLayouts : 1 +info: VK_KHR_win32_keyed_mutex: +info: khrWin32KeyedMutex : 0 +info: VK_AMD_buffer_marker: +info: amdBufferMarker : 0 +info: VK_NV_device_diagnostic_checkpoints: +info: nvDeviceDiagnosticCheckpoints : 0 +info: VK_NV_low_latency2: +info: nvLowLatency2 : 0 +info: VK_NV_raw_access_chains: +info: shaderRawAccessChains : 0 +info: VK_NVX_binary_import: +info: nvxBinaryImport : 0 +info: VK_NVX_image_view_handle: +info: nvxImageViewHandle : 0 +info: Descriptor sizes (set alignment: 64) +info: Sampler : 32 +info: Uniform buffer : 8 +info: Storage buffer : 8 +info: Uniform texel buffer : 48 +info: Storage texel buffer : 48 +info: Sampled image : 48 +info: Storage image : 48 +info: Input attachment : 48 +info: Memory type mask for buffer resources: 0x1, usage: 0x219ff +info: Creating sampler descriptor heap (64 kB) +info: Graphics pipeline libraries supported +info: Binding model: Descriptor heap +info: Creating resource descriptor heap (4096 kB) +info: D3D9DeviceEx::ResetSwapChain: +info: Requested Presentation Parameters +info: - Width: 0 +info: - Height: 0 +info: - Format: D3D9Format::X8R8G8B8 +info: - Auto Depth Stencil: true +info: ^ Format: D3D9Format::D24S8 +info: - Windowed: true +info: - Swap effect: 1 +info: Found cache file: C:\Users\re\AppData\Local\dxvk\8f139506ba7b49dd.dxvk.bin +info: Cache: 0 shaders (0 kB) +info: DXVK: Using 8 compiler threads +info: Presenter: Actual swapchain properties: +info: Format: VK_FORMAT_B8G8R8A8_UNORM +info: Color space: VK_COLOR_SPACE_SRGB_NONLINEAR_KHR +info: Present mode: VK_PRESENT_MODE_FIFO_KHR (dynamic: yes) +info: Buffer size: 1024x749 +info: Image count: 2 diff --git a/verify/harness/run-recipe/launch.cmd b/verify/harness/run-recipe/launch.cmd new file mode 100644 index 0000000..f71d3ff --- /dev/null +++ b/verify/harness/run-recipe/launch.cmd @@ -0,0 +1,9 @@ +@echo off +cd /d C:\SOTS +set DXVK_LOG_LEVEL=info +set DXVK_LOG_PATH=C:\SOTS\dxvklog +set VK_DRIVER_FILES=C:\SOTS\lvp\lvp_icd.x86.json +set VK_ICD_FILENAMES=C:\SOTS\lvp\lvp_icd.x86.json +echo launch start %date% %time% > C:\SOTS\dxvklog\run.txt +"C:\SOTS\Sword of the Stars.exe" >> C:\SOTS\dxvklog\run.txt 2>&1 +echo exit code %errorlevel% %time% >> C:\SOTS\dxvklog\run.txt diff --git a/verify/harness/run-recipe/lvp_icd.x86.json b/verify/harness/run-recipe/lvp_icd.x86.json new file mode 100644 index 0000000..db91434 --- /dev/null +++ b/verify/harness/run-recipe/lvp_icd.x86.json @@ -0,0 +1,8 @@ +{ + "ICD": { + "api_version": "1.4.354", + "library_arch": "32", + "library_path": ".\\vulkan_lvp.dll" + }, + "file_format_version": "1.0.1" +} \ No newline at end of file diff --git a/verify/harness/run-recipe/run.txt b/verify/harness/run-recipe/run.txt new file mode 100644 index 0000000..426728a --- /dev/null +++ b/verify/harness/run-recipe/run.txt @@ -0,0 +1,24 @@ +launch start Mon 09/07/2026 15:50:12.22 +Root directory: "C:\SOTS" +Mounting current directory... OK +Mounting sots_local_en.gob... OK +Mounting sots.gob... OK + Driver Name: aticfx32.dll Desc: AMD Radeon RX 6700 XT + Driver Vendor: 0x1002 Device: 0x73DF SubSys: 0x0000 Revision: 0x0000 + Driver Version: Product: 0x7FFF Version: 0xFFFF SubVersion: 0xFFFF Build: 0xFFFF + Adapter: 0: 0 : AMD Radeon RX 6700 XT + Resolution: 1024x768 @ 0-Hz + Vertex Shader version: 3.0 + Pixel Shader version: 3.0 + Available Texture Mem: 3058-MB + Total System Mem: 12287-MB +Windows Locale: + Thread Locale ID = 0x00000409 + System Default Locale ID = 0x00000409 + User Default LocaleID = 0x00000409 +Sword of the Stars (ver.1.8.1 Wed Dec 13 03:38:31 2017) +Warning: Failed to initialize DirectSound manager. Application will run silent. +Warning: Movie: BinkSoundUseDirectSound: +Switching player profile to Profiles/Profile_re.ini... +Done. +Warning: GameSetup: No starmap shape selected, using default. diff --git a/verify/harness/run-recipe/session.log b/verify/harness/run-recipe/session.log new file mode 100644 index 0000000..d73b68c --- /dev/null +++ b/verify/harness/run-recipe/session.log @@ -0,0 +1,23 @@ +Root directory: "C:\SOTS" +Mounting current directory... OK +Mounting sots_local_en.gob... OK +Mounting sots.gob... OK + Driver Name: aticfx32.dll Desc: AMD Radeon RX 6700 XT + Driver Vendor: 0x1002 Device: 0x73DF SubSys: 0x0000 Revision: 0x0000 + Driver Version: Product: 0x7FFF Version: 0xFFFF SubVersion: 0xFFFF Build: 0xFFFF + Adapter: 0: 0 : AMD Radeon RX 6700 XT + Resolution: 1024x768 @ 0-Hz + Vertex Shader version: 3.0 + Pixel Shader version: 3.0 + Available Texture Mem: 3058-MB + Total System Mem: 12287-MB +Windows Locale: + Thread Locale ID = 0x00000409 + System Default Locale ID = 0x00000409 + User Default LocaleID = 0x00000409 +Sword of the Stars (ver.1.8.1 Wed Dec 13 03:38:31 2017) +Warning: Failed to initialize DirectSound manager. Application will run silent. +Warning: Movie: BinkSoundUseDirectSound: +Switching player profile to Profiles/Profile_re.ini... +Done. +Warning: GameSetup: No starmap shape selected, using default. diff --git a/verify/results/saves/turn1-state.sav b/verify/results/saves/turn1-state.sav new file mode 100644 index 0000000..15a79f6 Binary files /dev/null and b/verify/results/saves/turn1-state.sav differ diff --git a/verify/results/saves/turn2-state.sav b/verify/results/saves/turn2-state.sav new file mode 100644 index 0000000..45c1346 Binary files /dev/null and b/verify/results/saves/turn2-state.sav differ diff --git a/verify/results/saves/turn2-strict-issues.txt b/verify/results/saves/turn2-strict-issues.txt new file mode 100644 index 0000000..7e4b783 --- /dev/null +++ b/verify/results/saves/turn2-strict-issues.txt @@ -0,0 +1,53 @@ +# save_reader --strict against real save turn2-state.sav (padding=joint, 7 resyncs, 2728 raw bytes) + 16x [warn] /Sim/turnstats/hist/stats :: 'tch': hinted type string not plausible, read as int + 5x [warn] /Sim/Sys :: 'haltv': no value layout fits; skipped 31 bytes to tag + 2x [warn] /Sim/Sys :: 'haltv': no value layout fits; skipped 35 bytes to tag + 1x [error] /createParams/key :: expected string, read int + 1x [error] /sim/fleets[0]/Flt/ships[0]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[0]/Flt/ships[1]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[0]/Flt/ships[2]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[0]/Flt/ships[3]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[0]/Flt/ships[4]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[0]/Flt/ships[5]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[0]/Flt/ships[6]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[0]/Flt/ships[7]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[0]/Flt/ships[8]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[0]/Flt/ships[9]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[1]/Flt/ships[0]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[2]/Flt/ships[0]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[3]/Flt/ships[0]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[4]/Flt/ships[0]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[5]/Flt/ships[0]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/fleets[6]/Flt/ships[0]/Ship/PrisH/prisoners :: missing field 'PrNSp' (frame ended) + 1x [error] /sim/systems[10]/Sys/Name :: expected 'Name', found 'VFlags'; field missing + 1x [error] /sim/systems[10]/Sys/halt[2]/haltv :: expected bool, read raw + 1x [error] /sim/systems[10]/Sys/vnh :: expected 'vnh', found 'VFlags'; field missing + 1x [error] /sim/systems[14]/Sys/Name :: expected 'Name', found 'VFlags'; field missing + 1x [error] /sim/systems[14]/Sys/halt[2]/haltv :: expected bool, read raw + 1x [error] /sim/systems[14]/Sys/vnh :: expected 'vnh', found 'VFlags'; field missing + 1x [error] /sim/systems[16]/Sys/Name :: expected 'Name', found 'VFlags'; field missing + 1x [error] /sim/systems[16]/Sys/halt[2]/haltv :: expected bool, read raw + 1x [error] /sim/systems[16]/Sys/vnh :: expected 'vnh', found 'VFlags'; field missing + 1x [error] /sim/systems[18]/Sys/Name :: expected 'Name', found 'VFlags'; field missing + 1x [error] /sim/systems[18]/Sys/halt[2]/haltv :: expected bool, read raw + 1x [error] /sim/systems[18]/Sys/vnh :: expected 'vnh', found 'VFlags'; field missing + 1x [error] /sim/systems[24]/Sys/Name :: expected 'Name', found 'VFlags'; field missing + 1x [error] /sim/systems[24]/Sys/halt[2]/haltv :: expected bool, read raw + 1x [error] /sim/systems[24]/Sys/vnh :: expected 'vnh', found 'VFlags'; field missing + 1x [error] /sim/systems[25]/Sys/Name :: expected 'Name', found 'VFlags'; field missing + 1x [error] /sim/systems[25]/Sys/halt[2]/haltv :: expected bool, read raw + 1x [error] /sim/systems[25]/Sys/vnh :: expected 'vnh', found 'VFlags'; field missing + 1x [error] /sim/systems[8]/Sys/Name :: expected 'Name', found 'VFlags'; field missing + 1x [error] /sim/systems[8]/Sys/halt[2]/haltv :: expected bool, read raw + 1x [error] /sim/systems[8]/Sys/vnh :: expected 'vnh', found 'VFlags'; field missing + 1x [error] /sim/zdsi :: missing field 'zdsi' (frame ended) + 1x [error] /sim/zdst :: missing field 'zdst' (frame ended) + 1x [warn] /sim/systems[11]/Sys/nve :: 1 unexpected item(s) before 'NVE': 'indi' + 1x [warn] /sim/systems[15]/Sys/nve :: 1 unexpected item(s) before 'NVE': 'indi' + 1x [warn] /sim/systems[18]/Sys/nve :: 1 unexpected item(s) before 'NVE': 'indi' + 1x [warn] /sim/systems[1]/Sys/nve :: 1 unexpected item(s) before 'NVE': 'indi' + 1x [warn] /sim/systems[22]/Sys/nve :: 1 unexpected item(s) before 'NVE': 'indi' + 1x [warn] /sim/systems[25]/Sys/colonies[1]/PID :: 1 unexpected item(s) before 'PID': 'indi' + 1x [warn] /sim/systems[25]/Sys/nve :: 1 unexpected item(s) before 'NVE': 'indi' + 1x [warn] /sim/systems[27]/Sys/nve :: 1 unexpected item(s) before 'NVE': 'indi' + 1x [warn] /sim/systems[4]/Sys/nve :: 1 unexpected item(s) before 'NVE': 'indi' diff --git a/verify/results/saves/turn3-state.sav b/verify/results/saves/turn3-state.sav new file mode 100644 index 0000000..6b3e2e4 Binary files /dev/null and b/verify/results/saves/turn3-state.sav differ