paavohtl Claude Opus 4.6 (1M context) commited on
Commit
8c440e6
·
1 Parent(s): d085c78

feat: add FireWeapon passthrough hook with weapon type logging

Browse files

Hook FireWeapon (0x51EE60) as naked passthrough: save regs → call
logger → restore → jmp original. Logs weapon type (1-4) and subtypes
for each weapon fired during gameplay.

Verified: headless PASS (byte-identical), headful 133+3 / 0 FAIL.
58 weapon fires logged in test replay (mostly type=1 homing/bazooka).

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

crates/openwa-wormkit/src/replacements/weapon.rs CHANGED
@@ -1,14 +1,18 @@
1
- //! Weapon ammo hooks.
2
  //!
3
  //! Replaces WA.exe functions that manage weapon ammo in the TeamArenaState area (DDGame + 0x4628):
4
  //! - GetAmmo (0x5225E0): query ammo count with delay/phase checks
5
  //! - AddAmmo (0x522640): add ammo to a weapon slot
6
  //! - SubtractAmmo (0x522680): decrement ammo count
7
  //! - CountAliveWorms (0x5225A0): check if >1 worm alive on team
 
 
 
8
 
9
  use openwa_core::address::va;
10
  use openwa_core::engine::ddgame::{self, TeamArenaRef};
11
  use openwa_core::game::Weapon;
 
12
 
13
  use crate::hook::{self, usercall_trampoline};
14
 
@@ -123,6 +127,51 @@ unsafe extern "cdecl" fn count_alive_worms_impl(team_index: u32, arena: TeamAren
123
  usercall_trampoline!(fn trampoline_count_alive_worms; impl_fn = count_alive_worms_impl;
124
  regs = [eax, ecx]);
125
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  // ============================================================
127
  // Hook installation
128
  // ============================================================
@@ -144,6 +193,13 @@ pub fn install() -> Result<(), String> {
144
  va::COUNT_ALIVE_WORMS,
145
  trampoline_count_alive_worms as *const (),
146
  )?;
 
 
 
 
 
 
 
147
  }
148
 
149
  Ok(())
 
1
+ //! Weapon hooks.
2
  //!
3
  //! Replaces WA.exe functions that manage weapon ammo in the TeamArenaState area (DDGame + 0x4628):
4
  //! - GetAmmo (0x5225E0): query ammo count with delay/phase checks
5
  //! - AddAmmo (0x522640): add ammo to a weapon slot
6
  //! - SubtractAmmo (0x522680): decrement ammo count
7
  //! - CountAliveWorms (0x5225A0): check if >1 worm alive on team
8
+ //! - FireWeapon (0x51EE60): passthrough with weapon type logging
9
+
10
+ use core::sync::atomic::{AtomicU32, Ordering};
11
 
12
  use openwa_core::address::va;
13
  use openwa_core::engine::ddgame::{self, TeamArenaRef};
14
  use openwa_core::game::Weapon;
15
+ use openwa_core::log::log_line;
16
 
17
  use crate::hook::{self, usercall_trampoline};
18
 
 
127
  usercall_trampoline!(fn trampoline_count_alive_worms; impl_fn = count_alive_worms_impl;
128
  regs = [eax, ecx]);
129
 
130
+ // ============================================================
131
+ // FireWeapon passthrough (0x51EE60)
132
+ // ============================================================
133
+ // usercall(EAX=weapon_ctx) + 1 stack param (wrapper), RET 0x4.
134
+ // weapon_ctx+0x30 = weapon type (1-4)
135
+ // weapon_ctx+0x34 = subtype for types 3,4
136
+ // weapon_ctx+0x38 = subtype for types 1,2
137
+
138
+ static ORIG_FIRE_WEAPON: AtomicU32 = AtomicU32::new(0);
139
+
140
+ /// Logger called from the naked passthrough. EAX = weapon launch context.
141
+ /// Context+0x30 = weapon type (1-4), +0x34 = subtype for types 3/4,
142
+ /// +0x38 = subtype for types 1/2, +0x3C = params base.
143
+ unsafe extern "cdecl" fn fire_weapon_log(weapon_ctx: u32) {
144
+ let ctx = weapon_ctx as *const u8;
145
+ let weapon_type = *(ctx.add(0x30) as *const i32);
146
+ let subtype_34 = *(ctx.add(0x34) as *const i32);
147
+ let subtype_38 = *(ctx.add(0x38) as *const i32);
148
+
149
+ let _ = log_line(&format!(
150
+ "[Weapon] FireWeapon: type={} sub34={} sub38={}",
151
+ weapon_type, subtype_34, subtype_38
152
+ ));
153
+ }
154
+
155
+ /// Naked passthrough: save regs → call logger → restore → jmp original.
156
+ #[unsafe(naked)]
157
+ unsafe extern "C" fn trampoline_fire_weapon() {
158
+ core::arch::naked_asm!(
159
+ "push eax",
160
+ "push ecx",
161
+ "push edx",
162
+ // call logger with EAX (weapon_ctx) as cdecl arg
163
+ "push eax",
164
+ "call {log_fn}",
165
+ "add esp, 4",
166
+ "pop edx",
167
+ "pop ecx",
168
+ "pop eax",
169
+ "jmp [{orig}]",
170
+ log_fn = sym fire_weapon_log,
171
+ orig = sym ORIG_FIRE_WEAPON,
172
+ );
173
+ }
174
+
175
  // ============================================================
176
  // Hook installation
177
  // ============================================================
 
193
  va::COUNT_ALIVE_WORMS,
194
  trampoline_count_alive_worms as *const (),
195
  )?;
196
+
197
+ let trampoline = hook::install(
198
+ "FireWeapon",
199
+ va::FIRE_WEAPON,
200
+ trampoline_fire_weapon as *const (),
201
+ )?;
202
+ ORIG_FIRE_WEAPON.store(trampoline as u32, Ordering::Relaxed);
203
  }
204
 
205
  Ok(())