paavohtl Claude Opus 4.6 (1M context) commited on
Commit
547a858
Β·
1 Parent(s): 4467217

feat: type DDDisplayWrapper and CompatRenderer structs and vtables

Browse files

Add two new typed display subsystem structs:

- DDDisplayWrapper (vtable 0x662EC8, 25 slots): CWormsApp rendering
interface subobject that manages the software framebuffer and
dispatches to renderer backends. All 25 slots mapped.

- CompatRenderer (vtable 0x676A90, 37 slots): DirectDraw/D3D rendering
backend wrapping IDirectDraw2/IDirectDrawSurface. Two-section vtable
for primary display (0-24) and offscreen surface ops (25-36).
Struct fields mapped including DDraw interfaces and surface pointers.

Update DDDisplay docs with corrected dispatch chain:
DDDisplay β†’ DDDisplayWrapper β†’ RendererBackend

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

crates/openwa-core/src/address.rs CHANGED
@@ -71,7 +71,11 @@ pub mod va {
71
  STREAMING_AUDIO_RESET, STREAMING_AUDIO_STOP, STREAMING_AUDIO_TIMER_CALLBACK,
72
  };
73
  pub use crate::display::base::DISPLAY_BASE_VTABLE;
 
 
 
74
  pub use crate::display::dd_display::DD_DISPLAY_VTABLE;
 
75
  pub use crate::display::palette::PALETTE_VTABLE;
76
  pub use crate::frontend::map_view::MAP_VIEW_VTABLE;
77
  pub use crate::input::controller::INPUT_CTRL_VTABLE;
 
71
  STREAMING_AUDIO_RESET, STREAMING_AUDIO_STOP, STREAMING_AUDIO_TIMER_CALLBACK,
72
  };
73
  pub use crate::display::base::DISPLAY_BASE_VTABLE;
74
+ pub use crate::display::compat_renderer::{
75
+ CompatRendererVtable, COMPAT_RENDERER_VTABLE, DDRAW8_RENDERER_VTABLE,
76
+ };
77
  pub use crate::display::dd_display::DD_DISPLAY_VTABLE;
78
+ pub use crate::display::display_wrapper::{DDDisplayWrapperVtable, DD_DISPLAY_WRAPPER_VTABLE};
79
  pub use crate::display::palette::PALETTE_VTABLE;
80
  pub use crate::frontend::map_view::MAP_VIEW_VTABLE;
81
  pub use crate::input::controller::INPUT_CTRL_VTABLE;
