paavohtl Claude Opus 4.6 (1M context) commited on
Commit
86781eb
·
1 Parent(s): 77aec3a

refactor: add TeamIndexMap, RenderEntry, NetBridge structs to DDGame

Browse files

Add three new typed structs replacing raw pointer arithmetic:

- TeamIndexMap (0x64 bytes): entries[i16;16] + count + gap + terminator.
Three instances at DDGame+0x7650 replace 6 fragmented byte arrays.
- RenderEntry (0x14 bytes): active flag + unknown data.
14 entries at DDGame+0x73B0 replace opaque _unknown_73b0 blob.
- NetBridge (0x2C bytes): ddgame back-pointer + net_config bytes.
Created for online games only, replaces ddgame_secondary raw pointer.

ddgame_init_render_indices now takes *mut DDGame and uses pure field
access. DDGameWrapper.ddgame_secondary renamed to net_bridge with
typed *mut NetBridge pointer.

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

crates/openwa-core/src/engine/ddgame.rs CHANGED
@@ -8,6 +8,7 @@ use crate::display::gradient::compute_complex_gradient;
8
  use crate::display::palette::Palette;
9
  use crate::engine::ddgame_wrapper::DDGameWrapper;
10
  use crate::engine::game_info::GameInfo;
 
11
  use crate::input::keyboard::DDKeyboard;
12
  use crate::rebase::rb;
13
  // Re-export public GfxHandler functions so existing `engine::ddgame::*` imports keep working.
