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

refactor: rename GameStats → DDDisplayBase, display_gfx → display

Browse files

GameStats is actually the base class of the display hierarchy —
DisplayGfx__Constructor calls it as its base ctor. Rename to
DDDisplayBase to reflect the inheritance relationship.

Rename GameSession.display_gfx → display since it holds a polymorphic
base class pointer (DisplayGfx in normal, DDDisplayBase in headless).

Update Ghidra: DDDisplayBase__Constructor, DDDisplayBase__Destructor,
DDDisplayBase__Vtable (0x6645F8), DDDisplayBase__HeadlessVtable (0x66A0F8).

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

crates/openwa-core/src/address.rs CHANGED
@@ -45,8 +45,9 @@ pub mod va {
45
  pub const DDKEYBOARD_VTABLE: u32 = 0x0066_AEC8;
46
  /// Palette vtable (0x28-byte palette object)
47
  pub const PALETTE_VTABLE_MAYBE: u32 = 0x0066_A2E4;
48
- /// GameStats vtable — overlaid after GameStats__Constructor in headless mode
49
- pub const GAMESTATS_VTABLE: u32 = 0x0066_A0F8;
 
50
  /// Input controller vtable (0x1800-byte object, set inline before FUN_0058C0D0)
51
  pub const INPUT_CTRL_VTABLE: u32 = 0x0066_B3FC;
52
  /// TaskStateMachine vtable
@@ -276,9 +277,9 @@ pub mod va {
276
  pub const DDISPLAY_INIT: u32 = 0x0056_9D00;
277
  /// Alias kept for callers that use the old name.
278
  pub const CONSTRUCT_DD_DISPLAY: u32 = DDISPLAY_INIT;
279
- /// GameStats__Constructor_Maybe — stdcall(this) → GameStats*.
280
- /// Constructs the 0x3560-byte GameStats stub used in headless mode.
281
- pub const GAMESTATS_CTOR: u32 = 0x0052_2DB0;
282
  /// Streaming audio constructor — stdcall(IDirectSound*, path_config_ptr) → *mut u8.
283
  /// Constructs 0x354-byte streaming audio object (only if GameInfo+0xDAA4 != 0).
284
  pub const STREAMING_AUDIO_CTOR: u32 = 0x0058_BC10;
 
45
  pub const DDKEYBOARD_VTABLE: u32 = 0x0066_AEC8;
46
  /// Palette vtable (0x28-byte palette object)
47
  pub const PALETTE_VTABLE_MAYBE: u32 = 0x0066_A2E4;
48
+ /// DDDisplayBase headless vtable — overlaid after DDDisplayBase__Constructor
49
+ /// in headless mode, filling in stub slots for headless operation.
50
+ pub const DD_DISPLAY_BASE_HEADLESS_VTABLE: u32 = 0x0066_A0F8;
51
  /// Input controller vtable (0x1800-byte object, set inline before FUN_0058C0D0)
52
  pub const INPUT_CTRL_VTABLE: u32 = 0x0066_B3FC;
53
  /// TaskStateMachine vtable
 
277
  pub const DDISPLAY_INIT: u32 = 0x0056_9D00;
278
  /// Alias kept for callers that use the old name.
279
  pub const CONSTRUCT_DD_DISPLAY: u32 = DDISPLAY_INIT;
280
+ /// DDDisplayBase__Constructor — stdcall(this) → DDDisplayBase*.
281
+ /// Constructs the 0x3560-byte base display object (used standalone in headless mode).
282
+ pub const DD_DISPLAY_BASE_CTOR: u32 = 0x0052_2DB0;
283
  /// Streaming audio constructor — stdcall(IDirectSound*, path_config_ptr) → *mut u8.
284
  /// Constructs 0x354-byte streaming audio object (only if GameInfo+0xDAA4 != 0).
285
  pub const STREAMING_AUDIO_CTOR: u32 = 0x0058_BC10;
crates/openwa-core/src/dd_display_base.rs ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /// DDDisplayBase — base class of the display subsystem hierarchy.
2
+ ///
3
+ /// Constructor: DDDisplayBase__Constructor (0x522DB0), stdcall(this) → DDDisplayBase*.
4
+ /// Vtable (primary): 0x6645F8 (set by constructor).
5
+ /// Vtable (headless overlay): 0x66A0F8 (fills in stub slots for headless mode).
6
+ /// Size: 0x3560 bytes.
7
+ ///
8
+ /// Inheritance:
9
+ /// ```text
10
+ /// DDDisplayBase (this) ← vtable 0x6645F8 / 0x66A0F8
11
+ /// └─ DisplayGfx (derived) ← vtable 0x66A218
12
+ /// ```
13
+ ///
14
+ /// In headless mode (`GameInfo.headless_mode != 0`), only the base is constructed
15
+ /// with the headless vtable overlay. In normal mode, `DisplayGfx` (derived) is
16
+ /// constructed instead. The session's `display` field holds a polymorphic pointer
17
+ /// to whichever variant.
18
+ #[repr(C)]
19
+ pub struct DDDisplayBase {
20
+ pub vtable: *mut u8,
21
+ pub _unknown_004: [u8; 0x3560 - 4],
22
+ }
23
+
24
+ const _: () = assert!(core::mem::size_of::<DDDisplayBase>() == 0x3560);
25
+
26
+ impl DDDisplayBase {
27
+ /// Allocate and construct a DDDisplayBase for headless mode.
28
+ ///
29
+ /// Calls WA's native constructor, then overlays the headless vtable.
30
+ ///
31
+ /// # Safety
32
+ /// Must be called from within the WA.exe process.
33
+ pub unsafe fn construct() -> *mut Self {
34
+ use crate::address::va;
35
+ use crate::rebase::rb;
36
+ use crate::wa_alloc::WABox;
37
+ let this = WABox::<Self>::alloc(0x3560, 0x3560).leak();
38
+ let ctor: unsafe extern "stdcall" fn(*mut Self) -> *mut Self =
39
+ core::mem::transmute(rb(va::DD_DISPLAY_BASE_CTOR) as usize);
40
+ ctor(this);
41
+ (*this).vtable = rb(va::DD_DISPLAY_BASE_HEADLESS_VTABLE) as *mut u8;
42
+ this
43
+ }
44
+ }
crates/openwa-core/src/display_gfx.rs CHANGED
@@ -1,10 +1,14 @@
1
- /// DisplayGfx — main display/graphics subsystem container.
2
  ///
3
  /// Constructor: DisplayGfx__Constructor (0x569C10), stdcall(this) → DisplayGfx*.
4
  /// Size: 0x24E28 bytes.
5
  ///
 
 
 
 
6
  /// Created by GameEngine__InitHardware in normal (non-headless) mode.
7
- /// Stored at GameSession+0xAC.
8
  ///
9
  /// OPAQUE: Internal layout not yet mapped.
10
  #[repr(C)]
 
1
+ /// DisplayGfx — full display/graphics subsystem (derived from DDDisplayBase).
2
  ///
3
  /// Constructor: DisplayGfx__Constructor (0x569C10), stdcall(this) → DisplayGfx*.
4
  /// Size: 0x24E28 bytes.
5
  ///
6
+ /// Inheritance: DDDisplayBase (0x3560) → DisplayGfx (0x24E28).
7
+ /// The constructor calls DDDisplayBase__Constructor first, then sets the
8
+ /// DisplayGfx vtable (0x66A218) and initializes display-specific fields.
9
+ ///
10
  /// Created by GameEngine__InitHardware in normal (non-headless) mode.
11
+ /// Stored in the session's `display` field (shared with DDDisplayBase in headless).
12
  ///
13
  /// OPAQUE: Internal layout not yet mapped.
14
  #[repr(C)]
crates/openwa-core/src/game_session.rs CHANGED
@@ -74,9 +74,9 @@ pub struct GameSession {
74
  pub keyboard: *mut DDKeyboard,
75
  /// 0x0A8: `DSSound*` — 0xBE0 bytes, vtable `DSSound_vtable` (0x66AF20)
76
  pub sound: *mut DSSound,
77
- /// 0x0AC: `DisplayGfx*` — 0x24E28 bytes (normal), or `GameStats*` in headless mode.
78
- /// Stays `*mut u8` because it's dual-purpose (DisplayGfx vs GameStats).
79
- pub display_gfx: *mut u8,
80
  /// 0x0B0: `Palette*` — 0x28 bytes, vtable `Palette_vtable_Maybe`
81
  pub palette: *mut Palette,
82
  /// 0x0B4: streaming audio object — 0x354 bytes (`FUN_0058bc10`)
 
74
  pub keyboard: *mut DDKeyboard,
75
  /// 0x0A8: `DSSound*` — 0xBE0 bytes, vtable `DSSound_vtable` (0x66AF20)
76
  pub sound: *mut DSSound,
77
+ /// 0x0AC: Polymorphic display — `DisplayGfx*` (normal) or `DDDisplayBase*` (headless).
78
+ /// Stays `*mut u8` because the concrete type depends on mode.
79
+ pub display: *mut u8,
80
  /// 0x0B0: `Palette*` — 0x28 bytes, vtable `Palette_vtable_Maybe`
81
  pub palette: *mut Palette,
82
  /// 0x0B4: streaming audio object — 0x354 bytes (`FUN_0058bc10`)
crates/openwa-core/src/game_stats.rs DELETED
@@ -1,36 +0,0 @@
1
- /// GameStats — headless mode display stub.
2
- ///
3
- /// Constructor: GameStats__Constructor (0x522DB0), stdcall(this) → GameStats*.
4
- /// Vtable overlay: 0x66A0F8 (applied after constructor returns).
5
- /// Size: 0x3560 bytes.
6
- ///
7
- /// Created by GameEngine__InitHardware in headless mode (GameInfo+0xF914 != 0)
8
- /// instead of DisplayGfx. Stored in the same GameSession+0xAC slot.
9
- ///
10
- /// OPAQUE: Internal layout not yet mapped.
11
- #[repr(C)]
12
- pub struct GameStats {
13
- pub vtable: *mut u8,
14
- pub _unknown_004: [u8; 0x3560 - 4],
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-core/src/lib.rs CHANGED
@@ -37,4 +37,4 @@ pub mod input_ctrl;
37
  pub mod game_timer;
38
  pub mod streaming_audio;
39
  pub mod ddnetgame_wrapper;
40
- pub mod game_stats;
 
37
  pub mod game_timer;
38
  pub mod streaming_audio;
39
  pub mod ddnetgame_wrapper;
40
+ pub mod dd_display_base;
crates/openwa-wormkit/src/replacements/hardware_init.rs CHANGED
@@ -52,7 +52,7 @@ use openwa_core::dssound::DSSound;
52
  use openwa_core::palette::Palette;
53
  use openwa_core::input_ctrl::{InputCtrl, InputCtrlVtable};
54
  use openwa_core::game_timer::GameTimer;
55
- use openwa_core::game_stats::GameStats;
56
  use openwa_core::display_gfx::DisplayGfx;
57
  use openwa_core::streaming_audio::StreamingAudio;
58
  use openwa_core::ddnetgame_wrapper::DDNetGameWrapper;
@@ -335,7 +335,7 @@ unsafe extern "cdecl" fn impl_init_hardware(
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 ────────────────────────────────────────
341
  let flags = gi.display_flags;
@@ -430,7 +430,7 @@ unsafe extern "cdecl" fn impl_init_hardware(
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();
@@ -445,7 +445,7 @@ unsafe extern "cdecl" fn impl_init_hardware(
445
  let wrapper = game_session::construct_ddgame_wrapper(
446
  game_info,
447
  WABox::<DDGameWrapper>::alloc(0x6F10, 0x6EF0).leak(),
448
- (*session).display_gfx as *mut DDDisplay,
449
  (*session).sound,
450
  (*session).keyboard as *mut u8,
451
  (*session).palette,
 
52
  use openwa_core::palette::Palette;
53
  use openwa_core::input_ctrl::{InputCtrl, InputCtrlVtable};
54
  use openwa_core::game_timer::GameTimer;
55
+ use openwa_core::dd_display_base::DDDisplayBase;
56
  use openwa_core::display_gfx::DisplayGfx;
57
  use openwa_core::streaming_audio::StreamingAudio;
58
  use openwa_core::ddnetgame_wrapper::DDNetGameWrapper;
 
335
  if !headless {
336
  // ── DisplayGfx ───────────────────────────────────────────────────────
337
  let display_gfx = DisplayGfx::construct();
338
+ (*session).display = display_gfx as *mut u8;
339
 
340
  // ── DDDisplay::Init retry loop ────────────────────────────────────────
341
  let flags = gi.display_flags;
 
430
  }
431
  } else {
432
  // ── Headless / stats mode ─────────────────────────────────────────────
433
+ (*session).display = DDDisplayBase::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();
 
445
  let wrapper = game_session::construct_ddgame_wrapper(
446
  game_info,
447
  WABox::<DDGameWrapper>::alloc(0x6F10, 0x6EF0).leak(),
448
+ (*session).display as *mut DDDisplay,
449
  (*session).sound,
450
  (*session).keyboard as *mut u8,
451
  (*session).palette,