crates/openwa-core/src/display/compat_renderer.rs ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use core::ffi::c_void;
2
+
3
+ // CompatRenderer β€” DirectDraw/D3D rendering backend.
4
+ // See CompatRenderer struct and CompatRendererVtable below for full docs.
5
+
6
+ crate::define_addresses! {
7
+ class "DDraw8Renderer" {
8
+ vtable DDRAW8_RENDERER_VTABLE = 0x0067_67F8;
9
+ }
10
+ }
11
+
12
+ #[openwa_core::vtable(size = 37, va = 0x0067_6A90, class = "CompatRenderer")]
13
+ pub struct CompatRendererVtable {
14
+ // =====================================================================
15
+ // Primary display management (slots 0-24)
16
+ // =====================================================================
17
+ /// init / DirectDrawCreate + QueryInterface(IDirectDraw2) (0x59D430)
18
+ #[slot(0)]
19
+ pub init: fn(this: *mut CompatRenderer, p2: u32, p3: u32),
20
+ /// set cooperative level β€” fullscreen exclusive or windowed (0x59D5F0)
21
+ #[slot(1)]
22
+ pub set_cooperative_level: fn(this: *mut CompatRenderer),
23
+ /// reset state β€” calls teardown, clears initialized flag (0x59D480)
24
+ #[slot(2)]
25
+ pub reset_state: fn(this: *mut CompatRenderer),
26
+ /// get display size β€” returns stored width/height from +0x3C/+0x40 (0x59D510)
27
+ #[slot(3)]
28
+ pub get_display_size: fn(this: *mut CompatRenderer, out_w: *mut u32, out_h: *mut u32),
29
+ /// enum display modes β€” IDirectDraw2::EnumDisplayModes (0x59D560)
30
+ #[slot(4)]
31
+ pub enum_display_modes: fn(this: *mut CompatRenderer, p2: u32, p3: u32),
32
+ // Slot 5: stub (CGameTask__vt19)
33
+ /// set display mode + create surfaces β€” the main initialization (0x59D6D0)
34
+ ///
35
+ /// Calls IDirectDraw2::SetDisplayMode, creates primary surface with back buffer
36
+ /// (fullscreen) or with clipper (windowed). ~0x3F2 bytes.
37
+ #[slot(6)]
38
+ pub set_display_mode: fn(this: *mut CompatRenderer, width: i32, height: i32),
39
+ /// teardown β€” release surfaces, restore display mode (0x59DAD0)
40
+ ///
41
+ /// Calls timeEndPeriod, releases primary/back/clipper surfaces,
42
+ /// calls IDirectDraw2::RestoreDisplayMode, LockWindowUpdate(NULL).
43
+ #[slot(7)]
44
+ pub teardown: fn(this: *mut CompatRenderer, p2: i32),
45
+ /// vsync stub β€” calls teardown with 0, effectively a no-op (0x59DCB0)
46
+ #[slot(8)]
47
+ pub vsync_stub: fn(this: *mut CompatRenderer),
48
+ /// set display dimensions β€” thin wrapper to slot 6 (0x59DCE0)
49
+ #[slot(9)]
50
+ pub set_display_dims: fn(this: *mut CompatRenderer, width: i32, height: i32),
51
+ /// restore display dimensions β€” calls slot 6 with stored w/h (0x59DCC0)
52
+ #[slot(10)]
53
+ pub restore_display_dims: fn(this: *mut CompatRenderer),
54
+ /// restore lost surface β€” IDirectDrawSurface::Restore on primary (0x59D4B0)
55
+ #[slot(11)]
56
+ pub restore_surface: fn(this: *mut CompatRenderer) -> u32,
57
+ // Slot 12: stub
58
+ /// flip / present β€” end-of-frame IDirectDrawSurface::Flip (0x59DB70)
59
+ ///
60
+ /// Handles DDERR_SURFACELOST by calling RestoreSurface and retrying.
61
+ #[slot(13)]
62
+ pub flip: fn(this: *mut CompatRenderer),
63
+ /// get hardware caps β€” IDirectDraw2::GetCaps for HAL and HEL (0x59DC80)
64
+ #[slot(14)]
65
+ pub get_caps: fn(this: *mut CompatRenderer, out: *mut u8),
66
+ /// lock primary surface (read, 16bpp flags) (0x59D370)
67
+ ///
68
+ /// Returns pitch and pixel pointer through output params.
69
+ #[slot(15)]
70
+ pub lock_primary_read:
71
+ fn(this: *mut CompatRenderer, out_pitch: *mut u32, out_ptr: *mut *mut u8),
72
+ /// unlock primary surface (0x59D3B0)
73
+ #[slot(16)]
74
+ pub unlock_primary_read: fn(this: *mut CompatRenderer),
75
+ /// lock primary surface (write, 32bpp flags) (0x59D390)
76
+ #[slot(17)]
77
+ pub lock_primary_write:
78
+ fn(this: *mut CompatRenderer, out_pitch: *mut u32, out_ptr: *mut *mut u8),
79
+ /// unlock primary surface β€” same fn as slot 16 (0x59D3B0)
80
+ #[slot(18)]
81
+ pub unlock_primary_write: fn(this: *mut CompatRenderer),
82
+ /// blt filled rect to screen β€” IDirectDrawSurface::Blt with DDBLT_COLORFILL (0x59DDD0)
83
+ #[slot(19)]
84
+ pub blt_fill_rect:
85
+ fn(this: *mut CompatRenderer, x: i32, y: i32, width: i32, height: i32, color: u32),
86
+ /// blt clear both surfaces β€” clears front and back buffer (0x59DD00)
87
+ #[slot(20)]
88
+ pub blt_clear_both: fn(this: *mut CompatRenderer),
89
+ /// blt clear current surface β€” clears active drawing surface (0x59DD70)
90
+ #[slot(21)]
91
+ pub blt_clear_current: fn(this: *mut CompatRenderer),
92
+ // Slots 22-23: stubs
93
+ /// striped blt helper β€” breaks area into horizontal bands (0x5A2080)
94
+ #[slot(24)]
95
+ pub blt_striped: fn(this: *mut CompatRenderer, surface: *mut u8, y_offset: i32),
96
+
97
+ // =====================================================================
98
+ // Offscreen surface sub-object (slots 25-36, via second vtable at +0x04)
99
+ // =====================================================================
100
+ /// sub-object destructor β€” releases offscreen surface (0x59E7B0)
101
+ #[slot(25)]
102
+ pub offscreen_destructor: fn(this: *mut CompatRenderer, flags: u8) -> *mut CompatRenderer,
103
+ /// lock offscreen surface (0x59DF10)
104
+ ///
105
+ /// Handles DDERR_SURFACELOST with restore+retry.
106
+ /// Returns pitch and pixel pointer (via ESI β€” usercall).
107
+ #[slot(26)]
108
+ pub lock_offscreen: fn(this: *mut CompatRenderer, out_pitch: *mut u32, out_ptr: *mut *mut u8),
109
+ /// unlock offscreen surface β€” IDirectDrawSurface::Unlock on +0x10 (0x59E020)
110
+ #[slot(27)]
111
+ pub unlock_offscreen: fn(this: *mut CompatRenderer),
112
+ /// lock offscreen surface variant (0x59E090)
113
+ #[slot(28)]
114
+ pub lock_offscreen_alt:
115
+ fn(this: *mut CompatRenderer, out_pitch: *mut u32, out_ptr: *mut *mut u8),
116
+ /// unlock offscreen surface β€” same fn as slot 27 (0x59E020)
117
+ #[slot(29)]
118
+ pub unlock_offscreen_alt: fn(this: *mut CompatRenderer),
119
+ /// create offscreen plain surface β€” IDirectDraw2::CreateSurface (0x59E1D0)
120
+ ///
121
+ /// Creates DDSCAPS_OFFSCREENPLAIN surface, width 4-byte aligned,
122
+ /// 8bpp or 32bpp. Falls back to system memory if VRAM alloc fails.
123
+ #[slot(30)]
124
+ pub create_offscreen_surface:
125
+ fn(this: *mut CompatRenderer, width: i32, height: i32, bpp: i32) -> u32,
126
+ /// release offscreen surface β€” IDirectDrawSurface::Release on +0x10 (0x59E3B0)
127
+ #[slot(31)]
128
+ pub release_offscreen_surface: fn(this: *mut CompatRenderer),
129
+ /// set color key on offscreen surface (0x59E3F0)
130
+ #[slot(32)]
131
+ pub set_color_key: fn(this: *mut CompatRenderer, color: u32),
132
+ /// get color key from offscreen surface (0x59E480)
133
+ #[slot(33)]
134
+ pub get_color_key: fn(this: *mut CompatRenderer, out_color: *mut u32),
135
+ /// is offscreen surface lost β€” restore if DDERR_SURFACELOST (0x59E780)
136
+ #[slot(34)]
137
+ pub is_lost: fn(this: *mut CompatRenderer) -> u32,
138
+ /// blt offscreen β€” IDirectDrawSurface::Blt with source/dest rects (0x59E4D0)
139
+ #[slot(35)]
140
+ pub blt_offscreen: fn(
141
+ this: *mut CompatRenderer,
142
+ dst_x: i32,
143
+ dst_y: i32,
144
+ dst_w: i32,
145
+ dst_h: i32,
146
+ src: *mut c_void,
147
+ ),
148
+ /// blt fast offscreen β€” IDirectDrawSurface::BltFast with mirror/colorkey (0x59E5F0)
149
+ #[slot(36)]
150
+ pub blt_fast_offscreen:
151
+ fn(this: *mut CompatRenderer, dst_x: i32, dst_y: i32, src: *mut c_void, flags: u32),
152
+ }
153
+
154
+ /// CompatRenderer struct β€” DirectDraw/D3D rendering backend.
155
+ ///
156
+ /// Wraps IDirectDraw2 and IDirectDrawSurface interfaces for screen presentation.
157
+ /// Selected for DIRECTDRAW8, DIRECTDRAW32, DIRECT3D_* display modes.
158
+ #[repr(C)]
159
+ pub struct CompatRenderer {
160
+ /// 0x00: Primary vtable pointer (0x676A90)
161
+ pub vtable: *const CompatRendererVtable,
162
+ /// 0x04: Sub-object vtable pointer (offscreen surface ops, slots 25-36)
163
+ pub offscreen_vtable: *const c_void,
164
+ /// 0x08: Unknown
165
+ pub _unknown_08: u32,
166
+ /// 0x0C: IDirectDraw2 interface (used for offscreen surface creation)
167
+ pub ddraw2_offscreen: *mut c_void,
168
+ /// 0x10: IDirectDrawSurface (current offscreen surface)
169
+ pub offscreen_surface: *mut c_void,
170
+ /// 0x14: Timer period flag
171
+ pub timer_period: u32,
172
+ /// 0x18: Initialized flag (1 = DirectDraw created)
173
+ pub initialized: u32,
174
+ /// 0x1C: Unknown
175
+ pub _unknown_1c: u32,
176
+ /// 0x20: Windowed mode flag (0 = fullscreen exclusive, nonzero = windowed)
177
+ pub windowed: u32,
178
+ /// 0x24: Display mode field
179
+ pub display_mode: u32,
180
+ /// 0x28: IDirectDraw2 main interface
181
+ pub ddraw2_main: *mut c_void,
182
+ /// 0x2C: IDirectDrawSurface β€” primary/front surface
183
+ pub primary_surface: *mut c_void,
184
+ /// 0x30: IDirectDrawSurface β€” back buffer (windowed mode)
185
+ pub back_buffer: *mut c_void,
186
+ /// 0x34: Unknown
187
+ pub _unknown_34: u32,
188
+ /// 0x38: IDirectDrawClipper (windowed mode only)
189
+ pub clipper: *mut c_void,
190
+ /// 0x3C: Stored width
191
+ pub width: u32,
192
+ /// 0x40: Stored height
193
+ pub height: u32,
194
+ }
195
+
196
+ const _: () = assert!(core::mem::size_of::<CompatRenderer>() == 0x44);
197
+
198
+ // Generate calling wrappers: CompatRenderer::flip(), etc.
199
+ bind_CompatRendererVtable!(CompatRenderer, vtable);
crates/openwa-core/src/display/dd_display.rs CHANGED
@@ -7,15 +7,29 @@ use crate::fixed::Fixed;
7
  /// Destructor: 0x569CE0.
