paavohtl Claude Opus 4.6 (1M context) commited on
Commit
9a56ced
Β·
1 Parent(s): 628ffd1

refactor: extract subsystem creation into construct() methods

Browse files

Move transmute-heavy subsystem creation out of impl_init_hardware into
dedicated methods: DisplayGfx::construct(), GameStats::construct(),
DDNetGameWrapper::construct(), DDKeyboard::poll(), and a local
create_dssound() function for the 33-line COM init sequence.

impl_init_hardware now reads as a high-level orchestrator with only
the DDDisplay::Init retry loop and MapWindowPoints IAT call inline.

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

crates/openwa-core/src/ddkeyboard.rs CHANGED
@@ -43,6 +43,18 @@ pub struct DDKeyboard {
43
  const _: () = assert!(core::mem::size_of::<DDKeyboard>() == 0x33C);
44
 
45
  impl DDKeyboard {
 
 
 
 
 
 
 
 
 
 
 
 
46
  /// Create a new DDKeyboard with inline construction (no native C++ ctor).
47
  ///
48
  /// All fields are zero-initialized, then known fields are set.
 
43
  const _: () = assert!(core::mem::size_of::<DDKeyboard>() == 0x33C);
44
 
45
  impl DDKeyboard {
46
+ /// Poll keyboard state via WA's PollKeyboardState (0x572290).
47
+ ///
48
+ /// # Safety
49
+ /// Must be called from within the WA.exe process.
50
+ pub unsafe fn poll(&mut self) {
51
+ use crate::address::va;
52
+ use crate::rebase::rb;
53
+ let poll_fn: unsafe extern "stdcall" fn(*mut Self) =
54
+ core::mem::transmute(rb(va::DDKEYBOARD_POLL_KEYBOARD_STATE) as usize);
55
+ poll_fn(self);
56
+ }
57
+
58
  /// Create a new DDKeyboard with inline construction (no native C++ ctor).
59
  ///
60
  /// All fields are zero-initialized, then known fields are set.
crates/openwa-core/src/ddnetgame_wrapper.rs CHANGED
@@ -13,3 +13,18 @@ pub struct DDNetGameWrapper {
13
  }
14
 
15
  const _: () = assert!(core::mem::size_of::<DDNetGameWrapper>() == 0x2C);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  }
14
 
15
  const _: () = assert!(core::mem::size_of::<DDNetGameWrapper>() == 0x2C);
16
+
17
+ impl DDNetGameWrapper {
18
+ /// Allocate and construct a DDNetGameWrapper via WA's native constructor.
19
+ ///
20
+ /// # Safety
21
+ /// Must be called from within the WA.exe process.
22
+ pub unsafe fn construct() -> *mut Self {
23
+ use crate::address::va;
24
+ use crate::rebase::rb;
25
+ use crate::wa_alloc::WABox;
26
+ let ctor: unsafe extern "stdcall" fn(*mut Self) -> *mut Self =
27
+ core::mem::transmute(rb(va::DDNETGAME_WRAPPER_CTOR) as usize);
28
+ ctor(WABox::<Self>::alloc(0x2C, 0).leak())
29
+ }
30
+ }
crates/openwa-core/src/display_gfx.rs CHANGED
@@ -14,3 +14,18 @@ pub struct DisplayGfx {
14
  }
15
 
16
  const _: () = assert!(core::mem::size_of::<DisplayGfx>() == 0x24E28);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  }
15
 
16
  const _: () = assert!(core::mem::size_of::<DisplayGfx>() == 0x24E28);
17
+
18
+ impl DisplayGfx {
19
+ /// Allocate and construct a DisplayGfx via WA's native constructor.
20
+ ///
21
+ /// # Safety
22
+ /// Must be called from within the WA.exe process.
23
+ pub unsafe fn construct() -> *mut Self {
24
+ use crate::address::va;
25
+ use crate::rebase::rb;
26
+ use crate::wa_alloc::WABox;
27
+ let ctor: unsafe extern "stdcall" fn(*mut Self) -> *mut Self =
28
+ core::mem::transmute(rb(va::DISPLAYGFX_CTOR) as usize);
29
+ ctor(WABox::<Self>::alloc(0x24E28, 0x24E08).leak())
30
+ }
31
+ }
crates/openwa-core/src/game_stats.rs CHANGED
@@ -15,3 +15,22 @@ pub struct GameStats {
15
  }
16
 
17
  const _: () = assert!(core::mem::size_of::<GameStats>() == 0x3560);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  }
16
 
17
  const _: () = assert!(core::mem::size_of::<GameStats>() == 0x3560);
