paavohtl Claude Opus 4.6 commited on
Commit
5d14bb2
·
1 Parent(s): 768b0bb

refactor: remove DDGameWrapperHandle, retype DDGame.game_state to GameInfo

Browse files

- Delete DDGameWrapperHandle — consumers use *const DDGameWrapper directly
- Retype DDGame.game_state (*mut u8) → game_info (*mut GameInfo)
- Add GameInfo.crosshair_overflow_threshold at 0xD778 (was raw pointer math)
- Use SpeechSlotTable::clear() instead of write_bytes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

crates/openwa-core/src/address.rs CHANGED
@@ -655,8 +655,8 @@ pub mod va {
655
  }
656
 
657
  // === GameInfo struct offsets ===
658
- // These are offsets from the GameInfo pointer at DDGame+0x24 (also called
659
- // "game_state" in some code). Access pattern: *(DDGame+0x24) + offset.
660
 
661
  pub mod game_info_offsets {
662
  // === Speech configuration ===
 
655
  }
656
 
657
  // === GameInfo struct offsets ===
658
+ // These are offsets from the GameInfo pointer at DDGame+0x24
659
+ // (DDGame.game_info). Access pattern: *(DDGame+0x24) + offset.
660
 
661
  pub mod game_info_offsets {
662
  // === Speech configuration ===
crates/openwa-core/src/ddgame.rs CHANGED
@@ -1,3 +1,4 @@
 
1
  use crate::render::RenderQueue;
2
  use crate::speech::SpeechSlotTable;
3
 
@@ -35,8 +36,8 @@ pub struct DDGame {
35
  pub _caller: *mut u8,
36
  /// 0x020: PCLandscape pointer (copied from DDGameWrapper[0x133])
37
  pub landscape: *mut u8,
38
- /// 0x024: Game state pointer (param_10)
39
- pub game_state: *mut u8,
40
  /// 0x028: param_9
41
  pub _param_028: *mut u8,
42
  /// 0x02C: Secondary GfxDir object (0x70C bytes, conditional on GfxHandler 1)
@@ -119,7 +120,7 @@ pub struct DDGame {
119
  /// 0x7D84-0x7EFF: Unknown
120
  ///
121
  /// Known landmarks:
122
- /// - 0x7EF8: flag from game_state+0xF914
123
  /// - 0x7EFC: init 1
124
  pub _unknown_7d84: [u8; 0x7F00 - 0x7D84],
125
 
@@ -162,7 +163,7 @@ const _: () = assert!(core::mem::size_of::<DDGame>() == 0x98B8);
162
  pub mod offsets {
163
  // === Header / init params (0x000-0x02C) ===
164
  pub const LANDSCAPE: usize = 0x020;
165
- pub const GAME_STATE: usize = 0x024;
166
  pub const SECONDARY_GFXDIR: usize = 0x02C;
167
  pub const GRADIENT_IMAGE: usize = 0x030;
168
 
 
1
+ use crate::game_info::GameInfo;
2
  use crate::render::RenderQueue;
3
  use crate::speech::SpeechSlotTable;
4
 
 
36
  pub _caller: *mut u8,
37
  /// 0x020: PCLandscape pointer (copied from DDGameWrapper[0x133])
38
  pub landscape: *mut u8,
39
+ /// 0x024: GameInfo pointer (passed as param_10 to constructor).
40
+ pub game_info: *mut GameInfo,
41
  /// 0x028: param_9
42
  pub _param_028: *mut u8,
43
  /// 0x02C: Secondary GfxDir object (0x70C bytes, conditional on GfxHandler 1)
 
120
  /// 0x7D84-0x7EFF: Unknown
121
  ///
122
  /// Known landmarks:
123
+ /// - 0x7EF8: flag from game_info+0xF914
124
  /// - 0x7EFC: init 1
125
  pub _unknown_7d84: [u8; 0x7F00 - 0x7D84],
126
 
 
163
  pub mod offsets {
164
  // === Header / init params (0x000-0x02C) ===
165
  pub const LANDSCAPE: usize = 0x020;
166
+ pub const GAME_INFO: usize = 0x024;
167
  pub const SECONDARY_GFXDIR: usize = 0x02C;
168
  pub const GRADIENT_IMAGE: usize = 0x030;
169
 
crates/openwa-core/src/game_info.rs CHANGED
@@ -17,8 +17,14 @@ pub struct GameInfo {
17
  /// Used by DSSound_LoadAllSpeechBanks to iterate teams.
18
  pub speech_team_count: u8,
19
 
20
- /// 0x044D-0xDAE7: Unknown
21
- pub _unknown_044d: [u8; 0xDAE8 - 0x44D],
 
 
 
 
 
 
22
 
23
  // --- Cluster 1: data paths ---
24
 
 
17
  /// Used by DSSound_LoadAllSpeechBanks to iterate teams.
18
  pub speech_team_count: u8,
19
 
20
+ /// 0x044D-0xD777: Unknown
21
+ pub _unknown_044d: [u8; 0xD778 - 0x44D],
22
+
23
+ /// 0xD778: Crosshair overflow clamp threshold (compared to 0x11E in render).
24
+ pub crosshair_overflow_threshold: i32,
25
+
26
+ /// 0xD77C-0xDAE7: Unknown
27
+ pub _unknown_d77c: [u8; 0xDAE8 - 0xD77C],
28
 
29
  // --- Cluster 1: data paths ---
30
 
crates/openwa-core/src/wa/ddgame.rs DELETED
@@ -1,43 +0,0 @@
1
- //! Typed handle for DDGameWrapper — the main game engine wrapper object.
2
-
3
- use crate::ddgame::DDGame;
4
- use crate::game_info::GameInfo;
5
-
6
- /// Zero-cost handle to a DDGameWrapper instance (raw pointer as u32).
7
- ///
8
- /// DDGameWrapper is the top-level game engine object (~28KB). It holds
9
- /// the DDGame pointer, graphics handlers, landscape, and display state.
10
- ///
11
- /// Pointer chain: DDGameWrapper → DDGame (+0x488) → GameInfo (+0x24).
12
- #[derive(Clone, Copy)]
13
- #[repr(transparent)]
14
- pub struct DDGameWrapperHandle(pub u32);
15
-
16
- impl DDGameWrapperHandle {
17
- /// Wrap a raw DDGameWrapper pointer.
18
- pub unsafe fn from_raw(ptr: u32) -> Self {
19
- Self(ptr)
20
- }
21
-
22
- /// Read the DDGame pointer from DDGameWrapper+0x488.
23
- pub unsafe fn ddgame(self) -> *mut DDGame {
24
- *((self.0 + 0x488) as *const *mut DDGame)
25
- }
26
-
27
- /// Follow DDGameWrapper → DDGame(+0x488) → GameInfo(+0x24).
28
- ///
29
- /// GameInfo holds team configuration, speech paths, replay state, etc.
30
- /// Some fields are mapped in the struct; others require offset constants
31
- /// from `game_info_offsets`.
32
- pub unsafe fn game_info(self) -> *mut GameInfo {
33
- let ddgame = self.ddgame() as u32;
34
- *((ddgame + 0x24) as *const *mut GameInfo)
35
- }
36
-
37
- /// Pointer to the speech slot table (DDGame+0x77E4, 0x5A0 bytes).
38
- ///
39
- /// Maps (team_index, speech_line_id) → DSSound buffer index.
40
- pub unsafe fn speech_slot_table(self) -> *mut u8 {
41
- (self.ddgame() as u32 + 0x77E4) as *mut u8
42
- }
43
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
crates/openwa-core/src/wa/mod.rs CHANGED
@@ -1,6 +1,5 @@
1
  //! Typed wrappers for calling WA.exe and library functions.
2
 
3
- pub mod ddgame;
4
  pub mod frontend;
5
  pub mod mfc;
6
  pub mod registry;
 
1
  //! Typed wrappers for calling WA.exe and library functions.
2
 
 
3
  pub mod frontend;
4
  pub mod mfc;
5
  pub mod registry;
crates/openwa-wormkit/src/replacements/render.rs CHANGED
@@ -529,8 +529,7 @@ unsafe extern "cdecl" fn draw_crosshair_line_impl(task_ptr: u32) {
529
  let mut overflowed = false;
530
  let mut clamp_factor = 0i32;
531
 
532
- let game_state = ddgame.game_state as *const u8;
533
- let threshold = *(game_state.add(0xD778) as *const i32);
534
 
535
  if threshold > 0x11E {
536
  // Check X overflow: sin > 0 but endpoint wrapped below start
 
529
  let mut overflowed = false;
530
  let mut clamp_factor = 0i32;
531
 
532
+ let threshold = (*ddgame.game_info).crosshair_overflow_threshold;
 
533
 
534
  if threshold > 0x11E {
535
  // Check X overflow: sin > 0 but endpoint wrapped below start
crates/openwa-wormkit/src/replacements/speech.rs CHANGED
@@ -13,7 +13,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
13
  use heapless::CString;
14
 
15
  use openwa_core::rebase::rb;
16
- use openwa_core::wa::ddgame::DDGameWrapperHandle;
17
  use openwa_core::address::va::{self, game_info_offsets};
18
  use openwa_core::speech::SpeechLineTableEntry;
19
 
@@ -314,7 +314,7 @@ usercall_trampoline!(fn trampoline_load_speech_bank; impl_fn = load_speech_bank_
314
  reg = eax; stack_params = 3; ret_bytes = "0xC");
315
 
316
  unsafe extern "cdecl" fn load_speech_bank_impl(
317
- ddgw: DDGameWrapperHandle,
318
  team_index: u32,
319
  speech_base_path: *const u8,
320
  speech_dir: *const u8,
@@ -327,7 +327,7 @@ unsafe extern "cdecl" fn load_speech_bank_impl(
327
  .unwrap_or("?");
328
  let _ = log_line(&format!(
329
  "[Speech] LoadSpeechBank: team={} path=\"{}\" dir=\"{}\" ddgw=0x{:08X}",
330
- team_index, path_str, dir_str, ddgw.0
331
  ));
332
 
333
  let table_base = rb(va::SPEECH_LINE_TABLE) as *const SpeechLineTableEntry;
@@ -357,7 +357,7 @@ unsafe extern "cdecl" fn load_speech_bank_impl(
357
  let _ = full_path.extend_from_bytes(wav_path.as_bytes());
358
 
359
  let result = call_load_speech_wav(
360
- ddgw.0,
361
  team_index,
362
  entry.id,
363
  wav_path.as_ptr(),
@@ -368,7 +368,7 @@ unsafe extern "cdecl" fn load_speech_bank_impl(
368
  // Failed — try fallback with default speech dir if no more alternates
369
  let next_entry = &*table_base.add(i + 1);
370
  if next_entry.name_ptr.is_null() || next_entry.id != entry.id {
371
- let game_info = ddgw.game_info() as *const u8;
372
  let default_dir =
373
  game_info.add(game_info_offsets::DEFAULT_SPEECH_DIR as usize);
374
  let default_base =
@@ -386,7 +386,7 @@ unsafe extern "cdecl" fn load_speech_bank_impl(
386
  let _ = full_path2.extend_from_bytes(wav_path2.as_bytes());
387
 
388
  call_load_speech_wav(
389
- ddgw.0,
390
  team_index,
391
  entry.id,
392
  wav_path2.as_ptr(),
@@ -417,21 +417,23 @@ unsafe extern "cdecl" fn load_speech_bank_impl(
417
  usercall_trampoline!(fn trampoline_load_all_speech_banks;
418
  impl_fn = load_all_speech_banks_impl; reg = esi);
419
 
420
- unsafe extern "cdecl" fn load_all_speech_banks_impl(ddgw: DDGameWrapperHandle) {
421
- // Clear speech slot table: DDGame+0x77E4, 0x5A0 bytes
422
- core::ptr::write_bytes(ddgw.speech_slot_table(), 0, 0x5A0);
 
 
423
 
424
  // Read team count from GameInfo
425
- let game_info = ddgw.game_info();
426
- let team_count = (*game_info).speech_team_count as u32;
427
 
428
  let _ = log_line(&format!(
429
  "[Speech] LoadAllSpeechBanks: ddgw=0x{:08X} teams={}",
430
- ddgw.0, team_count
431
  ));
432
 
433
  // Load speech bank for each team
434
- let gi = game_info as *const u8;
435
  for i in 0..team_count {
436
  let team_offset = (i * game_info_offsets::SPEECH_TEAM_STRIDE) as usize;
437
  let base_path =
 
13
  use heapless::CString;
14
 
15
  use openwa_core::rebase::rb;
16
+ use openwa_core::ddgame_wrapper::DDGameWrapper;
17
  use openwa_core::address::va::{self, game_info_offsets};
18
  use openwa_core::speech::SpeechLineTableEntry;
19
 
 
314
  reg = eax; stack_params = 3; ret_bytes = "0xC");
315
 
316
  unsafe extern "cdecl" fn load_speech_bank_impl(
317
+ ddgw: *const DDGameWrapper,
318
  team_index: u32,
319
  speech_base_path: *const u8,
320
  speech_dir: *const u8,
 
327
  .unwrap_or("?");
328
  let _ = log_line(&format!(
329
  "[Speech] LoadSpeechBank: team={} path=\"{}\" dir=\"{}\" ddgw=0x{:08X}",
330
+ team_index, path_str, dir_str, ddgw as u32
331
  ));
332
 
333
  let table_base = rb(va::SPEECH_LINE_TABLE) as *const SpeechLineTableEntry;
 
357
  let _ = full_path.extend_from_bytes(wav_path.as_bytes());
358
 
359
  let result = call_load_speech_wav(
360
+ ddgw as u32,
361
  team_index,
362
  entry.id,
363
  wav_path.as_ptr(),
 
368
  // Failed — try fallback with default speech dir if no more alternates
369
  let next_entry = &*table_base.add(i + 1);
370
  if next_entry.name_ptr.is_null() || next_entry.id != entry.id {
371
+ let game_info = (*(*ddgw).ddgame).game_info as *const u8;
372
  let default_dir =
373
  game_info.add(game_info_offsets::DEFAULT_SPEECH_DIR as usize);
374
  let default_base =
 
386
  let _ = full_path2.extend_from_bytes(wav_path2.as_bytes());
387
 
388
  call_load_speech_wav(
389
+ ddgw as u32,
390
  team_index,
391
  entry.id,
392
  wav_path2.as_ptr(),
 
417
  usercall_trampoline!(fn trampoline_load_all_speech_banks;
418
  impl_fn = load_all_speech_banks_impl; reg = esi);
419
 
420
+ unsafe extern "cdecl" fn load_all_speech_banks_impl(ddgw: *const DDGameWrapper) {
421
+ let ddgame = &mut *(*ddgw).ddgame;
422
+
423
+ // Clear speech slot table
424
+ ddgame.speech_slot_table.clear();
425
 
426
  // Read team count from GameInfo
427
+ let game_info = &*ddgame.game_info;
428
+ let team_count = game_info.speech_team_count as u32;
429
 
430
  let _ = log_line(&format!(
431
  "[Speech] LoadAllSpeechBanks: ddgw=0x{:08X} teams={}",
432
+ ddgw as u32, team_count
433
  ));
434
 
435
  // Load speech bank for each team
436
+ let gi = ddgame.game_info as *const u8;
437
  for i in 0..team_count {
438
  let team_offset = (i * game_info_offsets::SPEECH_TEAM_STRIDE) as usize;
439
  let base_path =
crates/openwa-wormkit/src/validation/mod.rs CHANGED
@@ -281,7 +281,7 @@ fn validate_struct_offsets(result: &mut ValidationResult) {
281
  let _ = log_validation("");
282
  let _ = log_validation(" DDGame:");
283
  check_offset!(result, DDGame, landscape, 0x20);
284
- check_offset!(result, DDGame, game_state, 0x24);
285
  check_offset!(result, DDGame, arrow_sprites, 0x38);
286
  check_offset!(result, DDGame, arrow_gfxdirs, 0xB8);
287
  check_offset!(result, DDGame, display_gfx, 0x138);
 
281
  let _ = log_validation("");
282
  let _ = log_validation(" DDGame:");
283
  check_offset!(result, DDGame, landscape, 0x20);
284
+ check_offset!(result, DDGame, game_info, 0x24);
285
  check_offset!(result, DDGame, arrow_sprites, 0x38);
286
  check_offset!(result, DDGame, arrow_gfxdirs, 0xB8);
287
  check_offset!(result, DDGame, display_gfx, 0x138);