refactor: rename mislabeled tile-bitmap queue command and slot 11
Browse filesTrying 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>
|
@@ -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 |
-
|
|
|
|
|
|
|
|
|
|
| 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;
|
|
@@ -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 |
-
///
|
| 104 |
///
|
| 105 |
-
///
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
#[slot(11)]
|
| 107 |
-
pub
|
| 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.
|
|
@@ -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,
|
| 19 |
-
|
| 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::{
|
|
@@ -78,19 +78,54 @@ impl RenderQueue {
|
|
| 78 |
}
|
| 79 |
}
|
| 80 |
|
| 81 |
-
/// Type 0xD β
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
#[repr(C)]
|
| 83 |
-
pub struct
|
| 84 |
-
|
| 85 |
-
pub
|
| 86 |
-
|
| 87 |
-
pub
|
| 88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
pub flags: u8,
|
| 90 |
pub _pad: [u8; 3],
|
| 91 |
}
|
| 92 |
|
| 93 |
-
const _: () = assert!(core::mem::size_of::<
|
| 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 |
-
|
|
|
|
|
|
|
| 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 |
}
|
|
@@ -21,22 +21,31 @@ use crate::log_line;
|
|
| 21 |
// Calling conventions are __usercall variants with register + stack params.
|
| 22 |
|
| 23 |
// ---------------------------------------------------------------------------
|
| 24 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
// ---------------------------------------------------------------------------
|
| 26 |
|
| 27 |
-
usercall_trampoline!(fn
|
| 28 |
reg = eax; stack_params = 3; ret_bytes = "0xC");
|
| 29 |
|
| 30 |
-
unsafe extern "cdecl" fn
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
let q = &mut *(this as *mut RenderQueue);
|
| 32 |
|
| 33 |
-
if let Some(entry) = q.alloc::<
|
| 34 |
-
*entry =
|
| 35 |
-
command_type: command_type::
|
| 36 |
layer: 0x1B_0000,
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 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 |
-
"
|
| 598 |
-
va::
|
| 599 |
-
|
| 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(
|