paavohtl Claude Opus 4.6 (1M context) commited on
Commit
e209bb6
Β·
1 Parent(s): 62e3695

refactor: propagate WeaponReleaseContext type through fire dispatch

Browse files

- fire_weapon (renamed from fire_weapon_impl) now takes typed
*const WeaponReleaseContext instead of *const u8
- Convert from extern "cdecl" to plain unsafe fn β€” only called
from Rust (weapon_release_impl), no longer via naked trampoline
- Replace manual FireWeapon trampoline with install_trap! β€” the WA
function at 0x51EE60 is fully superseded by weapon_release
- All fire sub-function bridges use typed context pointer
- Remove ORIG_FIRE_WEAPON static (no longer needed)

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

crates/openwa-wormkit/src/replacements/weapon.rs CHANGED
@@ -26,6 +26,7 @@ use openwa_core::task::Task;
26
  use openwa_core::task::worm::{CTaskWorm, WormState};
27
 
28
  use crate::hook::{self, usercall_trampoline};
 
29
 
30
  // ============================================================
31
  // AddAmmo replacement (0x522640)
@@ -138,66 +139,21 @@ usercall_trampoline!(fn trampoline_count_alive_worms; impl_fn = count_alive_worm
138
  regs = [eax, ecx]);
139
 
140
  // ============================================================
141
- // FireWeapon replacement (0x51EE60)
142
  // ============================================================
143
- // Convention: usercall(EAX=worm, ECX=local_struct) + 1 stack(worm), RET 0x4.
144
- // Note: EAX = *(CTaskWorm+0x36C) = worm self-pointer, so EAX == stack param.
145
- //
146
- // Weapon launch data offsets (relative to worm/EAX):
147
- // +0x30 = weapon type (1-4)
148
- // +0x34 = subtype for types 3,4
149
- // +0x38 = subtype for types 1,2
150
- // +0x3C = params base
151
- //
152
- // worm = CTaskWorm pointer (ESI in original).
153
- // local_struct = stack-local buffer from WeaponRelease (ECX at call site).
154
- //
155
- // Sub-functions are usercall: ESI=worm, ECX=local_struct (for some).
156
- // We capture all three in our naked trampoline.
157
-
158
- static ORIG_FIRE_WEAPON: AtomicU32 = AtomicU32::new(0);
159
-
160
- /// Naked trampoline for FireWeapon.
161
- /// Must save ALL callee-saved registers (ESI, EDI, EBX, EBP) because the
162
- /// Rust cdecl impl may clobber them, and WeaponRelease (our caller) depends
163
- /// on them being preserved.
164
- #[unsafe(naked)]
165
- unsafe extern "C" fn trampoline_fire_weapon() {
166
- core::arch::naked_asm!(
167
- // Save all callee-saved + EDX
168
- "push ebx",
169
- "push esi",
170
- "push edi",
171
- "push ebp",
172
- "push edx",
173
- // Push cdecl args: (weapon_ctx=EAX, local_struct=ECX, worm)
174
- // Stack: 5 pushes (20) + ret (4) = 24 to stack param
175
- "push [esp+24]", // worm
176
- "push ecx", // local_struct
177
- "push eax", // weapon_ctx
178
- "call {impl_fn}",
179
- "add esp, 12",
180
- // Restore everything
181
- "pop edx",
182
- "pop ebp",
183
- "pop edi",
184
- "pop esi",
185
- "pop ebx",
186
- "ret 0x4",
187
- impl_fn = sym fire_weapon_impl,
188
- );
189
- }
190
 
191
- /// Rust implementation of FireWeapon dispatch.
192
  ///
193
- /// `entry`: EAX = active WeaponEntry pointer (from CTaskWorm+0x36C).
194
- /// `local_struct`: ECX = stack-local buffer from WeaponRelease.
195
- /// `worm`: stack param = CTaskWorm pointer (ESI in original).
196
  ///
