# 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…).