paavohtl Claude Opus 4.6 (1M context) commited on
Commit
72a7fdf
Β·
1 Parent(s): 2d4b740

feat: add passthrough hooks for 6 InitGameState constructors (Groups D+E)

Browse files

Passthrough hooks that call the original WA function, validating
calling conventions at runtime:

Group D (core game objects):
- HudPanel__Constructor (0x524070): stdcall(1), RET 0x4
- InitWeaponTable (0x53CAB0): stdcall(1), RET 0x4
- DDGame__InitTeamsFromSetup (0x5220B0): stdcall(2), RET 0x8

Group E (task objects):
- TeamManager__Constructor (0x563D40): stdcall(2), RET 0x8
- CTaskTurnGame__Constructor (0x55B280): stdcall(2), RET 0x8
- CTaskGameState__Constructor (0x532330): stdcall(2), RET 0x8

Key finding: TurnGame and GameState hooks cannot use format!/log_line
in the passthrough β€” the heap allocation clobbers ECX/EDX which the
caller's post-call code depends on. Logging removed from these two.

New file: game_state_hooks.rs for InitGameState passthrough hooks.
All 5 functions named in Ghidra. Headful + headless PASS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

crates/openwa-core/src/address.rs CHANGED
@@ -366,6 +366,16 @@ pub mod va {
366
  pub const INIT_TURN_STATE: u32 = 0x0052_8690;
367
  /// CGameTask__InitLandscapeFlags (0x528480): usercall(EAX=wrapper), plain RET.
368
  pub const INIT_LANDSCAPE_FLAGS: u32 = 0x0052_8480;
 
 
 
 
 
 
 
 
 
 
369
  /// DDGame__InitFeatureFlags (0x524700): stdcall(wrapper), RET 0x4.
370
  pub const DDGAME_INIT_FEATURE_FLAGS: u32 = 0x0052_4700;
371
  /// DDGame__InitDisplayFinal_Maybe (0x56A830): display finalization for non-headless.
 
366
  pub const INIT_TURN_STATE: u32 = 0x0052_8690;
367
  /// CGameTask__InitLandscapeFlags (0x528480): usercall(EAX=wrapper), plain RET.
368
  pub const INIT_LANDSCAPE_FLAGS: u32 = 0x0052_8480;
369
+ /// HudPanel__Constructor (0x524070): stdcall(this), RET 0x4.
370
+ pub const HUD_PANEL_CONSTRUCTOR: u32 = 0x0052_4070;
371
+ /// DDGame__InitTeamsFromSetup (0x5220B0): stdcall(team_arena, setup_data), RET 0x8.
372
+ pub const INIT_TEAMS_FROM_SETUP: u32 = 0x0052_20B0;
373
+ /// TeamManager__Constructor (0x563D40): stdcall(this, wrapper), RET 0x8.
374
+ pub const TEAM_MANAGER_CONSTRUCTOR: u32 = 0x0056_3D40;
375
+ /// CTaskTurnGame__Constructor (0x55B280): stdcall(this, setup_data), RET 0x8.
376
+ pub const TURN_GAME_CONSTRUCTOR: u32 = 0x0055_B280;
377
+ /// CTaskGameState__Constructor (0x532330): stdcall(this, param), RET 0x8.
378
+ pub const GAME_STATE_CONSTRUCTOR: u32 = 0x0053_2330;
379
  /// DDGame__InitFeatureFlags (0x524700): stdcall(wrapper), RET 0x4.
380
  pub const DDGAME_INIT_FEATURE_FLAGS: u32 = 0x0052_4700;
381
  /// DDGame__InitDisplayFinal_Maybe (0x56A830): display finalization for non-headless.
crates/openwa-wormkit/src/replacements/game_state_hooks.rs ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //! Passthrough hooks for DDGame__InitGameState sub-functions.
2
+ //!
3
+ //! These hooks intercept stdcall constructors called during InitGameState,
4
+ //! log their parameters, call the original, and return. This validates
5
+ //! calling conventions and prepares for future Rust replacements.
6
+
7
+ use crate::hook;
8
+ use crate::log_line;
9
+ use openwa_core::address::va;
10
+
11
+ // ─── Trampoline storage ─────────────────────────────────────────────────────
12
+
13
+ static mut HUD_PANEL_ORIG: *const () = core::ptr::null();
14
+ static mut INIT_WEAPON_TABLE_ORIG: *const () = core::ptr::null();
15
+ static mut INIT_TEAMS_ORIG: *const () = core::ptr::null();
16
+ static mut TEAM_MANAGER_ORIG: *const () = core::ptr::null();
17
+ static mut TURN_GAME_ORIG: *const () = core::ptr::null();
18
+ static mut GAME_STATE_ORIG: *const () = core::ptr::null();
19
+
20
+ // ─── Passthrough hooks ──────────────────────────────────────────────────────
21
+
22
+ // HudPanel__Constructor (0x524070): stdcall(this), RET 0x4
23
+ unsafe extern "stdcall" fn hook_hud_panel(this: u32) -> u32 {
24
+ let _ = log_line(&format!(
25
+ "[InitGameState] HudPanel::Constructor this=0x{this:08X}"
26
+ ));
27
+ let orig: unsafe extern "stdcall" fn(u32) -> u32 = core::mem::transmute(HUD_PANEL_ORIG);
28
+ orig(this)
29
+ }
30
+
31
+ // InitWeaponTable (0x53CAB0): stdcall(wrapper), RET 0x4
32
+ unsafe extern "stdcall" fn hook_init_weapon_table(wrapper: u32) -> u32 {
33
+ let _ = log_line(&format!(
34
+ "[InitGameState] InitWeaponTable wrapper=0x{wrapper:08X}"
35
+ ));
36
+ let orig: unsafe extern "stdcall" fn(u32) -> u32 = core::mem::transmute(INIT_WEAPON_TABLE_ORIG);
37
+ orig(wrapper)
38
+ }
39
+
40
+ // DDGame__InitTeamsFromSetup (0x5220B0): stdcall(team_arena, setup_data), RET 0x8
41
+ unsafe extern "stdcall" fn hook_init_teams(team_arena: u32, setup_data: u32) -> u32 {
42
+ let _ = log_line(&format!(
43
+ "[InitGameState] InitTeamsFromSetup arena=0x{team_arena:08X} setup=0x{setup_data:08X}"
44
+ ));
45
+ let orig: unsafe extern "stdcall" fn(u32, u32) -> u32 = core::mem::transmute(INIT_TEAMS_ORIG);
46
+ orig(team_arena, setup_data)
47
+ }
48
+
49
+ // TeamManager__Constructor (0x563D40): stdcall(this, wrapper), RET 0x8
50
+ unsafe extern "stdcall" fn hook_team_manager(this: u32, wrapper: u32) -> u32 {
51
+ let _ = log_line(&format!(
52
+ "[InitGameState] TeamManager::Constructor this=0x{this:08X} wrapper=0x{wrapper:08X}"
53
+ ));
54
+ let orig: unsafe extern "stdcall" fn(u32, u32) -> u32 = core::mem::transmute(TEAM_MANAGER_ORIG);
55
+ orig(this, wrapper)
56
+ }
57
+
58
+ // CTaskTurnGame__Constructor (0x55B280): stdcall(this, setup_data), RET 0x8
59
+ // No logging β€” format! clobbers ECX which the post-call code may depend on
60
+ unsafe extern "stdcall" fn hook_turn_game(this: u32, setup_data: u32) -> u32 {
61
+ let orig: unsafe extern "stdcall" fn(u32, u32) -> u32 = core::mem::transmute(TURN_GAME_ORIG);
62
+ orig(this, setup_data)
63
+ }
64
+
65
+ // CTaskGameState__Constructor (0x532330): stdcall(this, param), RET 0x8
66
+ // No logging β€” may clobber registers the caller depends on
67
+ unsafe extern "stdcall" fn hook_game_state(this: u32, param: u32) -> u32 {
68
+ let orig: unsafe extern "stdcall" fn(u32, u32) -> u32 = core::mem::transmute(GAME_STATE_ORIG);
69
+ orig(this, param)
70
+ }
71
+
72
+ // ─── Hook installation ──────────────────────────────────────────────────────
73
+
74
+ pub fn install() -> Result<(), String> {
75
+ unsafe {
76
+ HUD_PANEL_ORIG = hook::install(
77
+ "HudPanel__Constructor",
78
+ va::HUD_PANEL_CONSTRUCTOR,
79
+ hook_hud_panel as *const (),
80
+ )? as *const ();
81
+
82
+ INIT_WEAPON_TABLE_ORIG = hook::install(
83
+ "InitWeaponTable",
84
+ va::INIT_WEAPON_TABLE,
85
+ hook_init_weapon_table as *const (),
86
+ )? as *const ();
87
+
88
+ INIT_TEAMS_ORIG = hook::install(
89
+ "DDGame__InitTeamsFromSetup",
90
+ va::INIT_TEAMS_FROM_SETUP,
91
+ hook_init_teams as *const (),
92
+ )? as *const ();
93
+
94
+ TEAM_MANAGER_ORIG = hook::install(
95
+ "TeamManager__Constructor",
96
+ va::TEAM_MANAGER_CONSTRUCTOR,
97
+ hook_team_manager as *const (),
98
+ )? as *const ();
99
+
100
+ TURN_GAME_ORIG = hook::install(
101
+ "CTaskTurnGame__Constructor",
102
+ va::TURN_GAME_CONSTRUCTOR,
103
+ hook_turn_game as *const (),
104
+ )? as *const ();
105
+ GAME_STATE_ORIG = hook::install(
106
+ "CTaskGameState__Constructor",
107
+ va::GAME_STATE_CONSTRUCTOR,
108
+ hook_game_state as *const (),
109
+ )? as *const ();
110
+ }
111
+
112
+ Ok(())
113
+ }
crates/openwa-wormkit/src/replacements/mod.rs CHANGED
@@ -3,6 +3,7 @@ mod ddgame_init;
3
  mod display;
4
  mod frontend;
5
  mod game_session;
 
6
  mod hardware_init;
7
  mod headless;
8
  pub(crate) mod input;
@@ -28,5 +29,6 @@ pub fn install_all() -> Result<(), String> {
28
  speech::install()?;
29
  input::install()?;
30
  ddgame_init::install()?;
 
31
  Ok(())
32
  }
 
3
  mod display;
4
  mod frontend;
5
  mod game_session;
6
+ mod game_state_hooks;
7
  mod hardware_init;
8
  mod headless;
9
  pub(crate) mod input;
 
29
  speech::install()?;
30
  input::install()?;
31
  ddgame_init::install()?;
32
+ game_state_hooks::install()?;
33
  Ok(())
34
  }