197
- /// Completion flag at worm+0x3C (CGameTask.subclass_data[12]).
198
- /// Params pointer at entry+0x3C (WeaponEntry.fire_complete) β€” different object, same offset.
199
- pub(crate) unsafe extern "cdecl" fn fire_weapon_impl(
200
- entry: *const WeaponEntry, local_struct: *const u8, worm: *mut CTaskWorm,
 
 
 
201
  ) {
202
  use openwa_core::rebase::rb;
203
 
@@ -216,25 +172,25 @@ pub(crate) unsafe extern "cdecl" fn fire_weapon_impl(
216
  use openwa_core::game::weapon::{FireType, FireMethod};
217
  match FireType::try_from(fire_type) {
218
  Ok(FireType::Projectile) => match FireMethod::try_from(fire_method) {
219
- Ok(FireMethod::PlacedExplosive) => call_fire_placed_explosive(worm, fire_params, local_struct, rb(0x51EC80)),
220
- Ok(FireMethod::ProjectileFire) => call_fire_stdcall3(worm, fire_params, local_struct, rb(0x51DFB0)),
221
- Ok(FireMethod::CreateWeaponProjectile) => call_fire_thiscall2(worm, fire_params, local_struct, rb(0x51E0F0)),
222
- Ok(FireMethod::CreateArrow) => call_fire_thiscall2(worm, fire_params, local_struct, rb(0x51ED90)),
223
  _ => {}
224
  },
225
  Ok(FireType::Rope) => match FireMethod::try_from(fire_method) {
226
- Ok(FireMethod::PlacedExplosive) => call_fire_stdcall3(worm, fire_params, local_struct, rb(0x51E1C0)), // RopeType1
227
- Ok(FireMethod::ProjectileFire) => call_fire_thiscall2(worm, fire_params, local_struct, rb(0x51E0F0)),
228
- Ok(FireMethod::CreateWeaponProjectile) => call_fire_stdcall3(worm, fire_params, local_struct, rb(0x51E240)), // RopeType3
229
  _ => {}
230
  },
231
  Ok(FireType::Strike) => {
232
  // StrikeFire takes a pointer to the subtype_34 field (reinterpreted as fire params)
233
  let subtype_34_ptr = &raw const (*entry).special_subtype as *const WeaponFireParams;
234
- call_fire_stdcall3(worm, subtype_34_ptr, local_struct, rb(0x51E2C0));
235
  }
236
  Ok(FireType::Special) => {
237
- fire_weapon_special((*entry).special_subtype, entry, worm, local_struct);
238
  }
239
  _ => {}
240
  }
@@ -248,10 +204,10 @@ pub(crate) unsafe extern "cdecl" fn fire_weapon_impl(
248
  // the usercall context that sub-functions expect.
249
 
250
  /// Bridge: PlacedExplosive β€” usercall(ECX=local_struct, EDX=worm, [ESP+4]=fire_params), RET 0x4.
251
- /// Args: (worm, fire_params, local_struct, addr).
252
  #[unsafe(naked)]
253
  unsafe extern "C" fn call_fire_placed_explosive(
254
- _worm: *mut CTaskWorm, _fire_params: *const WeaponFireParams, _local_struct: *const u8, _addr: u32,
255
  ) {
256
  core::arch::naked_asm!(
257
  "push ebx",
@@ -274,7 +230,7 @@ unsafe extern "C" fn call_fire_placed_explosive(
274
  /// Bridge: Projectile/Rope/Grenade β€” stdcall(worm, fire_params, local_struct), RET 0xC.
275
  #[unsafe(naked)]
276
  unsafe extern "C" fn call_fire_stdcall3(
277
- _worm: *mut CTaskWorm, _fire_params: *const WeaponFireParams, _local: *const u8, _addr: u32,
278
  ) {
279
  core::arch::naked_asm!(
280
  "push ebx",
@@ -297,7 +253,7 @@ unsafe extern "C" fn call_fire_stdcall3(
297
  /// Bridge: CreateWeaponProjectile β€” thiscall(ECX=worm, fire_params, local_struct), RET 0x8.
298
  #[unsafe(naked)]
299
  unsafe extern "C" fn call_fire_thiscall2(
300
- _worm: *mut CTaskWorm, _fire_params: *const WeaponFireParams, _local: *const u8, _addr: u32,
301
  ) {
302
  core::arch::naked_asm!(
303
  "push ebx",
@@ -322,7 +278,7 @@ unsafe extern "C" fn call_fire_thiscall2(
322
  #[unsafe(naked)]
323
  #[allow(dead_code)]
324
  unsafe extern "C" fn call_fire_stdcall2(
325
- _worm: *mut CTaskWorm, _fire_params: *const WeaponFireParams, _local: *const u8, _addr: u32,
326
  ) {
327
  core::arch::naked_asm!(
328
  "push ebx",
@@ -347,7 +303,7 @@ unsafe extern "C" fn call_fire_stdcall2(
347
  /// Some handlers explicitly set EAX=worm or EAX=*(worm+0x44).
348
  /// Handlers without explicit MOV EAX inherit the entry pointer.
349
  unsafe fn fire_weapon_special(
350
- subtype: i32, entry: *const WeaponEntry, worm: *mut CTaskWorm, local_struct: *const u8,
351
  ) {
352
  use openwa_core::game::weapon::SpecialFireSubtype as S;
353
  use openwa_core::rebase::rb;
@@ -357,13 +313,13 @@ unsafe fn fire_weapon_special(
357
 
358
  match S::try_from(subtype) {
359
  Ok(S::Blowtorch) => CTaskWorm::set_state_raw(worm, WormState::Blowtorch),
360
- Ok(S::PneumaticDrill) => fire_drill(worm, local_struct),
361
- Ok(S::Girder) => fire_girder(worm, params_38_ptr, local_struct),
362
  Ok(S::BaseballBat) => CTaskWorm::set_state_raw(worm, WormState::BaseballBat),
363
  Ok(S::FirePunch) => CTaskWorm::set_state_raw(worm, WormState::FirePunch),
364
  Ok(S::DragonBall) => CTaskWorm::set_state_raw(worm, WormState::DragonBall),
365
  Ok(S::Kamikaze) => CTaskWorm::set_state_raw(worm, WormState::Kamikaze),
366
- Ok(S::Prod) => fire_prod(worm, local_struct),
367
  Ok(S::AirStrike) => fire_air_strike(worm),
368
  Ok(S::ScalesOfJustice) => CTaskWorm::set_state_raw(worm, WormState::ScalesOfJustice),
369
  Ok(S::NapalmStrike) => fire_send_team_message(worm, 0x2B),
@@ -1488,8 +1444,7 @@ pub fn install() -> Result<(), String> {
1488
  let _ = hook::install("GetAmmo", va::GET_AMMO, trampoline_get_ammo as *const ())?;
1489
  let _ = hook::install("SubtractAmmo", va::SUBTRACT_AMMO, trampoline_subtract_ammo as *const ())?;
1490
  let _ = hook::install("CountAliveWorms", va::COUNT_ALIVE_WORMS, trampoline_count_alive_worms as *const ())?;
1491
- let trampoline = hook::install("FireWeapon", va::FIRE_WEAPON, trampoline_fire_weapon as *const ())?;
1492
- ORIG_FIRE_WEAPON.store(trampoline as u32, Ordering::Relaxed);
1493
 
1494
  // Passthrough hooks on fire sub-functions (log + call original)
1495
  let t = hook::install("CreateWeaponProjectile", va::CREATE_WEAPON_PROJECTILE, trampoline_create_weapon_projectile as *const ())?;
 
26
  use openwa_core::task::worm::{CTaskWorm, WormState};
27
 
28
  use crate::hook::{self, usercall_trampoline};
29
+ use crate::replacements::weapon_release::WeaponReleaseContext;
30
 
31
  // ============================================================
32
  // AddAmmo replacement (0x522640)
 
139
  regs = [eax, ecx]);
140
 
141
  // ============================================================
142
+ // FireWeapon (0x51EE60) β€” trapped, called directly from weapon_release
143
  // ============================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
+ /// FireWeapon dispatch β€” called directly by `weapon_release_impl`.
146
  ///
147
+ /// The WA function at 0x51EE60 is trapped via `install_trap!`; its only
148
+ /// caller (WeaponRelease) is ported and invokes this Rust function instead.
 
149
  ///
150
+ /// Dispatches to type-specific fire sub-functions based on `entry.fire_type`
151
+ /// and `entry.fire_method` / `entry.special_subtype`.
152
+ /// Sets the completion flag at worm+0x3C before and after dispatch.
153
+ pub(crate) unsafe fn fire_weapon(
154
+ entry: *const WeaponEntry,
155
+ ctx: *const WeaponReleaseContext,
156
+ worm: *mut CTaskWorm,
157
  ) {
158
  use openwa_core::rebase::rb;
159
 
 
172
  use openwa_core::game::weapon::{FireType, FireMethod};
173
  match FireType::try_from(fire_type) {
174
  Ok(FireType::Projectile) => match FireMethod::try_from(fire_method) {
175
+ Ok(FireMethod::PlacedExplosive) => call_fire_placed_explosive(worm, fire_params, ctx, rb(0x51EC80)),
176
+ Ok(FireMethod::ProjectileFire) => call_fire_stdcall3(worm, fire_params, ctx, rb(0x51DFB0)),
177
+ Ok(FireMethod::CreateWeaponProjectile) => call_fire_thiscall2(worm, fire_params, ctx, rb(0x51E0F0)),
178
+ Ok(FireMethod::CreateArrow) => call_fire_thiscall2(worm, fire_params, ctx, rb(0x51ED90)),
179
  _ => {}
180
  },
181
  Ok(FireType::Rope) => match FireMethod::try_from(fire_method) {
182
+ Ok(FireMethod::PlacedExplosive) => call_fire_stdcall3(worm, fire_params, ctx, rb(0x51E1C0)), // RopeType1
183
+ Ok(FireMethod::ProjectileFire) => call_fire_thiscall2(worm, fire_params, ctx, rb(0x51E0F0)),
184
+ Ok(FireMethod::CreateWeaponProjectile) => call_fire_stdcall3(worm, fire_params, ctx, rb(0x51E240)), // RopeType3
185
  _ => {}
186
  },
187
  Ok(FireType::Strike) => {
188
  // StrikeFire takes a pointer to the subtype_34 field (reinterpreted as fire params)
189
  let subtype_34_ptr = &raw const (*entry).special_subtype as *const WeaponFireParams;
190
+ call_fire_stdcall3(worm, subtype_34_ptr, ctx, rb(0x51E2C0));
191
  }
192
  Ok(FireType::Special) => {
193
+ fire_weapon_special((*entry).special_subtype, entry, worm, ctx);
194
  }
195
  _ => {}
196
  }
 
204
  // the usercall context that sub-functions expect.
205
 
206
  /// Bridge: PlacedExplosive β€” usercall(ECX=local_struct, EDX=worm, [ESP+4]=fire_params), RET 0x4.
207
+ /// Args: (worm, fire_params, ctx, addr).
208
  #[unsafe(naked)]
209
  unsafe extern "C" fn call_fire_placed_explosive(
210
+ _worm: *mut CTaskWorm, _fire_params: *const WeaponFireParams, _ctx: *const WeaponReleaseContext, _addr: u32,
211
  ) {
212
  core::arch::naked_asm!(
213
  "push ebx",
 
230
  /// Bridge: Projectile/Rope/Grenade β€” stdcall(worm, fire_params, local_struct), RET 0xC.
231
  #[unsafe(naked)]
232
  unsafe extern "C" fn call_fire_stdcall3(
233
+ _worm: *mut CTaskWorm, _fire_params: *const WeaponFireParams, _ctx: *const WeaponReleaseContext, _addr: u32,
234
  ) {
235
  core::arch::naked_asm!(
236
  "push ebx",
 
253
  /// Bridge: CreateWeaponProjectile β€” thiscall(ECX=worm, fire_params, local_struct), RET 0x8.
254
  #[unsafe(naked)]
255
  unsafe extern "C" fn call_fire_thiscall2(
256
+ _worm: *mut CTaskWorm, _fire_params: *const WeaponFireParams, _ctx: *const WeaponReleaseContext, _addr: u32,
257
  ) {
258
  core::arch::naked_asm!(
259
  "push ebx",
 
278
  #[unsafe(naked)]
279
  #[allow(dead_code)]
280
  unsafe extern "C" fn call_fire_stdcall2(
281
+ _worm: *mut CTaskWorm, _fire_params: *const WeaponFireParams, _ctx: *const WeaponReleaseContext, _addr: u32,
282
  ) {
283
  core::arch::naked_asm!(
284
  "push ebx",
 
303
  /// Some handlers explicitly set EAX=worm or EAX=*(worm+0x44).
304
  /// Handlers without explicit MOV EAX inherit the entry pointer.
305
  unsafe fn fire_weapon_special(
306
+ subtype: i32, entry: *const WeaponEntry, worm: *mut CTaskWorm, ctx: *const WeaponReleaseContext,
307
  ) {
308
  use openwa_core::game::weapon::SpecialFireSubtype as S;
309
  use openwa_core::rebase::rb;
 
313
 
314
  match S::try_from(subtype) {
315
  Ok(S::Blowtorch) => CTaskWorm::set_state_raw(worm, WormState::Blowtorch),
316
+ Ok(S::PneumaticDrill) => fire_drill(worm, ctx as *const u8),
317
+ Ok(S::Girder) => fire_girder(worm, params_38_ptr, ctx as *const u8),
318
  Ok(S::BaseballBat) => CTaskWorm::set_state_raw(worm, WormState::BaseballBat),
319
  Ok(S::FirePunch) => CTaskWorm::set_state_raw(worm, WormState::FirePunch),
320
  Ok(S::DragonBall) => CTaskWorm::set_state_raw(worm, WormState::DragonBall),
321
  Ok(S::Kamikaze) => CTaskWorm::set_state_raw(worm, WormState::Kamikaze),
322
+ Ok(S::Prod) => fire_prod(worm, ctx as *const u8),
323
  Ok(S::AirStrike) => fire_air_strike(worm),
324
  Ok(S::ScalesOfJustice) => CTaskWorm::set_state_raw(worm, WormState::ScalesOfJustice),
325
  Ok(S::NapalmStrike) => fire_send_team_message(worm, 0x2B),
 
1444
  let _ = hook::install("GetAmmo", va::GET_AMMO, trampoline_get_ammo as *const ())?;
1445
  let _ = hook::install("SubtractAmmo", va::SUBTRACT_AMMO, trampoline_subtract_ammo as *const ())?;
1446
  let _ = hook::install("CountAliveWorms", va::COUNT_ALIVE_WORMS, trampoline_count_alive_worms as *const ())?;
1447
+ hook::install_trap!("FireWeapon", va::FIRE_WEAPON);
 
1448
 
1449
  // Passthrough hooks on fire sub-functions (log + call original)
1450
  let t = hook::install("CreateWeaponProjectile", va::CREATE_WEAPON_PROJECTILE, trampoline_create_weapon_projectile as *const ())?;
crates/openwa-wormkit/src/replacements/weapon_release.rs CHANGED
@@ -28,7 +28,7 @@ usercall_trampoline!(fn trampoline_weapon_release; impl_fn = weapon_release_impl
28
  /// FireWeapon as the `local_struct` (ECX) parameter.
29
  #[repr(C)]
30
  #[derive(Clone, Copy)]
31
- struct WeaponReleaseContext {
32
  team_id: u32,
33
  worm_id: u32,
34
  spawn_x: u32,
@@ -399,8 +399,7 @@ unsafe extern "cdecl" fn weapon_release_impl(
399
 
400
  // ── 11. Call FireWeapon ──────────────────────────────────
401
  let entry = (*worm).active_weapon_entry;
402
- let ctx_ptr = &ctx as *const WeaponReleaseContext as *const u8;
403
- weapon::fire_weapon_impl(entry, ctx_ptr, worm);
404
 
405
  let _ = log_line(&format!(
406
  "[WeaponRelease] worm=0x{:08X} weapon={:?} type={} sub34={} sub38={}",
 
28
  /// FireWeapon as the `local_struct` (ECX) parameter.
29
  #[repr(C)]
30
  #[derive(Clone, Copy)]
31
+ pub(crate) struct WeaponReleaseContext {
32
  team_id: u32,
33
  worm_id: u32,
34
  spawn_x: u32,
 
399
 
400
  // ── 11. Call FireWeapon ──────────────────────────────────
401
  let entry = (*worm).active_weapon_entry;
402
+ weapon::fire_weapon(entry, &ctx, worm);
 
403
 
404
  let _ = log_line(&format!(
405
  "[WeaponRelease] worm=0x{:08X} weapon={:?} type={} sub34={} sub38={}",