@@ -222,8 +223,10 @@ pub struct DDGame {
222
  pub viewport_width_2: i32,
223
  /// 0x738C: Viewport height max/duplicate (Fixed-point).
224
  pub viewport_height_2: i32,
225
- /// 0x7390-0x739F: Unknown
226
- pub _unknown_7390: [u8; 0x73A0 - 0x7390],
 
 
227
 
228
  /// 0x73A0: Camera X position (Fixed-point, e.g. 393.0).
229
  pub camera_x: i32,
@@ -234,22 +237,16 @@ pub struct DDGame {
234
  /// 0x73AC: Camera target Y (Fixed-point).
235
  pub camera_target_y: i32,
236
 
237
- /// 0x73B0-0x764F: Unknown
238
- pub _unknown_73b0: [u8; 0x7650 - 0x73B0],
239
-
240
- /// 0x7650-0x768F: Team index mapping table 1.
241
- /// Packed u16 pairs: [0,1], [2,3], ..., [14,15]. Team-to-slot or turn order.
242
- pub team_index_map_1: [u8; 0x7690 - 0x7650],
243
- /// 0x7690-0x76AF: Unknown
244
- pub _unknown_7690: [u8; 0x76B0 - 0x7690],
245
- /// 0x76B0-0x76EF: Team index mapping table 2 (same pattern).
246
- pub team_index_map_2: [u8; 0x76F0 - 0x76B0],
247
- /// 0x76F0-0x7717: Unknown
248
- pub _unknown_76f0: [u8; 0x7718 - 0x76F0],
249
- /// 0x7718-0x7757: Team index mapping table 3 (similar pattern, slightly different end).
250
- pub team_index_map_3: [u8; 0x7758 - 0x7718],
251
- /// 0x7758-0x777B: Unknown
252
- pub _unknown_7758: [u8; 0x777C - 0x7758],
253
  /// 0x777C: Level width output (written by PCLandscape constructor param 10).
254
  pub level_width_raw: u32,
255
  /// 0x7780: Level height output (written by PCLandscape constructor param 11).
@@ -402,8 +399,8 @@ pub unsafe fn ddgame_init_fields(ddgame: *mut DDGame) {
402
  (*ddgame).init_field_64d8 = 0;
403
  (*ddgame).init_field_72a4 = 0;
404
 
405
- // InitRenderIndices — original sets ESI = ddgame + 0x72D8 before calling
406
- ddgame_init_render_indices(base.add(0x72D8));
407
 
408
  // Zero x and y of each screen coordinate entry (4 entries each)
409
  for entry in &mut (*ddgame).screen_coords {
@@ -419,50 +416,28 @@ pub unsafe fn ddgame_init_fields(ddgame: *mut DDGame) {
419
  /// Pure Rust implementation of DDGame__InitRenderIndices (0x526080).
420
  ///
421
  /// Convention: usercall(ESI=base_ptr), plain RET.
 
422
  ///
423
- /// **Important:** The base pointer is NOT the DDGame pointer!
424
- /// InitFields calls this with ESI = ddgame + 0x72D8.
425
- /// All offsets are relative to whatever ESI points to.
426
- ///
427
- /// Absolute DDGame offsets (base = ddgame+0x72D8):
428
- /// - base+0xC4 = ddgame+0x739C
429
- /// - base+0xD8 = ddgame+0x73B0 (eh_vector_constructor_iterator region)
430
- /// - base+0x378 = ddgame+0x7650 (team_index_map_1)
431
- /// - base+0x3DC = ddgame+0x76B4 (team_index_map_2)
432
- /// - base+0x440 = ddgame+0x7718 (team_index_map_3)
433
  ///
434
  /// # Safety
435
- /// `base` must point to a valid memory region with at least 0x4A4 bytes.
436
- pub unsafe fn ddgame_init_render_indices(base: *mut u8) {
437
- *(base.add(0xC4) as *mut u32) = 0;
438
-
439
- // eh_vector_constructor_iterator equivalent:
440
- // FUN_525F40 is fastcall { *param_1 = 0; }
441
- // 14 entries at stride 0x14 starting from +0xD8
442
- for i in 0..14usize {
443
- *(base.add(0xD8 + i * 0x14) as *mut u32) = 0;
444
- }
445
-
446
- // Identity permutation 1: base+0x378 (= ddgame+0x7650), 16 entries (i16)
447
- for i in 0..16i16 {
448
- *(base.add(0x378 + i as usize * 2) as *mut i16) = i;
449
- }
450
- *(base.add(0x398) as *mut u16) = 0x10;
451
- *(base.add(0x3DA) as *mut u16) = 0;
452
 
453
- // Identity permutation 2: base+0x3DC (= ddgame+0x76B4), 16 entries (i16)
454
- for i in 0..16i16 {
455
- *(base.add(0x3DC + i as usize * 2) as *mut i16) = i;
456
  }
457
- *(base.add(0x3FC) as *mut u16) = 0x10;
458
- *(base.add(0x43E) as *mut u16) = 0;
459
 
460
- // Identity permutation 3: base+0x440 (= ddgame+0x7718), 16 entries (i16)
461
- for i in 0..16i16 {
462
- *(base.add(0x440 + i as usize * 2) as *mut i16) = i;
 
 
 
 
463
  }
464
- *(base.add(0x4A2) as *mut u16) = 0;
465
- *(base.add(0x460) as *mut u16) = 0x10;
466
  }
467
 
468
  // BitGrid__Init moved to crate::task::bit_grid
@@ -692,18 +667,17 @@ pub unsafe fn create_ddgame(
692
  (*ddgame).sound_available = if is_headless { 0 } else { 1 };
693
  (*ddgame).field_7efc = 1;
694
 
695
- // ── 7. DDGameWrapper+0x48C init ──
696
- (*wrapper).ddgame_secondary = core::ptr::null_mut();
697
 
698
- // ── 8. Conditional network object (game_version == -2) ──
699
  if (*game_info).game_version == -2 {
700
- let net_obj = wa_malloc_zeroed(0x2C);
701
- *(net_obj as *mut *mut DDGame) = ddgame;
702
- *net_obj.add(0x28) = (*game_info).net_config_1;
703
- *net_obj.add(0x29) = (*game_info).net_config_2;
704
- (*wrapper).ddgame_secondary = net_obj;
705
  if network_ecx != 0 {
706
- *((network_ecx as *mut u8).add(0x18) as *mut *mut u8) = net_obj;
707
  }
708
  }
709
 
@@ -1535,6 +1509,39 @@ pub struct CoordEntry {
1535
 
1536
  const _: () = assert!(core::mem::size_of::<CoordEntry>() == 0x10);
1537
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1538
  // ============================================================
1539
  // Sound queue entry — 16 entries at DDGame + 0x7F00
1540
  // ============================================================
 
8
  use crate::display::palette::Palette;
9
  use crate::engine::ddgame_wrapper::DDGameWrapper;
10
  use crate::engine::game_info::GameInfo;
11
+ use crate::engine::net_bridge::NetBridge;
12
  use crate::input::keyboard::DDKeyboard;
13
  use crate::rebase::rb;
14
  // Re-export public GfxHandler functions so existing `engine::ddgame::*` imports keep working.
 
223
  pub viewport_width_2: i32,
224
  /// 0x738C: Viewport height max/duplicate (Fixed-point).
225
  pub viewport_height_2: i32,
226
+ /// 0x7390-0x739B: Unknown
227
+ pub _unknown_7390: [u8; 0x739C - 0x7390],
228
+ /// 0x739C: Render state flag (zeroed by InitRenderIndices).
229
+ pub render_state_flag: u32,
230
 
231
  /// 0x73A0: Camera X position (Fixed-point, e.g. 393.0).
232
  pub camera_x: i32,
 
237
  /// 0x73AC: Camera target Y (Fixed-point).
238
  pub camera_target_y: i32,
239
 
240
+ /// 0x73B0: Render entry table (14 entries × 0x14 bytes).
241
+ /// First u32 of each entry zeroed by InitRenderIndices.
242
+ pub render_entries: [RenderEntry; 14],
243
+ /// 0x74C8-0x764F: Unknown
244
+ pub _unknown_74c8: [u8; 0x7650 - 0x74C8],
245
+
246
+ /// 0x7650: Team index permutation maps (3 × 0x64 bytes).
247
+ /// Used for team-to-slot mapping (render order, turn order, display order).
248
+ /// Initialized as identity permutations [0..15] with count=16.
249
+ pub team_index_maps: [TeamIndexMap; 3],
 
 
 
 
 
 
250
  /// 0x777C: Level width output (written by PCLandscape constructor param 10).
251
  pub level_width_raw: u32,
252
  /// 0x7780: Level height output (written by PCLandscape constructor param 11).
 
399
  (*ddgame).init_field_64d8 = 0;
400
  (*ddgame).init_field_72a4 = 0;
401
 
402
+ // InitRenderIndices — original sets ESI = ddgame + 0x72D8, now uses typed DDGame ptr
403
+ ddgame_init_render_indices(ddgame);
404
 
405
  // Zero x and y of each screen coordinate entry (4 entries each)
406
  for entry in &mut (*ddgame).screen_coords {
 
416
  /// Pure Rust implementation of DDGame__InitRenderIndices (0x526080).
417
  ///
418
  /// Convention: usercall(ESI=base_ptr), plain RET.
419
+ /// Initialize render state flag, render entry table, and team index maps.
420
  ///
421
+ /// Original: called with ESI = ddgame + 0x72D8, but now uses typed DDGame fields.
 
 
 
 
 
 
 
 
 
422
  ///
423
  /// # Safety
424
+ /// `ddgame` must point to a valid DDGame allocation.
425
+ pub unsafe fn ddgame_init_render_indices(ddgame: *mut DDGame) {
426
+ (*ddgame).render_state_flag = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
427
 
428
+ // Zero the active flag of each render entry (eh_vector_constructor_iterator).
429
+ for entry in &mut (*ddgame).render_entries {
430
+ entry.active = 0;
431
  }
 
 
432
 
433
+ // Initialize all three team index maps as identity permutations.
434
+ for map in &mut (*ddgame).team_index_maps {
435
+ for i in 0..16i16 {
436
+ map.entries[i as usize] = i;
437
+ }
438
+ map.count = 0x10;
439
+ map.terminator = 0;
440
  }
 
 
441
  }
442
 
443
  // BitGrid__Init moved to crate::task::bit_grid
 
667
  (*ddgame).sound_available = if is_headless { 0 } else { 1 };
668
  (*ddgame).field_7efc = 1;
669
 
670
+ // ── 7. Network bridge (online games only) ──
671
+ (*wrapper).net_bridge = core::ptr::null_mut();
672
 
 
673
  if (*game_info).game_version == -2 {
674
+ let bridge = wa_malloc_zeroed(core::mem::size_of::<NetBridge>() as u32) as *mut NetBridge;
675
+ (*bridge).ddgame = ddgame;
676
+ (*bridge).net_config_1 = (*game_info).net_config_1;
677
+ (*bridge).net_config_2 = (*game_info).net_config_2;
678
+ (*wrapper).net_bridge = bridge;
679
  if network_ecx != 0 {
680
+ *((network_ecx as *mut u8).add(0x18) as *mut *mut NetBridge) = bridge;
681
  }
682
  }
683
 
 
1509
 
1510
  const _: () = assert!(core::mem::size_of::<CoordEntry>() == 0x10);
1511
 
1512
+ /// Team index permutation map (0x64 = 100 bytes).
1513
+ ///
1514
+ /// Used for mapping team indices to rendering/turn slots. Three instances
1515
+ /// live in DDGame at offsets 0x7650, 0x76B4, 0x7718 (stride 0x64).
1516
+ /// Initialized as identity permutations [0,1,2,...,15] with count=16.
1517
+ #[repr(C)]
1518
+ #[derive(Clone, Copy)]
1519
+ pub struct TeamIndexMap {
1520
+ /// 16 team index entries (identity permutation on init).
1521
+ pub entries: [i16; 16],
1522
+ /// Number of active entries (initialized to 16).
1523
+ pub count: u16,
1524
+ /// Gap — unknown purpose, not initialized by constructor.
1525
+ pub _gap: [u8; 64],
1526
+ /// Terminator (initialized to 0).
1527
+ pub terminator: u16,
1528
+ }
1529
+ const _: () = assert!(core::mem::size_of::<TeamIndexMap>() == 0x64);
1530
+
1531
+ /// Render table entry (0x14 = 20 bytes).
1532
+ ///
1533
+ /// 14 entries live at DDGame+0x73B0 (stride 0x14). Only the first u32
1534
+ /// is zeroed during construction; the rest is uninitialized/unknown.
1535
+ #[repr(C)]
1536
+ #[derive(Clone, Copy)]
1537
+ pub struct RenderEntry {
1538
+ /// Active/state flag (zeroed on init).
1539
+ pub active: u32,
1540
+ /// Unknown data.
1541
+ pub _unknown: [u8; 16],
1542
+ }
1543
+ const _: () = assert!(core::mem::size_of::<RenderEntry>() == 0x14);
1544
+
1545
  // ============================================================
1546
  // Sound queue entry — 16 entries at DDGame + 0x7F00
1547
  // ============================================================
crates/openwa-core/src/engine/ddgame_wrapper.rs CHANGED
@@ -1,6 +1,7 @@
1
  use crate::audio::dssound::DSSound;
2
  use crate::display::dd_display::DDDisplay;
3
  use crate::engine::ddgame::DDGame;
 
4
  use crate::render::landscape::PCLandscape;
5
 
6
  /// Speech name table entry size (0x40 = 64 bytes, null-terminated C string).
@@ -27,8 +28,8 @@ pub struct DDGameWrapper {
27
  pub _unknown_004: [u8; 0x484],
28
  /// 0x488: Pointer to DDGame allocation (DWORD index 0x122)
29
  pub ddgame: *mut DDGame,
30
- /// 0x48C: Secondary DDGame struct pointer (0x2C bytes, conditional)
31
- pub ddgame_secondary: *mut u8,
32
  /// 0x490-0x4BF: Unknown
33
  pub _unknown_490: [u8; 0x30],
34
  /// 0x4C0: Primary GfxDir — main sprite archive (Gfx.dir / Gfx0.dir / Gfx1.dir).
 
1
  use crate::audio::dssound::DSSound;
2
  use crate::display::dd_display::DDDisplay;
3
  use crate::engine::ddgame::DDGame;
4
+ use crate::engine::net_bridge::NetBridge;
5
  use crate::render::landscape::PCLandscape;
6
 
7
  /// Speech name table entry size (0x40 = 64 bytes, null-terminated C string).
 
28
  pub _unknown_004: [u8; 0x484],
29
  /// 0x488: Pointer to DDGame allocation (DWORD index 0x122)
30
  pub ddgame: *mut DDGame,
31
+ /// 0x48C: Network bridge object (0x2C bytes). Only set for online games (game_version == -2).
32
+ pub net_bridge: *mut NetBridge,
33
  /// 0x490-0x4BF: Unknown
34
  pub _unknown_490: [u8; 0x30],
35
  /// 0x4C0: Primary GfxDir — main sprite archive (Gfx.dir / Gfx0.dir / Gfx1.dir).
crates/openwa-core/src/engine/mod.rs CHANGED
@@ -5,11 +5,13 @@ pub mod game_info;
5
  pub mod game_session;
6
  pub mod game_state_init;
7
  pub mod game_timer;
 
8
  pub mod net_wrapper;
9
 
10
  pub use ddgame::{
11
- DDGame, FullTeamBlock, SoundQueueEntry, TeamArenaRef, TeamArenaState, TeamBlockHeader,
12
- TeamBlockSlot0, WormEntry, GAME_PHASE_NORMAL_MIN, GAME_PHASE_SUDDEN_DEATH,
 
13
  };
14
  pub use ddgame_wrapper::DDGameWrapper;
15
  pub use game_info::GameInfo;
 
5
  pub mod game_session;
6
  pub mod game_state_init;
7
  pub mod game_timer;
8
+ pub mod net_bridge;
9
  pub mod net_wrapper;
10
 
11
  pub use ddgame::{
12
+ DDGame, FullTeamBlock, RenderEntry, SoundQueueEntry, TeamArenaRef, TeamArenaState,
13
+ TeamBlockHeader, TeamBlockSlot0, TeamIndexMap, WormEntry, GAME_PHASE_NORMAL_MIN,
14
+ GAME_PHASE_SUDDEN_DEATH,
15
  };
16
  pub use ddgame_wrapper::DDGameWrapper;
17
  pub use game_info::GameInfo;
crates/openwa-core/src/engine/net_bridge.rs ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use crate::engine::ddgame::DDGame;
2
+
3
+ /// NetBridge — lightweight adapter between DDGame and the network layer (0x2C bytes).
4
+ ///
5
+ /// Only created for online games (`game_version == -2`). Allocated during
6
+ /// `DDGame__Constructor` and stored at `DDGameWrapper+0x48C`.
7
+ /// Also linked into the network context object (constructor param 7, via ECX)
8
+ /// at offset +0x18.
9
+ ///
10
+ /// Not to be confused with `DDNetGameWrapper` (same size, different purpose) —
11
+ /// that one is constructed by `GameEngine__InitHardware` and stored at
12
+ /// `GameSession+0xC0`.
13
+ #[repr(C)]
14
+ pub struct NetBridge {
15
+ /// 0x00: Back-pointer to the owning DDGame instance.
16
+ pub ddgame: *mut DDGame,
17
+ /// 0x04-0x27: Unknown (zero-filled on construction).
18
+ pub _unknown_04: [u8; 0x24],
19
+ /// 0x28: Network config byte 1 (from GameInfo+0xD944).
20
+ pub net_config_1: u8,
21
+ /// 0x29: Network config byte 2 (from GameInfo+0xD946).
22
+ pub net_config_2: u8,
23
+ /// 0x2A-0x2B: Padding (zero-filled).
24
+ pub _pad_2a: [u8; 2],
25
+ }
26
+
27
+ const _: () = assert!(core::mem::size_of::<NetBridge>() == 0x2C);
crates/openwa-wormkit/src/replacements/ddgame_init.rs CHANGED
@@ -46,7 +46,9 @@ hook::usercall_trampoline!(
46
  );
47
 
48
  extern "cdecl" fn impl_init_render_indices(base: u32) -> u32 {
49
- unsafe { ddgame_init_render_indices(base as *mut u8) }
 
 
50
  base // Original: MOV EAX, ESI; RET
51
  }
52
 
 
46
  );
47
 
48
  extern "cdecl" fn impl_init_render_indices(base: u32) -> u32 {
49
+ // ESI = ddgame + 0x72D8; recover the DDGame pointer
50
+ let ddgame = (base - 0x72D8) as *mut DDGame;
51
+ unsafe { ddgame_init_render_indices(ddgame) }
52
  base // Original: MOV EAX, ESI; RET
53
  }
54