8
  ///
9
  /// Actual runtime type is DisplayGfx (0x24E28 bytes), which extends DisplayBase.
10
- /// Manages layers, sprites, fonts, palettes, and delegates to a renderer backend
11
- /// (CompatRenderer for D3D/DDraw, OpenGLCPU for OpenGL).
 
12
  ///
13
- /// Key internal fields (offsets from DisplayBase):
14
- /// - 0x3548/0x354C: display width/height
15
- /// - 0x3550-0x355C: clip rect (x1, y1, x2, y2)
16
- /// - 0x3560/0x3564: camera offset (x, y)
 
 
 
 
 
 
 
 
 
 
 
 
17
  /// - 0x3580-0x3584: bitmap vector (ptr, end)
18
- /// - 0x3D9C: renderer backend pointer
 
19
  ///
20
  /// OPAQUE: Full struct layout not yet mapped (see DisplayGfx for size).
21
  #[repr(C)]
 
7
  /// Destructor: 0x569CE0.
8
  ///
9
  /// Actual runtime type is DisplayGfx (0x24E28 bytes), which extends DisplayBase.
10
+ /// Manages layers, sprites, fonts, palettes, and delegates rendering through
11
+ /// DDDisplayWrapper (g_DDDisplayWrapper at 0x79D6D4), which in turn dispatches
12
+ /// to a renderer backend (CompatRenderer for D3D/DDraw, OpenGLCPU for OpenGL).
13
  ///
