docs: add InitGameState porting plan
Browse filesIncremental strategy: 7 usercall functions ported to pure Rust (Groups A-C),
10 stdcall constructors hooked individually (Groups D-G), with headful+headless
replay tests after each group.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
docs/plans/2026-03-19-init-game-state-design.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# DDGame__InitGameState Porting Plan
|
| 2 |
+
|
| 3 |
+
## Overview
|
| 4 |
+
|
| 5 |
+
Port `DDGame__InitGameState` (0x526500, 1226 decompiled lines) using the same
|
| 6 |
+
incremental strategy as the DDGame constructor: port sub-functions individually
|
| 7 |
+
as hooks, test each group, then eventually combine into a full Rust replacement.
|
| 8 |
+
|
| 9 |
+
InitGameState is called once after the DDGame constructor. It creates all runtime
|
| 10 |
+
game objects: HUD panel, weapon table, team arena, turn game task, camera state,
|
| 11 |
+
scoring, alliance data, and more.
|
| 12 |
+
|
| 13 |
+
## Structure
|
| 14 |
+
|
| 15 |
+
InitGameState is actually two merged functions:
|
| 16 |
+
- Wrapper (0x526500-0x52657B): SEH setup, copies 2 scheme bytes, calls FUN_541620
|
| 17 |
+
- Falls through into CTaskGameState__vmethod_7 (0x52657C, ~2000 instructions)
|
| 18 |
+
|
| 19 |
+
`param_1` = DDGameWrapper. `param_1+0x488` = DDGame. `DDGame+0x24` = GameInfo/scheme.
|
| 20 |
+
|
| 21 |
+
## Phase 1: Port usercall functions to pure Rust
|
| 22 |
+
|
| 23 |
+
All 7 usercall functions are small (<80 lines), mostly pure field writes, and
|
| 24 |
+
have zero or minimal dependencies. Porting them eliminates naked asm bridges.
|
| 25 |
+
|
| 26 |
+
### Group A — Zero-dependency functions (port first, test)
|
| 27 |
+
|
| 28 |
+
| Function | Address | Lines | What it does |
|
| 29 |
+
|---|---|---|---|
|
| 30 |
+
| FUN_541620 | 0x541620 | ~15 | Sprite/gfx table init (fastcall ECX,EDX, loop + 3 fields) |
|
| 31 |
+
| FUN_541060 | 0x541060 | ~15 | Buffer alloc constructor (EAX=size, ESI=output, calls wa_malloc) |
|
| 32 |
+
|
| 33 |
+
### Group B — Team/scoring init (port, test)
|
| 34 |
+
|
| 35 |
+
| Function | Address | Lines | What it does |
|
| 36 |
+
|---|---|---|---|
|
| 37 |
+
| InitTeamScoring | 0x528510 | ~65 | Pure field writes to CGameTask scoring arrays, zero calls |
|
| 38 |
+
| InitAllianceData | 0x5262D0 | ~80 | Alliance bitmask computation, pure field writes + bitmath |
|
| 39 |
+
|
| 40 |
+
### Group C — Turn state + weapon availability (port, test)
|
| 41 |
+
|
| 42 |
+
| Function | Address | Lines | What it does |
|
| 43 |
+
|---|---|---|---|
|
| 44 |
+
| InitTurnState | 0x528690 | ~80 | DDGame turn fields + camera center. Calls FUN_524700 (bridge) |
|
| 45 |
+
| FUN_53FFC0 | 0x53FFC0 | ~50 | Weapon availability switch (port FUN_565960 ~20 lines too) |
|
| 46 |
+
| FUN_528480 | 0x528480 | ~20 | Post-init: scheme flag + 1 vtable dispatch |
|
| 47 |
+
|
| 48 |
+
### Bridge (defer porting)
|
| 49 |
+
|
| 50 |
+
| Function | Address | Lines | Why |
|
| 51 |
+
|---|---|---|---|
|
| 52 |
+
| FUN_524700 | 0x524700 | ~600 | Scheme-version feature flags. Pure computation but massive. |
|
| 53 |
+
|
| 54 |
+
## Phase 2: Hook stdcall constructors individually
|
| 55 |
+
|
| 56 |
+
All verified via RET instruction. Hook each, test in isolation.
|
| 57 |
+
|
| 58 |
+
### Group D — Core game objects
|
| 59 |
+
|
| 60 |
+
| Function | Address | RET | Params | Creates |
|
| 61 |
+
|---|---|---|---|---|
|
| 62 |
+
| HudPanel__Constructor | 0x524070 | 0x4 | 1 | DDGame+0x534 |
|
| 63 |
+
| InitWeaponTable | 0x53CAB0 | 0x4 | 1 | Weapon table setup |
|
| 64 |
+
| InitTeamsFromSetup | 0x5220B0 | 0x8 | 2 | DDGame+0x4628 team arena |
|
| 65 |
+
|
| 66 |
+
### Group E — Task objects
|
| 67 |
+
|
| 68 |
+
| Function | Address | RET | Params | Creates |
|
| 69 |
+
|---|---|---|---|---|
|
| 70 |
+
| TeamManager__Constructor | 0x563D40 | 0x8 | 2 | DDGame+0x530 |
|
| 71 |
+
| CTaskTurnGame__Constructor | 0x55B280 | 0x8 | 2 | Turn game task |
|
| 72 |
+
| CTaskGameState__Constructor | 0x532330 | 0x8 | 2 | Game state task |
|
| 73 |
+
|
| 74 |
+
### Group F — Display objects
|
| 75 |
+
|
| 76 |
+
| Function | Address | RET | Params | Creates |
|
| 77 |
+
|---|---|---|---|---|
|
| 78 |
+
| DisplayGfx__Constructor | 0x563FC0 | 0x14 | 5 | Full display gfx |
|
| 79 |
+
| DDDisplay__ConstructTextbox | 0x4FAF00 | 0xC | 3 | Textbox |
|
| 80 |
+
| FUN_567770 | 0x567770 | 0x4 | 1 | Final conditional display ctor |
|
| 81 |
+
|
| 82 |
+
### Group G — Buffer/stream objects
|
| 83 |
+
|
| 84 |
+
| Function | Address | RET | Params | Creates |
|
| 85 |
+
|---|---|---|---|---|
|
| 86 |
+
| FUN_545FD0 | 0x545FD0 | 0xC | 3 | Buffer object (called twice) |
|
| 87 |
+
| FUN_4FB490 | 0x4FB490 | 0x4 | 1 | GameStateStream sub-init |
|
| 88 |
+
|
| 89 |
+
## Testing Strategy
|
| 90 |
+
|
| 91 |
+
- Run headful + headless replay tests after each group (A, B, C, D, E, F, G)
|
| 92 |
+
- Headless MUST pass (deterministic output match) before proceeding
|
| 93 |
+
- Headful validates no crashes, correct rendering, game completion
|
| 94 |
+
- Name all functions in Ghidra as they're analyzed
|
| 95 |
+
|
| 96 |
+
## Naming Convention
|
| 97 |
+
|
| 98 |
+
Functions ported to Rust: `init_sprite_table`, `init_team_scoring`, etc.
|
| 99 |
+
Functions bridged via FFI: `wa_init_feature_flags` (for FUN_524700)
|
| 100 |
+
Ghidra labels: `CGameTask__InitTeamScoring`, `CGameTask__InitAllianceData`, etc.
|