paavohtl Claude Opus 4.6 (1M context) commited on
Commit
89a37f4
·
1 Parent(s): 8c5449d

refactor: propagate types and trim comments in render hook shims

Browse files

Replace u32 params with Fixed, SpriteOp, and typed pointers across
render_queue.rs and display_vtable.rs. Propagate to core callees
(blit_sprite, bungee_trail, crosshair_line). Trim comments to just
Ghidra name + address.

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

crates/openwa-core/src/render/bungee_trail.rs CHANGED
@@ -21,8 +21,8 @@ use crate::trig::trig_lookup;
21
  ///
22
  /// `task_ptr` must point to a valid `BungeeTrailTask`. ASLR rebase must be
23
  /// initialized.
24
- pub unsafe fn draw_bungee_trail(task_ptr: u32, style: u32, fill: u32) {
25
- let task = &*(task_ptr as *const BungeeTrailTask);
26
 
27
  if task.trail_visible == 0 {
28
  return;
 
21
  ///
22
  /// `task_ptr` must point to a valid `BungeeTrailTask`. ASLR rebase must be
23
  /// initialized.
24
+ pub unsafe fn draw_bungee_trail(task: *const BungeeTrailTask, style: u32, fill: u32) {
25
+ let task = &*task;
26
 
27
  if task.trail_visible == 0 {
28
  return;
crates/openwa-core/src/render/crosshair_line.rs CHANGED
@@ -22,11 +22,10 @@ use crate::trig::trig_lookup;
22
  ///
23
  /// `task_ptr` must point to a valid `WeaponAimTask`. ASLR rebase must be
24
  /// initialized.
25
- pub unsafe fn draw_crosshair_line(task_ptr: u32) {
26
- let task = &*(task_ptr as *const WeaponAimTask);
27
- let gt = &task.game_task;
28
 
29
- if task.aim_active == 0 {
30
  return;
31
  }
32
 
@@ -36,7 +35,7 @@ pub unsafe fn draw_crosshair_line(task_ptr: u32) {
36
  let start_x = gt.pos_x.0;
37
  let start_y = gt.pos_y.0;
38
 
39
- let angle = task.aim_angle;
40
 
41
  // Trig interpolation
42
  let sin_table = rb(va::G_SIN_TABLE) as *const i32;
@@ -45,7 +44,7 @@ pub unsafe fn draw_crosshair_line(task_ptr: u32) {
45
  let cos_interp = trig_lookup(cos_table, angle);
46
 
47
  // Scale = fixed_mul(DDGame.parallax_scale, 0x140000) + task.aim_range_offset
48
- let scale = Fixed(ddgame.parallax_scale).mul_raw(Fixed(0x14_0000)).0 + task.aim_range_offset;
49
 
50
  // Endpoint = start + direction * scale
51
  let mut endpoint_x = Fixed(sin_interp.0)
 
22
  ///
23
  /// `task_ptr` must point to a valid `WeaponAimTask`. ASLR rebase must be
24
  /// initialized.
25
+ pub unsafe fn draw_crosshair_line(task: *const WeaponAimTask) {
26
+ let gt = &(*task).game_task;
 
27
 
28
+ if (*task).aim_active == 0 {
29
  return;
30
  }
31
 
 
35
  let start_x = gt.pos_x.0;
36
  let start_y = gt.pos_y.0;
37
 
38
+ let angle = (*task).aim_angle;
39
 
40
  // Trig interpolation
41
  let sin_table = rb(va::G_SIN_TABLE) as *const i32;
 
44
  let cos_interp = trig_lookup(cos_table, angle);
45
 
46
  // Scale = fixed_mul(DDGame.parallax_scale, 0x140000) + task.aim_range_offset
47
+ let scale = Fixed(ddgame.parallax_scale).mul_raw(Fixed(0x14_0000)).0 + (*task).aim_range_offset;
48
 
49
  // Endpoint = start + direction * scale
50
  let mut endpoint_x = Fixed(sin_interp.0)
crates/openwa-core/src/render/display/blit_sprite.rs CHANGED
@@ -23,7 +23,7 @@ pub unsafe fn blit_sprite(
23
  this: *mut DisplayGfx,
24
  x: Fixed,
25
  y: Fixed,
26
- sprite_flags: u32,
27
  palette: u32,
28
  ) {
29
  let gfx = this;
@@ -32,7 +32,6 @@ pub unsafe fn blit_sprite(
32
  // ---------------------------------------------------------------
33
  // Extract sprite index and flags
34
  // ---------------------------------------------------------------
35
- let sprite = SpriteOp(sprite_flags);
36
  let flags = sprite.flags();
37
  let sprite_id = sprite.index() as u32;
38
 
 
23
  this: *mut DisplayGfx,
24
  x: Fixed,
25
  y: Fixed,
26
+ sprite: SpriteOp,
27
  palette: u32,
28
  ) {
29
  let gfx = this;
 
32
  // ---------------------------------------------------------------
33
  // Extract sprite index and flags
34
  // ---------------------------------------------------------------
 
35
  let flags = sprite.flags();
36
  let sprite_id = sprite.index() as u32;
37
 
crates/openwa-dll/src/replacements/render/display_vtable.rs CHANGED
@@ -1,11 +1,5 @@
1
- //! DisplayGfx vtable patches — DisplayBase pure-call stubs, headless destructor,
2
- //! and ported DisplayGfx methods.
3
- //!
4
- //! Patches DisplayBase vtables in WA.exe's .rdata:
5
- //! - Primary vtable (0x6645F8): replaces _purecall slots with safe no-op stubs
6
- //! - Headless vtable (0x66A0F8): replaces destructor with Rust version that
7
- //! correctly frees our Rust-allocated sprite cache sub-objects
8
- //! - DisplayGfx vtable (0x66A218): replaces ported methods with Rust
9
 
10
  use core::ffi::c_char;
11
 
@@ -18,6 +12,7 @@ use openwa_core::render::display::vtable::{self as display_vtable_impl, DisplayG
18
  use openwa_core::render::display::DisplayBase;
19
  use openwa_core::render::display::DisplayGfx;
20
  use openwa_core::render::sprite::gfx_dir::GfxDir;
 
21
  use openwa_core::vtable::patch_vtable;
22
  use openwa_core::vtable_replace;
23
  use openwa_core::wa_alloc::wa_free;
@@ -33,8 +28,6 @@ const VTABLE_SLOTS: usize = 32;
33
 
34
  unsafe extern "thiscall" fn noop_thiscall(_this: *mut u8) {}
35
 
36
- /// Rust destructor for headless DisplayBase. Frees the sprite cache chain
37
- /// (SpriteCache -> FrameCache -> buffer) that was allocated by new_headless().
38
  unsafe extern "thiscall" fn headless_destructor(
39
  this: *mut DisplayBase,
40
  flags: u8,
@@ -57,22 +50,18 @@ unsafe extern "thiscall" fn headless_destructor(
57
  this
58
  }
59
 
60
- // No saved originals needed — all paths are fully ported or use direct bridges.
61
-
62
- /// Thiscall stub for DisplayGfx::BlitSprite (slot 19, 0x56B080).
63
- /// Implementation in `openwa_core::render::display::blit_sprite`.
64
  unsafe extern "thiscall" fn blit_sprite(
65
  this: *mut DisplayGfx,
66
  x: Fixed,
67
  y: Fixed,
68
- sprite_flags: u32,
69
  palette: u32,
70
  ) {
71
- openwa_core::render::display::blit_sprite::blit_sprite(this, x, y, sprite_flags, palette);
72
  }
73
 
74
- /// Thiscall stub for DisplayGfx::DrawScaledSprite (slot 20).
75
- /// Implementation in `openwa_core::render::display::blit_sprite`.
76
  unsafe extern "thiscall" fn draw_scaled_sprite(
77
  this: *mut DisplayGfx,
78
  x: Fixed,
@@ -89,21 +78,7 @@ unsafe extern "thiscall" fn draw_scaled_sprite(
89
  );
90
  }
91
 
92
- // =========================================================================
93
- // Font vtable method wrappers
94
- // =========================================================================
95
- //
96
- // Font slot wrappers (slots 7/8/9/10/34/35/36) all live in openwa-core's
97
- // `display::vtable` module. The wrappers in this file (slots 31/34/35/37
98
- // for sprite/font loading) are thin forwarders only because they need to
99
- // capture a bridge function pointer (`wa_load_sprite_from_vfs` etc.) at
100
- // install time.
101
-
102
- // =========================================================================
103
- // Sprite loading vtable method wrappers
104
- // =========================================================================
105
-
106
- /// Thiscall wrapper for DisplayGfx::LoadSprite (vtable slot 31, 0x523400).
107
  unsafe extern "thiscall" fn load_sprite(
108
  this: *mut DisplayGfx,
109
  layer: u32,
@@ -123,13 +98,7 @@ unsafe extern "thiscall" fn load_sprite(
123
  )
124
  }
125
 
126
- /// Bridge to LoadSpriteFromVfs (0x4FAAF0).
127
- /// Usercall: ECX=gfx, EAX=name, stack(sprite, layer_ctx), RET 0x8.
128
- ///
129
- /// Verified from caller at 0x523489:
130
- /// ECX ← layer_contexts[layer] (gfx/VFS context)... wait, re-checked:
131
- /// ECX ← gfx param, EAX ← name param.
132
- /// Stack: sprite (EDI from ConstructSprite), layer_ctx.
133
  #[unsafe(naked)]
134
  unsafe extern "cdecl" fn wa_load_sprite_from_vfs(
135
  _sprite: *mut openwa_core::render::sprite::Sprite,
@@ -151,11 +120,7 @@ unsafe extern "cdecl" fn wa_load_sprite_from_vfs(
151
 
152
  static mut LOAD_SPRITE_FROM_VFS_ADDR: u32 = 0;
153
 
154
- // GetSpriteFrameForBlit (slot 33) is NOT ported — see the docstring on
155
- // `DisplayVtable::get_sprite_frame_for_blit` in openwa-core for the full
156
- // rationale. Our `blit_sprite` (slot 19) above calls it via the bound
157
- // vtable wrapper on every sprite render.
158
-
159
  unsafe extern "thiscall" fn load_sprite_by_layer(
160
  this: *mut DisplayGfx,
161
  layer: u32,
@@ -166,6 +131,7 @@ unsafe extern "thiscall" fn load_sprite_by_layer(
166
  display_vtable_impl::load_sprite_by_layer(this, layer, id, gfx_dir, name)
167
  }
168
 
 
169
  unsafe extern "thiscall" fn load_font(
170
  this: *mut DisplayGfx,
171
  mode: u32,
@@ -176,6 +142,7 @@ unsafe extern "thiscall" fn load_font(
176
  display_vtable_impl::load_font(this, mode, font_id, gfx_dir, filename)
177
  }
178
 
 
179
  unsafe extern "thiscall" fn load_font_extension(
180
  this: *mut DisplayGfx,
181
  font_id: i32,
@@ -187,10 +154,6 @@ unsafe extern "thiscall" fn load_font_extension(
187
  display_vtable_impl::load_font_extension(this, font_id, path, char_map, palette_value, flag)
188
  }
189
 
190
- // ---------------------------------------------------------------------------
191
- // Installation
192
- // ---------------------------------------------------------------------------
193
-
194
  pub fn install() -> Result<(), String> {
195
  let _ = log_line("[Display] Patching DisplayBase vtables");
196
 
@@ -198,7 +161,6 @@ pub fn install() -> Result<(), String> {
198
  let purecall_addr = rb(PURECALL);
199
  let noop_addr = noop_thiscall as *const () as u32;
200
 
201
- // Patch primary vtable (0x6645F8): replace _purecall with no-ops.
202
  let primary = rb(va::DISPLAY_BASE_VTABLE) as *mut u32;
203
  patch_vtable(primary, VTABLE_SLOTS, |vt| {
204
  let mut patched = 0u32;
@@ -214,81 +176,57 @@ pub fn install() -> Result<(), String> {
214
  ));
215
  })?;
216
 
217
- // Patch headless vtable (0x66A0F8): replace destructor (slot 0)
218
- // with our Rust version that frees the Rust-allocated sprite cache.
219
  let headless = rb(va::DISPLAY_BASE_HEADLESS_VTABLE) as *mut u32;
220
  patch_vtable(headless, VTABLE_SLOTS, |vt| {
221
  *vt = headless_destructor as *const () as u32;
222
  let _ = log_line("[Display] Headless: patched slot 0 (destructor) -> Rust");
223
  })?;
224
 
225
- // Initialize bridge address statics for sprite loading
226
  LOAD_SPRITE_FROM_VFS_ADDR = rb(va::LOAD_SPRITE_FROM_VFS);
227
 
228
- // Patch DisplayGfx vtable (0x66A218): replace ported methods with Rust.
229
  vtable_replace!(DisplayGfxVtable, va::DISPLAY_GFX_VTABLE, {
230
- destructor => display_destructor::display_gfx_destructor,
231
- get_dimensions => display_vtable_impl::get_dimensions,
232
- set_layer_color => display_vtable_impl::set_layer_color,
233
- set_active_layer => display_vtable_impl::set_active_layer,
234
- get_sprite_info => display_vtable_impl::get_sprite_info,
235
- draw_text_on_bitmap => display_vtable_impl::draw_text_on_bitmap,
236
- draw_tiled_bitmap => display_vtable_impl::draw_tiled_bitmap,
237
- get_font_info => display_vtable_impl::get_font_info,
238
- get_font_metric => display_vtable_impl::get_font_metric,
239
- set_font_param => display_vtable_impl::set_font_param,
240
- draw_polyline => display_vtable_impl::draw_polyline,
241
- draw_line => display_vtable_impl::draw_line,
242
- draw_line_clipped => display_vtable_impl::draw_line_clipped,
243
- draw_pixel_strip => display_vtable_impl::draw_pixel_strip,
244
- draw_crosshair => display_vtable_impl::draw_crosshair,
245
- draw_outlined_pixel => display_vtable_impl::draw_outlined_pixel,
246
- fill_rect => display_vtable_impl::fill_rect,
247
- draw_via_callback => display_vtable_impl::draw_via_callback,
248
- draw_tiled_terrain => display_vtable_impl::draw_tiled_terrain,
249
- flush_render => display_vtable_impl::flush_render,
250
- set_camera_offset => display_vtable_impl::set_camera_offset,
251
- set_clip_rect => display_vtable_impl::set_clip_rect,
252
- is_sprite_loaded => display_vtable_impl::is_sprite_loaded,
253
- load_sprite => load_sprite,
254
- draw_scaled_sprite => draw_scaled_sprite,
255
- set_layer_visibility => display_vtable_impl::set_layer_visibility,
256
- update_palette => display_vtable_impl::update_palette,
257
- set_font_palette => display_vtable_impl::set_font_palette,
258
- slot 19 => blit_sprite,
259
- get_sprite_frame_for_blit => display_vtable_impl::get_sprite_frame_for_blit,
260
- load_sprite_by_layer => load_sprite_by_layer,
261
- load_font => load_font,
262
- load_font_extension => load_font_extension,
263
  })?;
264
  let _ = log_line("[Display] DisplayGfx: patched 32 methods -> Rust");
265
 
266
- // DisplayGfx::LoadSpriteEx (vtable slot 30) has zero callers in both
267
- // WA.exe (no instructions reach vtable[+0x78] on a DisplayGfx) and our
268
- // own Rust code (no `bind_DisplayVtable!` invocation, no direct call).
269
- // Trap it so we'll be alerted the moment any future caller appears.
270
  hook::install_trap!("DisplayGfx__LoadSpriteEx", va::DISPLAY_GFX_LOAD_SPRITE_EX);
271
 
272
- // ── Slot 33 leaf functions: trap the WA-side originals ────────
273
- //
274
- // After `vtable_replace!` swapped slot 33 to our Rust impl, the
275
- // original `DisplayGfx::GetSpriteFrameForBlit` (0x5237C0) is no
276
- // longer reached, which means its leaves have no remaining
277
- // callers in shipping WA either:
278
- //
279
- // - `Sprite__GetFrameForBlit` (0x4FAD30): only caller was the
280
- // original slot 33 dispatcher.
281
- // - `SpriteBank__GetFrameForBlit` (0x4F9710): same. Also bank
282
- // objects are themselves unreachable (LoadSpriteEx is trapped).
283
- // - `FrameCache__Allocate` (0x4FA950): only callers were the
284
- // two GetFrameForBlit helpers above.
285
- // - `DisplayBitGrid::SetExternalBuffer` (0x4F6470): only caller
286
- // was `SpriteBank__GetFrameForBlit`.
287
- //
288
- // `Sprite_LZSS_Decode` (0x5B29E0) is NOT trapped — it has
289
- // independent callers (`IMG_Decode`, `FUN_0048EE32`) reachable
290
- // from `DDGame__Constructor`/`SoundEmitter`/`LoadHudAndWeaponSprites`,
291
- // all still bridged. Trapping it would break those.
292
  hook::install_trap!("Sprite__GetFrameForBlit", va::SPRITE_GET_FRAME_FOR_BLIT);
293
  hook::install_trap!(
294
  "SpriteBank__GetFrameForBlit",
@@ -300,26 +238,10 @@ pub fn install() -> Result<(), String> {
300
  va::DISPLAY_BIT_GRID_SET_EXTERNAL_BUFFER
301
  );
302
 
303
- // ── Slot 0 destructor leaves: trap the WA-side originals ──────
304
- //
305
- // After `vtable_replace!` swapped slot 0 to our Rust impl, the
306
- // original `DisplayGfx::DestructorImpl` (0x56A010) is no longer
307
- // reached, so its only-callee helpers lose their last live
308
- // caller too.
309
- //
310
- // `TileBitmapSet::Destructor` (0x569BC0) has a *second* xref to
311
- // `FUN_0056C2E0` (a tile-bitmap-set realloc helper called only
312
- // from `CBitmap__Constructor_Maybe` at 0x56C060). That second
313
- // chain is **structurally dead** in shipping WA: 0x56C060 sits
314
- // in the DisplayGfx vtable .rdata block at slot index 38 (offset
315
- // 0x98 past the vtable base 0x66A218), but no instruction in
316
- // WA.exe loads from offset 0x98 of any pointer — verified by
317
- // byte-pattern search for `FF 90 98 00 00 00` (CALL [reg+0x98])
318
- // and `8B 4? 98` (MOV reg,[reg+0x98]) returning no DisplayGfx
319
- // hits. The compiler emitted slots 38/39 of DisplayGfx into
320
- // .rdata but the C++ source has no live caller for them — they
321
- // are vestigial vtable entries from a removed feature. So
322
- // trapping `TileBitmapSet::Destructor` is safe.
323
  hook::install_trap!(
324
  "DisplayGfx__DestructorImpl",
325
  va::DISPLAY_GFX_DESTRUCTOR_IMPL
 
1
+ //! DisplayGfx vtable patches — DisplayBase purecall stubs, headless destructor,
2
+ //! and ported DisplayGfx method wiring.
 
 
 
 
 
 
3
 
4
  use core::ffi::c_char;
5
 
 
12
  use openwa_core::render::display::DisplayBase;
13
  use openwa_core::render::display::DisplayGfx;
14
  use openwa_core::render::sprite::gfx_dir::GfxDir;
15
+ use openwa_core::render::SpriteOp;
16
  use openwa_core::vtable::patch_vtable;
17
  use openwa_core::vtable_replace;
18
  use openwa_core::wa_alloc::wa_free;
 
28
 
29
  unsafe extern "thiscall" fn noop_thiscall(_this: *mut u8) {}
30
 
 
 
31
  unsafe extern "thiscall" fn headless_destructor(
32
  this: *mut DisplayBase,
33
  flags: u8,
 
50
  this
51
  }
52
 
53
+ // BlitSprite (slot 19, 0x56B080)
 
 
 
54
  unsafe extern "thiscall" fn blit_sprite(
55
  this: *mut DisplayGfx,
56
  x: Fixed,
57
  y: Fixed,
58
+ sprite: SpriteOp,
59
  palette: u32,
60
  ) {
61
+ openwa_core::render::display::blit_sprite::blit_sprite(this, x, y, sprite, palette);
62
  }
63
 
64
+ // DrawScaledSprite (slot 20, 0x56B5F0)
 
65
  unsafe extern "thiscall" fn draw_scaled_sprite(
66
  this: *mut DisplayGfx,
67
  x: Fixed,
 
78
  );
79
  }
80
 
81
+ // LoadSprite (slot 31, 0x523400)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  unsafe extern "thiscall" fn load_sprite(
83
  this: *mut DisplayGfx,
84
  layer: u32,
 
98
  )
99
  }
100
 
101
+ // LoadSpriteFromVfs (0x4FAAF0) — naked bridge
 
 
 
 
 
 
102
  #[unsafe(naked)]
103
  unsafe extern "cdecl" fn wa_load_sprite_from_vfs(
104
  _sprite: *mut openwa_core::render::sprite::Sprite,
 
120
 
121
  static mut LOAD_SPRITE_FROM_VFS_ADDR: u32 = 0;
122
 
123
+ // LoadSpriteByLayer (slot 34)
 
 
 
 
124
  unsafe extern "thiscall" fn load_sprite_by_layer(
125
  this: *mut DisplayGfx,
126
  layer: u32,
 
131
  display_vtable_impl::load_sprite_by_layer(this, layer, id, gfx_dir, name)
132
  }
133
 
134
+ // LoadFont (slot 35)
135
  unsafe extern "thiscall" fn load_font(
136
  this: *mut DisplayGfx,
137
  mode: u32,
 
142
  display_vtable_impl::load_font(this, mode, font_id, gfx_dir, filename)
143
  }
144
 
145
+ // LoadFontExtension (slot 36)
146
  unsafe extern "thiscall" fn load_font_extension(
147
  this: *mut DisplayGfx,
148
  font_id: i32,
 
154
  display_vtable_impl::load_font_extension(this, font_id, path, char_map, palette_value, flag)
155
  }
156
 
 
 
 
 
157
  pub fn install() -> Result<(), String> {
158
  let _ = log_line("[Display] Patching DisplayBase vtables");
159
 
 
161
  let purecall_addr = rb(PURECALL);
162
  let noop_addr = noop_thiscall as *const () as u32;
163
 
 
164
  let primary = rb(va::DISPLAY_BASE_VTABLE) as *mut u32;
165
  patch_vtable(primary, VTABLE_SLOTS, |vt| {
166
  let mut patched = 0u32;
 
176
  ));
177
  })?;
178
 
 
 
179
  let headless = rb(va::DISPLAY_BASE_HEADLESS_VTABLE) as *mut u32;
180
  patch_vtable(headless, VTABLE_SLOTS, |vt| {
181
  *vt = headless_destructor as *const () as u32;
182
  let _ = log_line("[Display] Headless: patched slot 0 (destructor) -> Rust");
183
  })?;
184
 
 
185
  LOAD_SPRITE_FROM_VFS_ADDR = rb(va::LOAD_SPRITE_FROM_VFS);
186
 
 
187
  vtable_replace!(DisplayGfxVtable, va::DISPLAY_GFX_VTABLE, {
188
+ destructor => display_destructor::display_gfx_destructor,
189
+ get_dimensions => display_vtable_impl::get_dimensions,
190
+ set_layer_color => display_vtable_impl::set_layer_color,
191
+ set_active_layer => display_vtable_impl::set_active_layer,
192
+ get_sprite_info => display_vtable_impl::get_sprite_info,
193
+ draw_text_on_bitmap => display_vtable_impl::draw_text_on_bitmap,
194
+ draw_tiled_bitmap => display_vtable_impl::draw_tiled_bitmap,
195
+ get_font_info => display_vtable_impl::get_font_info,
196
+ get_font_metric => display_vtable_impl::get_font_metric,
197
+ set_font_param => display_vtable_impl::set_font_param,
198
+ draw_polyline => display_vtable_impl::draw_polyline,
199
+ draw_line => display_vtable_impl::draw_line,
200
+ draw_line_clipped => display_vtable_impl::draw_line_clipped,
201
+ draw_pixel_strip => display_vtable_impl::draw_pixel_strip,
202
+ draw_crosshair => display_vtable_impl::draw_crosshair,
203
+ draw_outlined_pixel => display_vtable_impl::draw_outlined_pixel,
204
+ fill_rect => display_vtable_impl::fill_rect,
205
+ draw_via_callback => display_vtable_impl::draw_via_callback,
206
+ draw_tiled_terrain => display_vtable_impl::draw_tiled_terrain,
207
+ flush_render => display_vtable_impl::flush_render,
208
+ set_camera_offset => display_vtable_impl::set_camera_offset,
209
+ set_clip_rect => display_vtable_impl::set_clip_rect,
210
+ is_sprite_loaded => display_vtable_impl::is_sprite_loaded,
211
+ load_sprite => load_sprite,
212
+ draw_scaled_sprite => draw_scaled_sprite,
213
+ set_layer_visibility => display_vtable_impl::set_layer_visibility,
214
+ update_palette => display_vtable_impl::update_palette,
215
+ set_font_palette => display_vtable_impl::set_font_palette,
216
+ blit_sprite => blit_sprite,
217
+ get_sprite_frame_for_blit => display_vtable_impl::get_sprite_frame_for_blit,
218
+ load_sprite_by_layer => load_sprite_by_layer,
219
+ load_font => load_font,
220
+ load_font_extension => load_font_extension,
221
  })?;
222
  let _ = log_line("[Display] DisplayGfx: patched 32 methods -> Rust");
223
 
224
+ // Slot 30: zero callers in WA.exe or Rust.
 
 
 
225
  hook::install_trap!("DisplayGfx__LoadSpriteEx", va::DISPLAY_GFX_LOAD_SPRITE_EX);
226
 
227
+ // Slot 33 leaf functions — only callers were the original WA-side
228
+ // GetSpriteFrameForBlit (now replaced). Note: Sprite_LZSS_Decode
229
+ // (0x5B29E0) is NOT trapped — it has independent live callers.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  hook::install_trap!("Sprite__GetFrameForBlit", va::SPRITE_GET_FRAME_FOR_BLIT);
231
  hook::install_trap!(
232
  "SpriteBank__GetFrameForBlit",
 
238
  va::DISPLAY_BIT_GRID_SET_EXTERNAL_BUFFER
239
  );
240
 
241
+ // Slot 0 destructor leaf functions — only caller was the original
242
+ // WA-side DestructorImpl (now replaced). TileBitmapSet::Destructor's
243
+ // second xref (via slot 38) is structurally dead — no instruction
244
+ // in WA.exe dispatches through vtable offset 0x98.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  hook::install_trap!(
246
  "DisplayGfx__DestructorImpl",
247
  va::DISPLAY_GFX_DESTRUCTOR_IMPL
crates/openwa-dll/src/replacements/render/render_queue.rs CHANGED
@@ -1,382 +1,322 @@
1
- //! RenderQueue enqueue hooks — full Rust replacements for command enqueueing,
2
- //! plus the per-frame RenderDrawingQueue dispatcher bridge.
3
 
4
  use openwa_core::address::va;
 
5
  use openwa_core::fixed::Fixed;
 
6
  use openwa_core::render::display::DisplayGfx;
7
  use openwa_core::render::queue::*;
8
  use openwa_core::render::queue_dispatch::{render_drawing_queue, ClipContext};
9
  use openwa_core::render::SpriteOp;
 
10
 
11
  use crate::hook::{self, usercall_trampoline};
12
 
13
- // ---------------------------------------------------------------------------
14
- // EnqueueTiledBitmap (0x541D60) — type 0xD, EAX=this, 3 stack, RET 0xC
15
- //
16
- // Mis-labelled `RQ_DrawPixel` in earlier reverse-engineering passes. It does
17
- // NOT enqueue a single-pixel draw — it enqueues a tile-cached bitmap draw
18
- // dispatched by `RenderDrawingQueue` case 0xD into vtable slot 11.
19
- // ---------------------------------------------------------------------------
20
 
21
  usercall_trampoline!(fn trampoline_enqueue_tiled_bitmap; impl_fn = enqueue_tiled_bitmap_impl;
22
  reg = eax; stack_params = 3; ret_bytes = "0xC");
23
 
24
  unsafe extern "cdecl" fn enqueue_tiled_bitmap_impl(
25
- this: u32,
26
- y_fixed16: u32,
27
- source_descriptor: u32,
28
  flags: u32,
29
  ) {
30
- let q = &mut *(this as *mut RenderQueue);
31
-
32
- if let Some(entry) = q.alloc::<DrawTiledBitmapCmd>() {
33
  *entry = DrawTiledBitmapCmd {
34
  command_type: command_type::DRAW_TILED_BITMAP,
35
  layer: 0x1B_0000,
36
  x: Fixed(0xFF00_0000u32 as i32),
37
- y: Fixed(y_fixed16 as i32),
38
- source: source_descriptor as *const _,
39
  flags: flags as u8,
40
  _pad: [0; 3],
41
  };
42
  }
43
  }
44
 
45
- // ---------------------------------------------------------------------------
46
- // DrawLineStrip (0x541DD0) — type 8, EAX=this, EDI=count, 2 stack, RET 0x8
47
- // Allocation: count * 0xC + 0x1C (variable size)
48
- // ---------------------------------------------------------------------------
49
 
50
  usercall_trampoline!(fn trampoline_draw_line_strip; impl_fn = draw_line_strip_impl;
51
  regs = [eax, edi]; stack_params = 2; ret_bytes = "0x8");
52
 
53
  unsafe extern "cdecl" fn draw_line_strip_impl(
54
- this: u32,
55
  count: u32,
56
- vertex_ptr: u32,
57
- param_1: u32,
58
  ) {
59
- let q = &mut *(this as *mut RenderQueue);
60
  let total_size = count as usize * 0xC + 0x1C;
61
 
62
- if let Some(ptr) = q.alloc_raw(total_size) {
63
  let header = &mut *(ptr as *mut DrawLineStripHeader);
64
  *header = DrawLineStripHeader {
65
  command_type: command_type::DRAW_LINE_STRIP,
66
  layer: 0xE_0000,
67
  count,
68
- color: param_1,
69
  };
70
  core::ptr::copy_nonoverlapping(
71
- vertex_ptr as *const u8,
72
  ptr.add(core::mem::size_of::<DrawLineStripHeader>()),
73
  count as usize * 0xC,
74
  );
75
  }
76
  }
77
 
78
- // ---------------------------------------------------------------------------
79
- // DrawPolygon (0x541E50) — type 9, ECX=this, ESI=count, 3 stack, RET 0xC
80
- // Allocation: count * 0xC + 0x20 (variable size)
81
- // ---------------------------------------------------------------------------
82
 
83
  usercall_trampoline!(fn trampoline_draw_polygon; impl_fn = draw_polygon_impl;
84
  regs = [ecx, esi]; stack_params = 3; ret_bytes = "0xC");
85
 
86
  unsafe extern "cdecl" fn draw_polygon_impl(
87
- this: u32,
88
  count: u32,
89
- vertex_ptr: u32,
90
- param_1: u32,
91
- param_2: u32,
92
  ) {
93
- let q = &mut *(this as *mut RenderQueue);
94
  let total_size = count as usize * 0xC + 0x20;
95
 
96
- if let Some(ptr) = q.alloc_raw(total_size) {
97
  let header = &mut *(ptr as *mut DrawPolygonHeader);
98
  *header = DrawPolygonHeader {
99
  command_type: command_type::DRAW_POLYGON,
100
  layer: 0xE_0000,
101
  count,
102
- color1: param_1,
103
- color2: param_2,
104
  };
105
  core::ptr::copy_nonoverlapping(
106
- vertex_ptr as *const u8,
107
  ptr.add(core::mem::size_of::<DrawPolygonHeader>()),
108
  count as usize * 0xC,
109
  );
110
  }
111
  }
112
 
113
- // ---------------------------------------------------------------------------
114
- // DrawCrosshair (0x541ED0) — type 0xB, ECX=this, 5 stack, RET 0x14
115
- // Enqueues a crosshair draw command. Dispatched by RenderDrawingQueue
116
- // case 0xB → DisplayGfx::draw_crosshair (vtable slot 16).
117
- // ---------------------------------------------------------------------------
118
 
119
  usercall_trampoline!(fn trampoline_draw_crosshair; impl_fn = draw_crosshair_impl;
120
  reg = ecx; stack_params = 5; ret_bytes = "0x14");
121
 
122
  unsafe extern "cdecl" fn draw_crosshair_impl(
123
- this: u32,
124
  layer: u32,
125
- x_pos: u32,
126
- y_pos: u32,
127
  color_fg: u32,
128
  color_bg: u32,
129
  ) {
130
- let q = &mut *(this as *mut RenderQueue);
131
-
132
- if let Some(entry) = q.alloc::<DrawCrosshairCmd>() {
133
  *entry = DrawCrosshairCmd {
134
  command_type: command_type::DRAW_CROSSHAIR,
135
  layer,
136
  color_fg,
137
  color_bg,
138
- x: Fixed(x_pos as i32),
139
- y: Fixed(y_pos as i32),
140
  ref_z: 0,
141
  };
142
  }
143
  }
144
 
145
- // ---------------------------------------------------------------------------
146
- // DrawRect (0x541F40) — type 0, ECX=this, EDX=y_clip, 6 stack, RET 0x18
147
- // ---------------------------------------------------------------------------
148
 
149
  usercall_trampoline!(fn trampoline_draw_rect; impl_fn = draw_rect_impl;
150
  regs = [ecx, edx]; stack_params = 6; ret_bytes = "0x18");
151
 
152
  unsafe extern "cdecl" fn draw_rect_impl(
153
- this: u32,
154
- y_clip: u32,
155
  layer: u32,
156
- x1: u32,
157
- y1: u32,
158
- x2: u32,
159
- y2: u32,
160
  color: u32,
161
  ) {
162
- let q = &mut *(this as *mut RenderQueue);
163
-
164
- if let Some(entry) = q.alloc::<DrawRectCmd>() {
165
  *entry = DrawRectCmd {
166
  command_type: command_type::DRAW_RECT,
167
  layer,
168
  color,
169
- x1: Fixed(x1 as i32).floor(),
170
- y1: Fixed(y1 as i32).floor(),
171
- x2: Fixed(x2 as i32).floor(),
172
- y2: Fixed(y2 as i32).floor(),
173
- ref_z: Fixed(y_clip as i32).floor().0,
174
  };
175
  }
176
  }
177
 
178
- // ---------------------------------------------------------------------------
179
- // DrawSpriteGlobal (0x541FE0) — type 4, EAX=y, ECX=this, 4 stack, RET 0x10
180
- // ---------------------------------------------------------------------------
181
 
182
  usercall_trampoline!(fn trampoline_draw_sprite_global; impl_fn = draw_sprite_global_impl;
183
  regs = [eax, ecx]; stack_params = 4; ret_bytes = "0x10");
184
 
185
  unsafe extern "cdecl" fn draw_sprite_global_impl(
186
- y_pos: u32,
187
- this: u32,
188
  layer: u32,
189
- x_pos: u32,
190
- sprite_id: u32,
191
  frame: u32,
192
  ) {
193
- let q = &mut *(this as *mut RenderQueue);
194
-
195
- if let Some(entry) = q.alloc::<DrawSpriteCmd>() {
196
  *entry = DrawSpriteCmd {
197
  command_type: command_type::DRAW_SPRITE_GLOBAL,
198
  layer,
199
- x: Fixed(x_pos as i32).floor(),
200
- y: Fixed(y_pos as i32).floor(),
201
- sprite: SpriteOp(sprite_id),
202
  palette: frame,
203
  };
204
  }
205
  }
206
 
207
- // ---------------------------------------------------------------------------
208
- // DrawSpriteLocal (0x542060) — type 5, EAX=y, ECX=this, 4 stack, RET 0x10
209
- // ---------------------------------------------------------------------------
210
 
211
  usercall_trampoline!(fn trampoline_draw_sprite_local; impl_fn = draw_sprite_local_impl;
212
  regs = [eax, ecx]; stack_params = 4; ret_bytes = "0x10");
213
 
214
  unsafe extern "cdecl" fn draw_sprite_local_impl(
215
- y_pos: u32,
216
- this: u32,
217
  layer: u32,
218
- x_pos: u32,
219
- sprite_id: u32,
220
  frame: u32,
221
  ) {
222
- let q = &mut *(this as *mut RenderQueue);
223
-
224
- if let Some(entry) = q.alloc::<DrawSpriteCmd>() {
225
  *entry = DrawSpriteCmd {
226
  command_type: command_type::DRAW_SPRITE_LOCAL,
227
  layer,
228
- x: Fixed(x_pos as i32).floor(),
229
- y: Fixed(y_pos as i32).floor(),
230
- sprite: SpriteOp(sprite_id),
231
  palette: frame,
232
  };
233
  }
234
  }
235
 
236
- // ---------------------------------------------------------------------------
237
- // DrawSpriteOffset (0x5420E0) — type 6, ECX=this, EDX=y_clip, 6 stack, RET 0x18
238
- // ---------------------------------------------------------------------------
239
 
240
  usercall_trampoline!(fn trampoline_draw_sprite_offset; impl_fn = draw_sprite_offset_impl;
241
  regs = [ecx, edx]; stack_params = 6; ret_bytes = "0x18");
242
 
243
  unsafe extern "cdecl" fn draw_sprite_offset_impl(
244
- this: u32,
245
- y_clip: u32,
246
  layer: u32,
247
- x_pos: u32,
248
- y_pos: u32,
249
- sprite_id: u32,
250
- param_7: u32,
251
- param_8: u32,
252
  ) {
253
- let q = &mut *(this as *mut RenderQueue);
254
-
255
- if let Some(entry) = q.alloc::<DrawSpriteOffsetCmd>() {
256
  *entry = DrawSpriteOffsetCmd {
257
  command_type: command_type::DRAW_SPRITE_OFFSET,
258
  layer,
259
- flags: sprite_id,
260
- x: Fixed(x_pos as i32).floor(),
261
- y: Fixed(y_pos as i32).floor(),
262
  ref_z: 0,
263
- ref_z_2: Fixed(y_clip as i32).floor().0,
264
- sprite: SpriteOp(param_7),
265
- palette: param_8,
266
  };
267
  }
268
  }
269
 
270
- // ---------------------------------------------------------------------------
271
- // DrawBitmapGlobal (0x542170) — type 1, ECX=this, EDX=y, 7 stack, RET 0x1C
272
- // ---------------------------------------------------------------------------
273
 
274
  usercall_trampoline!(fn trampoline_draw_bitmap_global; impl_fn = draw_bitmap_global_impl;
275
  regs = [ecx, edx]; stack_params = 7; ret_bytes = "0x1C");
276
 
277
  unsafe extern "cdecl" fn draw_bitmap_global_impl(
278
- this: u32,
279
- y_pos: u32,
280
  layer: u32,
281
- x_pos: u32,
282
- bitmap_ptr: u32,
283
- param_6: u32,
284
- param_7: u32,
285
- param_8: u32,
286
- param_9: u32,
287
  ) {
288
- let q = &mut *(this as *mut RenderQueue);
289
-
290
- if let Some(entry) = q.alloc::<DrawBitmapGlobalCmd>() {
291
  *entry = DrawBitmapGlobalCmd {
292
  command_type: command_type::DRAW_BITMAP_GLOBAL,
293
  layer,
294
- x: Fixed(x_pos as i32).floor(),
295
- y: Fixed(y_pos as i32).floor(),
296
- bitmap: bitmap_ptr as *mut _,
297
  src_x: 0,
298
- src_y: param_6 as i32,
299
- src_w: param_7 as i32,
300
- src_h: param_8 as i32,
301
- flags: param_9,
302
  };
303
  }
304
  }
305
 
306
- // ---------------------------------------------------------------------------
307
- // DrawTextboxLocal (0x542200) — type 2, ECX=this, EDX=y, 6 stack, RET 0x18
308
- // ---------------------------------------------------------------------------
309
 
310
  usercall_trampoline!(fn trampoline_draw_textbox_local; impl_fn = draw_textbox_local_impl;
311
  regs = [ecx, edx]; stack_params = 6; ret_bytes = "0x18");
312
 
313
  unsafe extern "cdecl" fn draw_textbox_local_impl(
314
- this: u32,
315
- y_pos: u32,
316
  layer: u32,
317
- x_pos: u32,
318
- text_ptr: u32,
319
- param_6: u32,
320
- param_7: u32,
321
- param_8: u32,
322
  ) {
323
- let q = &mut *(this as *mut RenderQueue);
324
-
325
- if let Some(entry) = q.alloc::<DrawTextboxLocalCmd>() {
326
  *entry = DrawTextboxLocalCmd {
327
  command_type: command_type::DRAW_TEXTBOX_LOCAL,
328
  layer,
329
  mode: 0,
330
- x: Fixed(x_pos as i32).floor(),
331
- y: Fixed(y_pos as i32).floor(),
332
  ref_z: 0,
333
  ref_z_2: 0,
334
- bitmap: text_ptr as *mut _,
335
  src_x: 0,
336
  src_y: 0,
337
- src_w: param_6 as i32,
338
- src_h: param_7 as i32,
339
- flags: param_8,
340
  };
341
  }
342
  }
343
 
344
- // ---------------------------------------------------------------------------
345
- // DrawBungeeTrail (0x500720) — stdcall(task, style, fill), RET 0xC
346
- //
347
- // Draws bungee drop trajectory path. Logic lives in openwa_core::render::bungee_trail.
348
- // ---------------------------------------------------------------------------
349
 
350
- unsafe extern "stdcall" fn draw_bungee_trail_impl(task_ptr: u32, style: u32, fill: u32) {
351
- openwa_core::render::bungee_trail::draw_bungee_trail(task_ptr, style, fill);
 
 
 
 
352
  }
353
 
354
- // ---------------------------------------------------------------------------
355
- // DrawCrosshairLine (0x5197D0) — usercall(EDI=task_ptr), plain RET
356
- //
357
- // Draws the weapon aiming crosshair line:
358
- // 1. Compute direction from angle at task+0x264
359
- // 2. Compute line length from DDGame scale + task offset
360
- // 3. Endpoint = start + direction * length (with overflow clamping)
361
- // 4. DrawPolygon (2 vertices) for the line
362
- // 5. Conditionally DrawSpriteLocal at endpoint (crosshair sprite)
363
- // ---------------------------------------------------------------------------
364
 
365
  usercall_trampoline!(fn trampoline_draw_crosshair_line; impl_fn = draw_crosshair_line_impl;
366
  reg = edi);
367
 
368
- unsafe extern "cdecl" fn draw_crosshair_line_impl(task_ptr: u32) {
369
- openwa_core::render::crosshair_line::draw_crosshair_line(task_ptr);
370
  }
371
 
372
- // ---------------------------------------------------------------------------
373
- // RenderDrawingQueue (0x542350) — usercall(EAX=RenderQueue*) + 2 stack
374
- // (DisplayGfx*, ClipContext*), RET 0x8.
375
- //
376
- // The per-frame render-queue dispatcher. Pure Rust port lives in
377
- // `openwa_core::render::queue_dispatch::render_drawing_queue`. The
378
- // trampoline captures EAX and forwards the two stack args.
379
- // ---------------------------------------------------------------------------
380
 
381
  usercall_trampoline!(fn trampoline_render_drawing_queue;
382
  impl_fn = render_drawing_queue_impl;
@@ -390,10 +330,6 @@ unsafe extern "cdecl" fn render_drawing_queue_impl(
390
  render_drawing_queue(rq, display, clip);
391
  }
392
 
393
- // ---------------------------------------------------------------------------
394
- // Installation
395
- // ---------------------------------------------------------------------------
396
-
397
  pub fn install() -> Result<(), String> {
398
  unsafe {
399
  let _ = hook::install(
 
1
+ //! RenderQueue enqueue hooks and the per-frame dispatcher bridge.
 
2
 
3
  use openwa_core::address::va;
4
+ use openwa_core::bitgrid::DisplayBitGrid;
5
  use openwa_core::fixed::Fixed;
6
+ use openwa_core::render::display::vtable::TiledBitmapSource;
7
  use openwa_core::render::display::DisplayGfx;
8
  use openwa_core::render::queue::*;
9
  use openwa_core::render::queue_dispatch::{render_drawing_queue, ClipContext};
10
  use openwa_core::render::SpriteOp;
11
+ use openwa_core::task::{BungeeTrailTask, WeaponAimTask};
12
 
13
  use crate::hook::{self, usercall_trampoline};
14
 
15
+ // EnqueueTiledBitmap (0x541D60)
 
 
 
 
 
 
16
 
17
  usercall_trampoline!(fn trampoline_enqueue_tiled_bitmap; impl_fn = enqueue_tiled_bitmap_impl;
18
  reg = eax; stack_params = 3; ret_bytes = "0xC");
19
 
20
  unsafe extern "cdecl" fn enqueue_tiled_bitmap_impl(
21
+ queue: *mut RenderQueue,
22
+ y: Fixed,
23
+ source: *const TiledBitmapSource,
24
  flags: u32,
25
  ) {
26
+ if let Some(entry) = (*queue).alloc::<DrawTiledBitmapCmd>() {
 
 
27
  *entry = DrawTiledBitmapCmd {
28
  command_type: command_type::DRAW_TILED_BITMAP,
29
  layer: 0x1B_0000,
30
  x: Fixed(0xFF00_0000u32 as i32),
31
+ y,
32
+ source,
33
  flags: flags as u8,
34
  _pad: [0; 3],
35
  };
36
  }
37
  }
38
 
39
+ // DrawLineStrip (0x541DD0) — variable-size allocation: count * 0xC + 0x1C
 
 
 
40
 
41
  usercall_trampoline!(fn trampoline_draw_line_strip; impl_fn = draw_line_strip_impl;
42
  regs = [eax, edi]; stack_params = 2; ret_bytes = "0x8");
43
 
44
  unsafe extern "cdecl" fn draw_line_strip_impl(
45
+ queue: *mut RenderQueue,
46
  count: u32,
47
+ vertices: *const u8,
48
+ color: u32,
49
  ) {
 
50
  let total_size = count as usize * 0xC + 0x1C;
51
 
52
+ if let Some(ptr) = (*queue).alloc_raw(total_size) {
53
  let header = &mut *(ptr as *mut DrawLineStripHeader);
54
  *header = DrawLineStripHeader {
55
  command_type: command_type::DRAW_LINE_STRIP,
56
  layer: 0xE_0000,
57
  count,
58
+ color,
59
  };
60
  core::ptr::copy_nonoverlapping(
61
+ vertices,
62
  ptr.add(core::mem::size_of::<DrawLineStripHeader>()),
63
  count as usize * 0xC,
64
  );
65
  }
66
  }
67
 
68
+ // DrawPolygon (0x541E50) — variable-size allocation: count * 0xC + 0x20
 
 
 
69
 
70
  usercall_trampoline!(fn trampoline_draw_polygon; impl_fn = draw_polygon_impl;
71
  regs = [ecx, esi]; stack_params = 3; ret_bytes = "0xC");
72
 
73
  unsafe extern "cdecl" fn draw_polygon_impl(
74
+ queue: *mut RenderQueue,
75
  count: u32,
76
+ vertices: *const u8,
77
+ color1: u32,
78
+ color2: u32,
79
  ) {
 
80
  let total_size = count as usize * 0xC + 0x20;
81
 
82
+ if let Some(ptr) = (*queue).alloc_raw(total_size) {
83
  let header = &mut *(ptr as *mut DrawPolygonHeader);
84
  *header = DrawPolygonHeader {
85
  command_type: command_type::DRAW_POLYGON,
86
  layer: 0xE_0000,
87
  count,
88
+ color1,
89
+ color2,
90
  };
91
  core::ptr::copy_nonoverlapping(
92
+ vertices,
93
  ptr.add(core::mem::size_of::<DrawPolygonHeader>()),
94
  count as usize * 0xC,
95
  );
96
  }
97
  }
98
 
99
+ // DrawCrosshair (0x541ED0)
 
 
 
 
100
 
101
  usercall_trampoline!(fn trampoline_draw_crosshair; impl_fn = draw_crosshair_impl;
102
  reg = ecx; stack_params = 5; ret_bytes = "0x14");
103
 
104
  unsafe extern "cdecl" fn draw_crosshair_impl(
105
+ queue: *mut RenderQueue,
106
  layer: u32,
107
+ x: Fixed,
108
+ y: Fixed,
109
  color_fg: u32,
110
  color_bg: u32,
111
  ) {
112
+ if let Some(entry) = (*queue).alloc::<DrawCrosshairCmd>() {
 
 
113
  *entry = DrawCrosshairCmd {
114
  command_type: command_type::DRAW_CROSSHAIR,
115
  layer,
116
  color_fg,
117
  color_bg,
118
+ x,
119
+ y,
120
  ref_z: 0,
121
  };
122
  }
123
  }
124
 
125
+ // DrawRect (0x541F40)
 
 
126
 
127
  usercall_trampoline!(fn trampoline_draw_rect; impl_fn = draw_rect_impl;
128
  regs = [ecx, edx]; stack_params = 6; ret_bytes = "0x18");
129
 
130
  unsafe extern "cdecl" fn draw_rect_impl(
131
+ queue: *mut RenderQueue,
132
+ y_clip: Fixed,
133
  layer: u32,
134
+ x1: Fixed,
135
+ y1: Fixed,
136
+ x2: Fixed,
137
+ y2: Fixed,
138
  color: u32,
139
  ) {
140
+ if let Some(entry) = (*queue).alloc::<DrawRectCmd>() {
 
 
141
  *entry = DrawRectCmd {
142
  command_type: command_type::DRAW_RECT,
143
  layer,
144
  color,
145
+ x1: x1.floor(),
146
+ y1: y1.floor(),
147
+ x2: x2.floor(),
148
+ y2: y2.floor(),
149
+ ref_z: y_clip.floor().0,
150
  };
151
  }
152
  }
153
 
154
+ // DrawSpriteGlobal (0x541FE0)
 
 
155
 
156
  usercall_trampoline!(fn trampoline_draw_sprite_global; impl_fn = draw_sprite_global_impl;
157
  regs = [eax, ecx]; stack_params = 4; ret_bytes = "0x10");
158
 
159
  unsafe extern "cdecl" fn draw_sprite_global_impl(
160
+ y_pos: Fixed,
161
+ queue: *mut RenderQueue,
162
  layer: u32,
163
+ x_pos: Fixed,
164
+ sprite: SpriteOp,
165
  frame: u32,
166
  ) {
167
+ if let Some(entry) = (*queue).alloc::<DrawSpriteCmd>() {
 
 
168
  *entry = DrawSpriteCmd {
169
  command_type: command_type::DRAW_SPRITE_GLOBAL,
170
  layer,
171
+ x: x_pos.floor(),
172
+ y: y_pos.floor(),
173
+ sprite,
174
  palette: frame,
175
  };
176
  }
177
  }
178
 
179
+ // DrawSpriteLocal (0x542060)
 
 
180
 
181
  usercall_trampoline!(fn trampoline_draw_sprite_local; impl_fn = draw_sprite_local_impl;
182
  regs = [eax, ecx]; stack_params = 4; ret_bytes = "0x10");
183
 
184
  unsafe extern "cdecl" fn draw_sprite_local_impl(
185
+ y_pos: Fixed,
186
+ queue: *mut RenderQueue,
187
  layer: u32,
188
+ x_pos: Fixed,
189
+ sprite: SpriteOp,
190
  frame: u32,
191
  ) {
192
+ if let Some(entry) = (*queue).alloc::<DrawSpriteCmd>() {
 
 
193
  *entry = DrawSpriteCmd {
194
  command_type: command_type::DRAW_SPRITE_LOCAL,
195
  layer,
196
+ x: x_pos.floor(),
197
+ y: y_pos.floor(),
198
+ sprite,
199
  palette: frame,
200
  };
201
  }
202
  }
203
 
204
+ // DrawSpriteOffset (0x5420E0)
 
 
205
 
206
  usercall_trampoline!(fn trampoline_draw_sprite_offset; impl_fn = draw_sprite_offset_impl;
207
  regs = [ecx, edx]; stack_params = 6; ret_bytes = "0x18");
208
 
209
  unsafe extern "cdecl" fn draw_sprite_offset_impl(
210
+ queue: *mut RenderQueue,
211
+ y_clip: Fixed,
212
  layer: u32,
213
+ x_pos: Fixed,
214
+ y_pos: Fixed,
215
+ flags: u32,
216
+ sprite: SpriteOp,
217
+ palette: u32,
218
  ) {
219
+ if let Some(entry) = (*queue).alloc::<DrawSpriteOffsetCmd>() {
 
 
220
  *entry = DrawSpriteOffsetCmd {
221
  command_type: command_type::DRAW_SPRITE_OFFSET,
222
  layer,
223
+ flags,
224
+ x: x_pos.floor(),
225
+ y: y_pos.floor(),
226
  ref_z: 0,
227
+ ref_z_2: y_clip.floor().0,
228
+ sprite,
229
+ palette,
230
  };
231
  }
232
  }
233
 
234
+ // DrawBitmapGlobal (0x542170)
 
 
235
 
236
  usercall_trampoline!(fn trampoline_draw_bitmap_global; impl_fn = draw_bitmap_global_impl;
237
  regs = [ecx, edx]; stack_params = 7; ret_bytes = "0x1C");
238
 
239
  unsafe extern "cdecl" fn draw_bitmap_global_impl(
240
+ queue: *mut RenderQueue,
241
+ y_pos: Fixed,
242
  layer: u32,
243
+ x_pos: Fixed,
244
+ bitmap: *mut DisplayBitGrid,
245
+ src_y: i32,
246
+ src_w: i32,
247
+ src_h: i32,
248
+ flags: u32,
249
  ) {
250
+ if let Some(entry) = (*queue).alloc::<DrawBitmapGlobalCmd>() {
 
 
251
  *entry = DrawBitmapGlobalCmd {
252
  command_type: command_type::DRAW_BITMAP_GLOBAL,
253
  layer,
254
+ x: x_pos.floor(),
255
+ y: y_pos.floor(),
256
+ bitmap,
257
  src_x: 0,
258
+ src_y,
259
+ src_w,
260
+ src_h,
261
+ flags,
262
  };
263
  }
264
  }
265
 
266
+ // DrawTextboxLocal (0x542200)
 
 
267
 
268
  usercall_trampoline!(fn trampoline_draw_textbox_local; impl_fn = draw_textbox_local_impl;
269
  regs = [ecx, edx]; stack_params = 6; ret_bytes = "0x18");
270
 
271
  unsafe extern "cdecl" fn draw_textbox_local_impl(
272
+ q: *mut RenderQueue,
273
+ y_pos: Fixed,
274
  layer: u32,
275
+ x_pos: Fixed,
276
+ bitmap: *mut DisplayBitGrid,
277
+ src_w: i32,
278
+ src_h: i32,
279
+ flags: u32,
280
  ) {
281
+ if let Some(entry) = (*q).alloc::<DrawTextboxLocalCmd>() {
 
 
282
  *entry = DrawTextboxLocalCmd {
283
  command_type: command_type::DRAW_TEXTBOX_LOCAL,
284
  layer,
285
  mode: 0,
286
+ x: x_pos.floor(),
287
+ y: y_pos.floor(),
288
  ref_z: 0,
289
  ref_z_2: 0,
290
+ bitmap,
291
  src_x: 0,
292
  src_y: 0,
293
+ src_w,
294
+ src_h,
295
+ flags,
296
  };
297
  }
298
  }
299
 
300
+ // DrawBungeeTrail (0x500720)
 
 
 
 
301
 
302
+ unsafe extern "stdcall" fn draw_bungee_trail_impl(
303
+ task: *const BungeeTrailTask,
304
+ style: u32,
305
+ fill: u32,
306
+ ) {
307
+ openwa_core::render::bungee_trail::draw_bungee_trail(task, style, fill);
308
  }
309
 
310
+ // DrawCrosshairLine (0x5197D0)
 
 
 
 
 
 
 
 
 
311
 
312
  usercall_trampoline!(fn trampoline_draw_crosshair_line; impl_fn = draw_crosshair_line_impl;
313
  reg = edi);
314
 
315
+ unsafe extern "cdecl" fn draw_crosshair_line_impl(task: *const WeaponAimTask) {
316
+ openwa_core::render::crosshair_line::draw_crosshair_line(task);
317
  }
318
 
319
+ // RenderDrawingQueue (0x542350)
 
 
 
 
 
 
 
320
 
321
  usercall_trampoline!(fn trampoline_render_drawing_queue;
322
  impl_fn = render_drawing_queue_impl;
 
330
  render_drawing_queue(rq, display, clip);
331
  }
332
 
 
 
 
 
333
  pub fn install() -> Result<(), String> {
334
  unsafe {
335
  let _ = hook::install(