14
+ /// ## Dispatch chain
15
+ ///
16
+ /// ```text
17
+ /// DDDisplay β†’ DDDisplayWrapper β†’ RendererBackend
18
+ /// vtable (CWormsApp sub- (CompatRenderer/
19
+ /// 0x66A218 object, vtable OpenGLCPU/DDraw)
20
+ /// 0x662EC8)
21
+ /// ```
22
+ ///
23
+ /// DDDisplay methods apply camera offset and clipping, then call through
24
+ /// `g_DDDisplayWrapper->vtable[N]` for the actual rendering operation.
25
+ ///
26
+ /// Key internal fields (offsets from start of DisplayGfx/DisplayBase):
27
+ /// - 0x3548/0x354C: display width/height (pixels)
28
+ /// - 0x3550-0x355C: clip rect (x1, y1, x2, y2, pixels)
29
+ /// - 0x3560/0x3564: camera offset (x, y, pixels)
30
  /// - 0x3580-0x3584: bitmap vector (ptr, end)
31
+ /// - 0x3D98: render lock flag (cleared by FlushRender)
32
+ /// - 0x3D9C: display context pointer (read during FlushRender)
33
  ///
34
  /// OPAQUE: Full struct layout not yet mapped (see DisplayGfx for size).
35
  #[repr(C)]
