paavohtl Claude Opus 4.6 (1M context) commited on
Commit
29a9a26
Β·
1 Parent(s): fcb7fd1

refactor: rename mislabeled tile-bitmap queue command and slot 11

Browse files

Trying to trap DisplayGfx vtable slot 11 as dead code revealed it is in
fact reachable: RenderDrawingQueue case 0xD dispatches into it, and the
queue entries come from our own Rust trampoline for what was labelled
RQ_DrawPixel. Both names were wrong end-to-end β€” the function enqueues a
tile-cached bitmap draw (only known producer: CTaskLand::RenderLandscape),
not a single pixel.

Renames, byte-format unchanged:
WA 0x541D60: RQ_DrawPixel β†’ RQ_EnqueueTiledBitmap
WA 0x56B8C0: DisplayGfx__CreateBitmap β†’ DisplayGfx__DrawTiledBitmap
Rust DrawPixelCmd β†’ DrawTiledBitmapCmd, with corrected field names
(x_pos/y_pos/color β†’ y_fixed16/source_descriptor/x_fixed16)
command_type::DRAW_PIXEL β†’ DRAW_TILED_BITMAP
trampoline_draw_pixel β†’ trampoline_enqueue_tiled_bitmap
vtable slot 11 create_bitmap β†’ draw_tiled_bitmap

The slot 11 docstring now captures the three-phase semantics (lazy
allocate tile surfaces, populate from source, clipped blit) so a future
port has a blueprint.

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

crates/openwa-core/src/address.rs CHANGED
@@ -240,6 +240,13 @@ pub mod va {
240
  fn/Stdcall DISPLAY_GFX_INIT_TEAM_PALETTE_DISPLAY = 0x0057_03E0;
241
  /// DisplayGfx__LoadSpriteEx (vtable slot 30) β€” thiscall
242
  fn/Thiscall DISPLAY_GFX_LOAD_SPRITE_EX = 0x0052_3310;
 
 
 
 
 
 
 
243
  }
244
 