18
+
19
+ impl GameStats {
20
+ /// Allocate and construct a GameStats via WA's native constructor,
21
+ /// then overlay the GameStats vtable (used in headless mode).
22
+ ///
23
+ /// # Safety
24
+ /// Must be called from within the WA.exe process.
25
+ pub unsafe fn construct() -> *mut Self {
26
+ use crate::address::va;
27
+ use crate::rebase::rb;
28
+ use crate::wa_alloc::WABox;
29
+ let stats = WABox::<Self>::alloc(0x3560, 0x3560).leak();
30
+ let ctor: unsafe extern "stdcall" fn(*mut Self) -> *mut Self =
31
+ core::mem::transmute(rb(va::GAMESTATS_CTOR) as usize);
32
+ ctor(stats);
33
+ (*stats).vtable = rb(va::GAMESTATS_VTABLE) as *mut u8;
34
+ stats
35
+ }
36
+ }
crates/openwa-wormkit/src/replacements/hardware_init.rs CHANGED
@@ -247,6 +247,51 @@ unsafe extern "stdcall" fn call_ddisplay_init(
247
  );
248
  }
249
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  // ─── Implementation ───────────────────────────────────────────────────────────
251
 
252
  unsafe extern "cdecl" fn impl_init_hardware(
@@ -289,9 +334,7 @@ unsafe extern "cdecl" fn impl_init_hardware(
289
 
290
  if !headless {
291
  // ── DisplayGfx ───────────────────────────────────────────────────────
292
- let displaygfx_ctor: unsafe extern "stdcall" fn(*mut DisplayGfx) -> *mut DisplayGfx =
293
- core::mem::transmute(rb(va::DISPLAYGFX_CTOR) as usize);
294
- let display_gfx = displaygfx_ctor(WABox::<DisplayGfx>::alloc(0x24E28, 0x24E08).leak());
295
  (*session).display_gfx = display_gfx as *mut u8;
296
 
297
  // ── DDDisplay::Init retry loop ────────────────────────────────────────
@@ -371,42 +414,7 @@ unsafe extern "cdecl" fn impl_init_hardware(
371
  (*session).palette = pal;
372
 
373
  // ── DSSound ───────────────────────────────────────────────────────────
374
- (*session).sound = core::ptr::null_mut();
375
- {
376
- let snd = WABox::<DSSound>::alloc(0xBE0, 0xBC0).leak();
377
- call_dssound_ctor(snd);
378
- (*snd).hwnd = hwnd;
379
- (*session).sound = snd;
380
-
381
- let ds_create: unsafe extern "stdcall" fn(*const u8, *mut *mut u8, *const u8) -> i32 =
382
- core::mem::transmute(rb(va::DIRECTSOUND_CREATE) as usize);
383
- let hr = ds_create(
384
- core::ptr::null(),
385
- &mut (*snd).direct_sound,
386
- core::ptr::null(),
387
- );
388
-
389
- if hr == 0 {
390
- DSSOUND_INIT_EAX = snd as u32;
391
- let hr2 = call_dssound_init_buffers(
392
- &mut (*snd).primary_buffer,
393
- &mut (*snd).primary_buffer_caps,
394
- );
395
- if hr2 == 0 {
396
- // IDirectSoundBuffer::Play(this, 0, 0, DSBPLAY_LOOPING=1)
397
- const PLAY_VSLOT: usize = 0x30 / 4; // IDirectSoundBuffer vtable slot 12
398
- let pds = (*snd).primary_buffer as *const *const usize;
399
- let vtbl = *pds;
400
- let play: unsafe extern "stdcall" fn(
401
- *const *const usize, u32, u32, u32,
402
- ) -> i32 = core::mem::transmute(*vtbl.add(PLAY_VSLOT));
403
- let hr3 = play(pds, 0, 0, 1);
404
- if hr3 == 0 {
405
- (*snd).init_success = 1;
406
- }
407
- }
408
- }
409
- }
410
 
411
  // ── Streaming audio ───────────────────────────────────────────────────
412
  (*session).streaming_audio = core::ptr::null_mut();
@@ -422,12 +430,7 @@ unsafe extern "cdecl" fn impl_init_hardware(
422
  }
423
  } else {
424
  // ── Headless / stats mode ─────────────────────────────────────────────
425
- let stats = WABox::<GameStats>::alloc(0x3560, 0x3560).leak();
426
- let gamestats_ctor: unsafe extern "stdcall" fn(*mut GameStats) -> *mut GameStats =
427
- core::mem::transmute(rb(va::GAMESTATS_CTOR) as usize);
428
- gamestats_ctor(stats);
429
- (*stats).vtable = rb(va::GAMESTATS_VTABLE) as *mut u8;
430
- (*session).display_gfx = stats as *mut u8;
431
  (*session).keyboard = core::ptr::null_mut();
432
  (*session).sound = core::ptr::null_mut();
433
  (*session).palette = core::ptr::null_mut();
@@ -462,16 +465,12 @@ unsafe extern "cdecl" fn impl_init_hardware(
462
 
463
  let kb = (*session).keyboard;
464
  if !kb.is_null() {
465
- let kb_poll: unsafe extern "stdcall" fn(*mut DDKeyboard) =
466
- core::mem::transmute(rb(va::DDKEYBOARD_POLL_KEYBOARD_STATE) as usize);
467
- kb_poll(kb);
468
  }
469
  }
470
 
471
  // ── DDNetGameWrapper (ALWAYS) ─────────────────────────────────────────────
472
- let net_ctor: unsafe extern "stdcall" fn(*mut DDNetGameWrapper) -> *mut DDNetGameWrapper =
473
- core::mem::transmute(rb(va::DDNETGAME_WRAPPER_CTOR) as usize);
474
- (*session).net_game = net_ctor(WABox::<DDNetGameWrapper>::alloc(0x2C, 0).leak()) as *mut u8;
475
 
476
  let _ = log_line("[hardware_init] GameEngine::InitHardware done");
477
  1
 
247
  );
248
  }
249
 
250
+ // ─── Subsystem creation ───────────────────────────────────────────────────────
251
+
252
+ /// Allocate and initialize a DSSound object with DirectSound COM setup.
253
+ ///
254
+ /// Creates the DSSound, calls DirectSoundCreate, initializes primary buffer,
255
+ /// and starts looping playback. Sets `init_success` if all COM steps succeed.
256
+ /// The returned pointer is always valid (sound may be partially initialized
257
+ /// if COM steps fail, matching original WA behavior).
258
+ unsafe fn create_dssound(hwnd: u32) -> *mut DSSound {
259
+ let snd = WABox::<DSSound>::alloc(0xBE0, 0xBC0).leak();
260
+ call_dssound_ctor(snd);
261
+ (*snd).hwnd = hwnd;
262
+
263
+ let ds_create: unsafe extern "stdcall" fn(*const u8, *mut *mut u8, *const u8) -> i32 =
264
+ core::mem::transmute(rb(va::DIRECTSOUND_CREATE) as usize);
265
+ let hr = ds_create(
266
+ core::ptr::null(),
267
+ &mut (*snd).direct_sound,
268
+ core::ptr::null(),
269
+ );
270
+
271
+ if hr == 0 {
272
+ DSSOUND_INIT_EAX = snd as u32;
273
+ let hr2 = call_dssound_init_buffers(
274
+ &mut (*snd).primary_buffer,
275
+ &mut (*snd).primary_buffer_caps,
276
+ );
277
+ if hr2 == 0 {
278
+ // IDirectSoundBuffer::Play(this, 0, 0, DSBPLAY_LOOPING=1)
279
+ const PLAY_VSLOT: usize = 0x30 / 4; // IDirectSoundBuffer vtable slot 12
280
+ let pds = (*snd).primary_buffer as *const *const usize;
281
+ let vtbl = *pds;
282
+ let play: unsafe extern "stdcall" fn(
283
+ *const *const usize, u32, u32, u32,
284
+ ) -> i32 = core::mem::transmute(*vtbl.add(PLAY_VSLOT));
285
+ let hr3 = play(pds, 0, 0, 1);
286
+ if hr3 == 0 {
287
+ (*snd).init_success = 1;
288
+ }
289
+ }
290
+ }
291
+
292
+ snd
293
+ }
294
+
295
  // ─── Implementation ───────────────────────────────────────────────────────────
296
 
297
  unsafe extern "cdecl" fn impl_init_hardware(
 
334
 
335
  if !headless {
336
  // ── DisplayGfx ───────────────────────────────────────────────────────
337
+ let display_gfx = DisplayGfx::construct();
 
 
338
  (*session).display_gfx = display_gfx as *mut u8;
339
 
340
  // ── DDDisplay::Init retry loop ────────────────────────────────────────
 
414
  (*session).palette = pal;
415
 
416
  // ── DSSound ───────────────────────────────────────────────────────────
417
+ (*session).sound = create_dssound(hwnd);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
 
419
  // ── Streaming audio ───────────────────────────────────────────────────
420
  (*session).streaming_audio = core::ptr::null_mut();
 
430
  }
431
  } else {
432
  // ── Headless / stats mode ─────────────────────────────────────────────
433
+ (*session).display_gfx = GameStats::construct() as *mut u8;
 
 
 
 
 
434
  (*session).keyboard = core::ptr::null_mut();
435
  (*session).sound = core::ptr::null_mut();
436
  (*session).palette = core::ptr::null_mut();
 
465
 
466
  let kb = (*session).keyboard;
467
  if !kb.is_null() {
468
+ (*kb).poll();
 
 
469
  }
470
  }
471
 
472
  // ── DDNetGameWrapper (ALWAYS) ─────────────────────────────────────────────
473
+ (*session).net_game = DDNetGameWrapper::construct() as *mut u8;
 
 
474
 
475
  let _ = log_line("[hardware_init] GameEngine::InitHardware done");
476
  1