crates/openwa-core/src/display/display_wrapper.rs ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // DDDisplayWrapper β€” CWormsApp rendering interface subobject.
2
+ // See DDDisplayWrapper struct and DDDisplayWrapperVtable below for full docs.
3
+
4
+ crate::define_addresses! {
5
+ class "CWormsApp" {
6
+ /// CWormsApp__FlipSurface β€” dispatches to renderer backend vtable[1]
7
+ fn/Thiscall CWORMSAPP_FLIP_SURFACE = 0x005A_2700;
8
+ /// ConstructFrameBuffer β€” allocates software framebuffer, inits renderer
9
+ fn/Thiscall CONSTRUCT_FRAME_BUFFER_THISCALL = 0x005A_2430;
10
+ /// CWormsApp__ReleaseFrameBuffer
11
+ fn/Thiscall CWORMSAPP_RELEASE_FRAME_BUFFER = 0x005A_24A0;
12
+ /// CWormsApp__FillRect β€” lock surface, memset rows, unlock
13
+ fn/Thiscall CWORMSAPP_FILL_RECT = 0x005A_25C0;
14
+ /// CWormsApp__BlitToFrameBuffer β€” optimized blit from surface to framebuffer
15
+ fn/Thiscall CWORMSAPP_BLIT_TO_FRAME_BUFFER = 0x005A_2A40;
16
+ /// CWormsApp__DrawLandscape β€” blit with clipping and transparency
17
+ fn/Thiscall CWORMSAPP_DRAW_LANDSCAPE = 0x005A_2790;
18
+ /// ClearFrameBuffer β€” memset(framebuffer, 0, w*h)
19
+ fn CWORMSAPP_CLEAR_FRAME_BUFFER = 0x005A_23F0;
20
+ }
21
+ }
22
+
23
+ /// DDDisplayWrapper vtable (0x662EC8, 25 slots).
24
+ ///
25
+ /// Many slots are thin thunks that forward to the renderer backend
26
+ /// via `*(this+0x18)->vtable[N]`. Others manage the software framebuffer
27
+ /// or perform blitting directly.
28
+ ///
29
+ /// ## Slot categories
30
+ ///
31
+ /// - **Renderer thunks** (0, 2, 4, 5, 8, 10, 11): Forward to backend
32
+ /// - **Framebuffer management** (3, 6, 7, 9, 15/17, 16/18, 20): Manage G_FRAME_BUFFER_*
33
+ /// - **Blitting** (19, 23, 24): FillRect, DrawLandscape, BlitToFrameBuffer
34
+ /// - **Surface management** (12, 13, 22): Renderer queries and surface allocation
35
+ /// - **Stub** (14): Returns 0
36
+ #[openwa_core::vtable(size = 25, va = 0x0066_2EC8, class = "DDDisplayWrapper")]
37
+ pub struct DDDisplayWrapperVtable {
38
+ /// renderer thunk -> backend vtable[0] (init/create) (0x4E3420, RET 0x8)
39
+ #[slot(0)]
40
+ pub renderer_init: fn(this: *mut DDDisplayWrapper, p2: u32, p3: u32),
41
+ /// flip/present β€” dispatches to renderer backend vtable[1] (0x5A2700)
42
+ #[slot(1)]
43
+ pub flip_surface: fn(this: *mut DDDisplayWrapper),
44
+ /// renderer thunk -> backend vtable[2] (reset state) (0x4E3440)
45
+ #[slot(2)]
46
+ pub renderer_reset: fn(this: *mut DDDisplayWrapper),
47
+ /// get framebuffer dimensions β€” reads G_FRAME_BUFFER_WIDTH/HEIGHT (0x5A2660, RET 0x4)
48
+ #[slot(3)]
49
+ pub get_framebuffer_dims: fn(this: *mut DDDisplayWrapper, out: *mut u32) -> u32,
50
+ /// renderer thunk -> backend vtable[4] (enum display modes) (0x4E3460, RET 0x8)
51
+ #[slot(4)]
52
+ pub renderer_enum_modes: fn(this: *mut DDDisplayWrapper, p2: u32, p3: u32),
53
+ /// renderer thunk -> backend vtable[5] (tail jump) (0x4E3480)
54
+ #[slot(5)]
55
+ pub renderer_slot5: fn(this: *mut DDDisplayWrapper),
56
+ /// construct framebuffer β€” wa_malloc(w*h), init renderer (0x5A2430, RET 0x8)
57
+ #[slot(6)]
58
+ pub construct_frame_buffer: fn(this: *mut DDDisplayWrapper, width: i32, height: i32) -> i32,
59
+ /// release framebuffer β€” calls renderer teardown, frees buffer (0x5A24A0)
60
+ #[slot(7)]
61
+ pub release_frame_buffer: fn(this: *mut DDDisplayWrapper, p2: i32),
62
+ /// renderer thunk -> backend vtable[8] (0x5A24F0)
63
+ #[slot(8)]
64
+ pub renderer_slot8: fn(this: *mut DDDisplayWrapper),
65
+ /// lock framebuffer for pixel access (0x5A2530)
66
+ ///
67
+ /// Returns framebuffer pointer and stride for direct pixel manipulation.
68
+ #[slot(9)]
69
+ pub lock_framebuffer:
70
+ fn(this: *mut DDDisplayWrapper, p2: u32, p3: u32, p4: u32, p5: u32) -> u32,
71
+ /// renderer thunk -> backend vtable[10] (0x5A2510)
72
+ #[slot(10)]
73
+ pub renderer_restore_dims: fn(this: *mut DDDisplayWrapper),
74
+ /// renderer thunk -> backend vtable[11] (restore surface) (0x5A25A0)
75
+ #[slot(11)]
76
+ pub renderer_restore_surface: fn(this: *mut DDDisplayWrapper),
77
+ /// allocate error/result object (0x5A2720, RET 0x4)
78
+ #[slot(12)]
79
+ pub alloc_error_result: fn(this: *mut DDDisplayWrapper, p2: u32) -> u32,
80
+ /// get renderer surface pointer (0x5A26C0)
81
+ ///
82
+ /// Queries `renderer->vtable[12]` with G_FRAME_BUFFER_PTR.
83
+ #[slot(13)]
84
+ pub get_renderer_surface: fn(this: *mut DDDisplayWrapper),
85
+ /// stub β€” returns 0 (CGameTask__vt18, 0x545780)
86
+ #[slot(14)]
87
+ pub stub_ret_zero: fn(this: *mut DDDisplayWrapper) -> u32,
88
+ /// lock surface for reading β€” returns framebuffer ptr + width (0x5A2690, RET 0x8)
89
+ #[slot(15)]
90
+ pub lock_surface_read: fn(this: *mut DDDisplayWrapper, p2: u32, p3: u32),
91
+ /// unlock surface (0x5A2C10, RET 0x4)
92
+ ///
93
+ /// No-op for software framebuffer β€” just returns success code.
94
+ #[slot(16)]
95
+ pub unlock_surface_read: fn(this: *mut DDDisplayWrapper, p2: u32),
96
+ /// lock surface for writing (0x5A2690 β€” same fn as slot 15, RET 0x8)
97
+ #[slot(17)]
98
+ pub lock_surface_write: fn(this: *mut DDDisplayWrapper, p2: u32, p3: u32),
99
+ /// unlock surface after writing (0x5A2C10 β€” same fn as slot 16, RET 0x4)
100
+ #[slot(18)]
101
+ pub unlock_surface_write: fn(this: *mut DDDisplayWrapper, p2: u32),
102
+ /// fill rectangle β€” lock, memset rows, unlock (0x5A25C0)
103
+ ///
104
+ /// Params: x, y, width, height, fill_value.
105
+ #[slot(19)]
106
+ pub fill_rect:
107
+ fn(this: *mut DDDisplayWrapper, x: i32, y: i32, width: i32, height: i32, fill_value: u32),
108
+ /// clear framebuffer β€” memset(ptr, 0, w*h) (0x5A23F0)
109
+ #[slot(20)]
110
+ pub clear_frame_buffer: fn(this: *mut DDDisplayWrapper),
111
+ /// trampoline to slot 20 via vtable dispatch (0x5A2420)
112
+ #[slot(21)]
113
+ pub clear_frame_buffer_indirect: fn(this: *mut DDDisplayWrapper),
114
+ /// allocate surface object β€” wa_malloc(0x14) (0x5A2760)
115
+ #[slot(22)]
116
+ pub alloc_surface: fn(this: *mut DDDisplayWrapper) -> *mut u8,
117
+ /// draw landscape β€” blit with clipping and optional color-key transparency (0x5A2790)
118
+ #[slot(23)]
119
+ pub draw_landscape:
120
+ fn(this: *mut DDDisplayWrapper, surface: *mut u8, y_offset: i32, flags: u32),
121
+ /// blit surface to framebuffer β€” optimized copy with stride alignment (0x5A2A40)
122
+ #[slot(24)]
123
+ pub blit_to_frame_buffer: fn(this: *mut DDDisplayWrapper, surface: *mut u8, y_offset: i32),
124
+ }
125
+
126
+ /// DDDisplayWrapper struct β€” CWormsApp rendering dispatch subobject.
127
+ ///
128
+ /// Stored as a global pointer at 0x79D6D4 (g_DDDisplayWrapper).
129
+ /// The full object layout is part of CWormsApp; only key fields are mapped here.
130
+ #[repr(C)]
131
+ pub struct DDDisplayWrapper {
132
+ /// 0x00: Vtable pointer (0x662EC8)
133
+ pub vtable: *const DDDisplayWrapperVtable,
134
+ /// 0x04-0x17: Unknown (part of CWormsApp layout)
135
+ pub _unknown_04: [u8; 0x14],
136
+ /// 0x18: Pointer to renderer backend (CompatRenderer, OpenGLCPU, or DDraw object)
137
+ pub renderer_backend: *mut u8,
138
+ }
139
+
140
+ const _: () = assert!(core::mem::size_of::<DDDisplayWrapper>() == 0x1C);
141
+
142
+ // Generate calling wrappers: DDDisplayWrapper::flip_surface(), etc.
143
+ bind_DDDisplayWrapperVtable!(DDDisplayWrapper, vtable);
crates/openwa-core/src/display/mod.rs CHANGED
@@ -1,12 +1,16 @@
1
  pub mod base;
 
2
  pub mod dd_display;
 
3
  pub mod gfx;
4
  pub mod gradient;
5
  pub mod opengl;
6
  pub mod palette;
7
 
8
  pub use base::{DisplayBase, DisplayBaseVtable, SpriteBufferCtrl, SpriteCacheWrapper};
 
9
  pub use dd_display::DDDisplay;
 
10
  pub use gfx::DisplayGfx;
11
  pub use opengl::OpenGLState;
12
  pub use palette::{Palette, PaletteVtable};
 
1
  pub mod base;
2
+ pub mod compat_renderer;
3
  pub mod dd_display;
4
+ pub mod display_wrapper;
5
  pub mod gfx;
6
  pub mod gradient;
7
  pub mod opengl;
8
  pub mod palette;
9
 
10
  pub use base::{DisplayBase, DisplayBaseVtable, SpriteBufferCtrl, SpriteCacheWrapper};
11
+ pub use compat_renderer::CompatRenderer;
12
  pub use dd_display::DDDisplay;
13
+ pub use display_wrapper::DDDisplayWrapper;
14
  pub use gfx::DisplayGfx;
15
  pub use opengl::OpenGLState;
16
  pub use palette::{Palette, PaletteVtable};