245
  class "Font" {
@@ -463,7 +470,10 @@ pub mod va {
463
  fn BLIT_SCREEN = 0x005A_2020;
464
  fn RQ_RENDER_DRAWING_QUEUE = 0x0054_2350;
465
  fn DRAW_LANDSCAPE = 0x005A_2790;
466
- fn RQ_DRAW_PIXEL = 0x0054_1D60;
 
 
 
467
  fn RQ_DRAW_LINE_STRIP = 0x0054_1DD0;
468
  fn RQ_DRAW_POLYGON = 0x0054_1E50;
469
  fn RQ_DRAW_CROSSHAIR = 0x0054_1ED0;
 
240
  fn/Stdcall DISPLAY_GFX_INIT_TEAM_PALETTE_DISPLAY = 0x0057_03E0;
241
  /// DisplayGfx__LoadSpriteEx (vtable slot 30) β€” thiscall
242
  fn/Thiscall DISPLAY_GFX_LOAD_SPRITE_EX = 0x0052_3310;
243
+ /// `DisplayGfx__DrawTiledBitmap` (vtable slot 11) β€” thiscall.
244
+ /// Tile-cached bitmap draw: lazily allocates 0x400-row tile
245
+ /// surfaces from a sprite source descriptor, populates them, and
246
+ /// blits the visible tiles to the display. Reachable today via
247
+ /// `RenderDrawingQueue` case 0xD; needs porting if/when slot is
248
+ /// replaced.
249
+ fn/Thiscall DISPLAY_GFX_DRAW_TILED_BITMAP = 0x0056_B8C0;
250
  }
251
 
252
  class "Font" {
 
470
  fn BLIT_SCREEN = 0x005A_2020;
471
  fn RQ_RENDER_DRAWING_QUEUE = 0x0054_2350;
472
  fn DRAW_LANDSCAPE = 0x005A_2790;
473
+ /// `RQ_EnqueueTiledBitmap` β€” formerly mis-labelled `RQ_DrawPixel`.
474
+ /// Enqueues a tile-cached bitmap draw command (type 0xD), dispatched
475
+ /// by `RenderDrawingQueue` into `DisplayGfx::draw_tiled_bitmap`.
476
+ fn RQ_ENQUEUE_TILED_BITMAP = 0x0054_1D60;
477
  fn RQ_DRAW_LINE_STRIP = 0x0054_1DD0;
478
  fn RQ_DRAW_POLYGON = 0x0054_1E50;
479
  fn RQ_DRAW_CROSSHAIR = 0x0054_1ED0;
crates/openwa-core/src/render/display/vtable.rs CHANGED
@@ -100,11 +100,37 @@ pub struct DisplayVtable {
100
  /// set font rendering parameter (0x523710, RET 0x10)
101
  #[slot(10)]
102
  pub set_font_param: fn(this: *mut DisplayGfx, font_id: i32, p3: u32, p4: u32, p5: u32) -> u32,
103
- /// create bitmap object (0x56B8C0, RET 0xC)
104
  ///
105
- /// Allocates CBitmap, initializes from sprite data.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  #[slot(11)]
107
- pub create_bitmap: fn(this: *mut DisplayGfx, count: u32, p3: i32, p4: i32),
108
  /// draw polyline with camera offset (0x56BCC0, RET 0xC)
109
  ///
110
  /// Transforms point array by camera offset, then draws connected line segments.
 
100
  /// set font rendering parameter (0x523710, RET 0x10)
101
  #[slot(10)]
102
  pub set_font_param: fn(this: *mut DisplayGfx, font_id: i32, p3: u32, p4: u32, p5: u32) -> u32,
103
+ /// draw tile-cached bitmap (0x56B8C0, RET 0xC) β€” `DisplayGfx__DrawTiledBitmap`.
104
  ///
105
+ /// Three-phase tile-bitmap operation, run as a single composite call.
106
+ /// Each invocation does some or all of:
107
+ ///
108
+ /// 1. **Allocate** (only if `this->bitmap_vec` is empty): walk the source
109
+ /// height in 0x400-row strips. For each strip, `wa_malloc(0xC)` a
110
+ /// `CBitmap` (vtable `0x643F64`, surface ptr at +4), lazily ask the
111
+ /// render context for a surface, init the surface as `0x40 Γ— strip Γ— 8bpp`
112
+ /// (retrying with 4bpp on failure), and `vector::push_back` the bitmap
113
+ /// into `DisplayGfx + 0x3580`. Sets `DisplayGfx[+0x358C] = 0`.
114
+ /// 2. **Populate** (only if `DisplayGfx[+0x358C] == 0`): for each tile,
115
+ /// lock its surface, blit the corresponding source strip via
116
+ /// `FUN_005B2A5E` (8bpp) or `BlitColorTable_Forward` (CLUT, `bpp == 0x40`),
117
+ /// unlock. Sets `DisplayGfx[+0x358C] = 1`.
118
+ /// 3. **Display**: `dest_x` is masked to a 0x40-aligned X coord; the
119
+ /// visible Y range is computed from `camera_y + dest_y` against
120
+ /// `display_height`, clamped to the available tile range. For each
121
+ /// visible tile Γ— each `0x40`-wide destination column, calls
122
+ /// `DisplayGfx__BlitBitmapClipped`.
123
+ ///
124
+ /// `source` is a sprite-source descriptor with at least these fields:
125
+ /// `+0x08` = bpp (8 or 0x40), `+0x10` = source row stride,
126
+ /// `+0x14` = (saved early, purpose unconfirmed), `+0x18` = total height.
127
+ ///
128
+ /// **Reachable at runtime** via `RenderDrawingQueue` case 0xD, fed by
129
+ /// `RQ_EnqueueTiledBitmap` (0x541D60). The only known producer is
130
+ /// `CTaskLand::RenderLandscape`. Currently bridged to the original WA
131
+ /// function β€” needs porting if/when slot 11 is replaced.
132
  #[slot(11)]
133
+ pub draw_tiled_bitmap: fn(this: *mut DisplayGfx, dest_x: u32, dest_y: i32, source: *mut u8),
134
  /// draw polyline with camera offset (0x56BCC0, RET 0xC)
135
  ///
136
  /// Transforms point array by camera offset, then draws connected line segments.
crates/openwa-core/src/render/mod.rs CHANGED
@@ -15,8 +15,8 @@ pub use display::{
15
  pub use landscape::{DirtyRect, PCLandscape};
16
  pub use opengl::OpenGLState;
17
  pub use queue::{
18
- DrawBitmapGlobalCmd, DrawCrosshairCmd, DrawLineStripHeader, DrawPixelCmd, DrawPolygonHeader,
19
- DrawRectCmd, DrawSpriteCmd, DrawSpriteOffsetCmd, DrawTextboxLocalCmd, RenderQueue,
20
  };
21
  pub use sprite::{ParsedSprite, Sprite, SpriteBank, SpriteFrame, SpriteId};
22
  pub use turn_order::{
 
15
  pub use landscape::{DirtyRect, PCLandscape};
16
  pub use opengl::OpenGLState;
17
  pub use queue::{
18
+ DrawBitmapGlobalCmd, DrawCrosshairCmd, DrawLineStripHeader, DrawPolygonHeader, DrawRectCmd,
19
+ DrawSpriteCmd, DrawSpriteOffsetCmd, DrawTextboxLocalCmd, DrawTiledBitmapCmd, RenderQueue,
20
  };
21
  pub use sprite::{ParsedSprite, Sprite, SpriteBank, SpriteFrame, SpriteId};
22
  pub use turn_order::{
crates/openwa-core/src/render/queue.rs CHANGED
@@ -78,19 +78,54 @@ impl RenderQueue {
78
  }
79
  }
80
 
81
- /// Type 0xD β€” single pixel (0x18 bytes).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  #[repr(C)]
83
- pub struct DrawPixelCmd {
84
- pub command_type: u32, // 0xD
85
- pub layer: u32, // hardcoded 0x1B_0000
86
- pub color: u32, // hardcoded 0xFF00_0000
87
- pub x_pos: u32,
88
- pub y_pos: u32,
 
 
 
 
 
 
 
 
 
 
 
 
89
  pub flags: u8,
90
  pub _pad: [u8; 3],
91
  }
92
 
93
- const _: () = assert!(core::mem::size_of::<DrawPixelCmd>() == 0x18);
94
 
95
  /// Type 0xB β€” draw crosshair (0x1C = 28 bytes).
96
  ///
@@ -217,5 +252,7 @@ pub mod command_type {
217
  pub const DRAW_POLYGON: u32 = 9;
218
  pub const DRAW_CROSSHAIR: u32 = 0xB;
219
  pub const DRAW_OUTLINED_PIXEL: u32 = 0xC;
220
- pub const DRAW_PIXEL: u32 = 0xD;
 
 
221
  }
 
78
  }
79
  }
80
 
81
+ /// Type 0xD β€” tile-bitmap draw command (0x18 bytes).
82
+ ///
83
+ /// Enqueued by `RQ_EnqueueTiledBitmap` (0x541D60, formerly mis-labelled
84
+ /// `RQ_DrawPixel`). The only known producer is `CTaskLand::RenderLandscape`,
85
+ /// which uses it to draw cached landscape texture tiles.
86
+ ///
87
+ /// Inside `RenderDrawingQueue`, case 0xD dispatches to
88
+ /// `DisplayGfx::draw_tiled_bitmap` (vtable slot 11, 0x56B8C0):
89
+ /// ```text
90
+ /// RQ_ClipCoordinates(0, x_fixed16, y_fixed16, &out_x, &out_y, ...);
91
+ /// vtable[slot 11](
92
+ /// out_x >> 16 (or 0 if `flags & 1`), // X in pixels
93
+ /// out_y >> 16, // Y in pixels
94
+ /// source_descriptor // sprite source struct *
95
+ /// );
96
+ /// ```
97
+ ///
98
+ /// The slot 11 method itself runs three phases on each call: lazily allocate
99
+ /// 0x400-row tile surfaces from `source_descriptor->total_height` (cached in
100
+ /// `DisplayGfx + 0x3580`), populate them from the source, then clipped-blit
101
+ /// the tiles to the display.
102
+ ///
103
+ /// Field names below are corrected. Wire format is byte-identical to what
104
+ /// the original `RQ_EnqueueTiledBitmap` writes.
105
  #[repr(C)]
106
+ pub struct DrawTiledBitmapCmd {
107
+ /// 0x00: Command type β€” always 0xD.
108
+ pub command_type: u32,
109
+ /// 0x04: Render layer β€” hardcoded 0x1B_0000 by the enqueue function.
110
+ pub layer: u32,
111
+ /// 0x08: Source X coordinate (Fixed16). Hardcoded 0xFF00_0000 (= -256.0)
112
+ /// β€” the tile sheet always renders from off-screen-left, with the
113
+ /// dispatcher's clipping pass producing the on-screen X.
114
+ pub x_fixed16: u32,
115
+ /// 0x0C: Source Y coordinate (Fixed16). Caller-supplied; goes through
116
+ /// `RQ_ClipCoordinates` and the high 16 bits become the destination Y.
117
+ pub y_fixed16: u32,
118
+ /// 0x10: Pointer to a sprite source descriptor β€” read by the slot 11
119
+ /// method as `p4` with fields at `+0x08` (bpp, 8 or 0x40), `+0x10`
120
+ /// (source row stride), `+0x14`, and `+0x18` (total source height).
121
+ pub source_descriptor: u32,
122
+ /// 0x14: Flag byte. Bit 0 forces the destination X to 0 (ignoring the
123
+ /// clipped result), used for "always start at left edge" rendering.
124
  pub flags: u8,
125
  pub _pad: [u8; 3],
126
  }
127
 
128
+ const _: () = assert!(core::mem::size_of::<DrawTiledBitmapCmd>() == 0x18);
129
 
130
  /// Type 0xB β€” draw crosshair (0x1C = 28 bytes).
131
  ///
 
252
  pub const DRAW_POLYGON: u32 = 9;
253
  pub const DRAW_CROSSHAIR: u32 = 0xB;
254
  pub const DRAW_OUTLINED_PIXEL: u32 = 0xC;
255
+ /// Tile-cached bitmap draw β€” dispatches to `DisplayGfx::draw_tiled_bitmap`
256
+ /// (vtable slot 11). Formerly mis-labelled `DRAW_PIXEL`.
257
+ pub const DRAW_TILED_BITMAP: u32 = 0xD;
258
  }
crates/openwa-dll/src/replacements/render.rs CHANGED
@@ -21,22 +21,31 @@ use crate::log_line;
21
  // Calling conventions are __usercall variants with register + stack params.
22
 
23
  // ---------------------------------------------------------------------------
24
- // DrawPixel (0x541D60) β€” type 0xD, EAX=this, 3 stack, RET 0xC
 
 
 
 
25
  // ---------------------------------------------------------------------------
26
 
27
- usercall_trampoline!(fn trampoline_draw_pixel; impl_fn = draw_pixel_impl;
28
  reg = eax; stack_params = 3; ret_bytes = "0xC");
29
 
30
- unsafe extern "cdecl" fn draw_pixel_impl(this: u32, x_pos: u32, y_pos: u32, flags: u32) {
 
 
 
 
 
31
  let q = &mut *(this as *mut RenderQueue);
32
 
33
- if let Some(entry) = q.alloc::<DrawPixelCmd>() {
34
- *entry = DrawPixelCmd {
35
- command_type: command_type::DRAW_PIXEL,
36
  layer: 0x1B_0000,
37
- color: 0xFF00_0000,
38
- x_pos,
39
- y_pos,
40
  flags: flags as u8,
41
  _pad: [0; 3],
42
  };
@@ -594,9 +603,9 @@ unsafe extern "cdecl" fn draw_crosshair_line_impl(task_ptr: u32) {
594
  fn install_render_queue() -> Result<(), String> {
595
  unsafe {
596
  let _ = hook::install(
597
- "DrawPixel",
598
- va::RQ_DRAW_PIXEL,
599
- trampoline_draw_pixel as *const (),
600
  )?;
601
 
602
  let _ = hook::install(
 
21
  // Calling conventions are __usercall variants with register + stack params.
22
 
23
  // ---------------------------------------------------------------------------
24
+ // EnqueueTiledBitmap (0x541D60) β€” type 0xD, EAX=this, 3 stack, RET 0xC
25
+ //
26
+ // Mis-labelled `RQ_DrawPixel` in earlier reverse-engineering passes. It does
27
+ // NOT enqueue a single-pixel draw β€” it enqueues a tile-cached bitmap draw
28
+ // dispatched by `RenderDrawingQueue` case 0xD into vtable slot 11.
29
  // ---------------------------------------------------------------------------
30
 
31
+ usercall_trampoline!(fn trampoline_enqueue_tiled_bitmap; impl_fn = enqueue_tiled_bitmap_impl;
32
  reg = eax; stack_params = 3; ret_bytes = "0xC");
33
 
34
+ unsafe extern "cdecl" fn enqueue_tiled_bitmap_impl(
35
+ this: u32,
36
+ y_fixed16: u32,
37
+ source_descriptor: u32,
38
+ flags: u32,
39
+ ) {
40
  let q = &mut *(this as *mut RenderQueue);
41
 
42
+ if let Some(entry) = q.alloc::<DrawTiledBitmapCmd>() {
43
+ *entry = DrawTiledBitmapCmd {
44
+ command_type: command_type::DRAW_TILED_BITMAP,
45
  layer: 0x1B_0000,
46
+ x_fixed16: 0xFF00_0000,
47
+ y_fixed16,
48
+ source_descriptor,
49
  flags: flags as u8,
50
  _pad: [0; 3],
51
  };
 
603
  fn install_render_queue() -> Result<(), String> {
604
  unsafe {
605
  let _ = hook::install(
606
+ "EnqueueTiledBitmap",
607
+ va::RQ_ENQUEUE_TILED_BITMAP,
608
+ trampoline_enqueue_tiled_bitmap as *const (),
609
  )?;
610
 
611
  let _ = hook::install(