refactor: switch slot 22 (DrawTiledTerrain) to native BlitBitmapClipped
Browse files`draw_tiled_terrain` was the last in-game caller of the asm bridge to
WA's `DisplayGfx::BlitBitmapClipped` (0x56A700). Slot 11's port already
had `blit_bitmap_clipped_native` doing the same clipping + dispatch
through `cbitmap_blit_via_wrapper`, so the bridge was redundant.
Verified the call site at 0x56C6A1: `EAX=this, EDX=col_width, stack
=(dst_x, dst_y, row_height, bitmap_ptr, flags)` maps cleanly to
`blit_bitmap_clipped_native(this, dst_x, dst_y, width, height,
surface, flags)`. Verified `TileBitmapSet::bitmap_ptrs[]` entries
share the CBitmap layout used by slot 11 by tracing FUN_00403c60's
`[ESI+0x4]` surface read against `CBitmap.surface`.
- Retypes `TileBitmapSet::bitmap_ptrs` from `*const u32` to
`*const *mut CBitmap`.
- Slot 22 now calls `blit_bitmap_clipped_native` directly.
- Deletes the `blit_bitmap_clipped` Rust wrapper and the
`blit_bitmap_clipped_bridge` naked-asm trampoline (~50 lines).
- Keeps `va::DISPLAY_GFX_BLIT_BITMAP_CLIPPED` because the slot-19
bitmap-sprite fallback path (`wa_blit_bitmap_clipped`) still
bridges to WA — that gets cleaned up by follow-up plan item 4.
Verification: cargo build clean, 15/15 headless replay tests pass,
headful napalm replay confirmed by user (no visual changes).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@@ -1,6 +1,7 @@
|
|
| 1 |
use super::base::DisplayBase;
|
| 2 |
use super::vtable::DisplayGfxVtable;
|
| 3 |
use crate::bitgrid::DisplayBitGrid;
|
|
|
|
| 4 |
use core::ffi::c_void;
|
| 5 |
|
| 6 |
/// DisplayGfx — full display/graphics subsystem (derived from DisplayBase).
|
|
@@ -212,10 +213,15 @@ impl DisplayGfx {
|
|
| 212 |
///
|
| 213 |
/// Pointed to by `DisplayGfx::tile_bitmap_sets`. Used by draw_tiled_terrain (slot 22)
|
| 214 |
/// to iterate over bitmap tiles in a grid pattern.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 215 |
#[repr(C)]
|
| 216 |
pub struct TileBitmapSet {
|
| 217 |
/// 0x00: Bitmap count or total size (observed values: 0x168 = 360)
|
| 218 |
pub count: u32,
|
| 219 |
-
/// 0x04: Pointer to array of
|
| 220 |
-
pub bitmap_ptrs: *const
|
| 221 |
}
|
|
|
|
| 1 |
use super::base::DisplayBase;
|
| 2 |
use super::vtable::DisplayGfxVtable;
|
| 3 |
use crate::bitgrid::DisplayBitGrid;
|
| 4 |
+
use crate::render::sprite::sprite::CBitmap;
|
| 5 |
use core::ffi::c_void;
|
| 6 |
|
| 7 |
/// DisplayGfx — full display/graphics subsystem (derived from DisplayBase).
|
|
|
|
| 213 |
///
|
| 214 |
/// Pointed to by `DisplayGfx::tile_bitmap_sets`. Used by draw_tiled_terrain (slot 22)
|
| 215 |
/// to iterate over bitmap tiles in a grid pattern.
|
| 216 |
+
///
|
| 217 |
+
/// Each `bitmap_ptrs[i]` is a [`CBitmap`] — the same struct slot 11's
|
| 218 |
+
/// `bitmap_vec` uses. WA's BlitBitmapClipped (`0x56A700`) and the inner
|
| 219 |
+
/// FUN_00403c60 read `+0x4` (the `surface` field) on every entry; the
|
| 220 |
+
/// vtable+surface+pad layout matches.
|
| 221 |
#[repr(C)]
|
| 222 |
pub struct TileBitmapSet {
|
| 223 |
/// 0x00: Bitmap count or total size (observed values: 0x168 = 360)
|
| 224 |
pub count: u32,
|
| 225 |
+
/// 0x04: Pointer to array of CBitmap pointers (one per grid tile, row-major).
|
| 226 |
+
pub bitmap_ptrs: *const *mut CBitmap,
|
| 227 |
}
|
|
@@ -1126,11 +1126,11 @@ pub unsafe extern "thiscall" fn draw_tiled_terrain(
|
|
| 1126 |
|
| 1127 |
let bitmap_ptr = *bitmap_array.add(bitmap_idx as usize);
|
| 1128 |
|
| 1129 |
-
|
| 1130 |
this,
|
| 1131 |
-
col_w,
|
| 1132 |
pixel_x + x_offset,
|
| 1133 |
pixel_y + y_offset,
|
|
|
|
| 1134 |
row_h,
|
| 1135 |
bitmap_ptr,
|
| 1136 |
blit_flags,
|
|
@@ -1144,59 +1144,6 @@ pub unsafe extern "thiscall" fn draw_tiled_terrain(
|
|
| 1144 |
}
|
| 1145 |
}
|
| 1146 |
|
| 1147 |
-
/// Bridge to DisplayGfx__BlitBitmapClipped (0x56A700).
|
| 1148 |
-
///
|
| 1149 |
-
/// Usercall: EAX=this (DisplayGfx*), EDX=col_width, 5 stack params (dst_x, dst_y,
|
| 1150 |
-
/// row_height, bitmap_ptr, flags), RET 0x14.
|
| 1151 |
-
///
|
| 1152 |
-
/// Clips the bitmap rectangle against the display clip rect, calls flush_render_lock,
|
| 1153 |
-
/// then delegates to the low-level blit function (0x403C60).
|
| 1154 |
-
unsafe fn blit_bitmap_clipped(
|
| 1155 |
-
gfx: *mut DisplayGfx,
|
| 1156 |
-
col_width: i32,
|
| 1157 |
-
dst_x: i32,
|
| 1158 |
-
dst_y: i32,
|
| 1159 |
-
row_height: i32,
|
| 1160 |
-
bitmap_ptr: u32,
|
| 1161 |
-
flags: u32,
|
| 1162 |
-
) {
|
| 1163 |
-
blit_bitmap_clipped_bridge(
|
| 1164 |
-
gfx as u32,
|
| 1165 |
-
col_width as u32,
|
| 1166 |
-
dst_x as u32,
|
| 1167 |
-
dst_y as u32,
|
| 1168 |
-
row_height as u32,
|
| 1169 |
-
bitmap_ptr,
|
| 1170 |
-
flags,
|
| 1171 |
-
rb(va::DISPLAY_GFX_BLIT_BITMAP_CLIPPED),
|
| 1172 |
-
);
|
| 1173 |
-
}
|
| 1174 |
-
|
| 1175 |
-
/// Naked bridge: sets EAX=this, EDX=col_width, pushes 5 stack params, calls target.
|
| 1176 |
-
#[unsafe(naked)]
|
| 1177 |
-
unsafe extern "cdecl" fn blit_bitmap_clipped_bridge(
|
| 1178 |
-
_this: u32,
|
| 1179 |
-
_col_width: u32,
|
| 1180 |
-
_dst_x: u32,
|
| 1181 |
-
_dst_y: u32,
|
| 1182 |
-
_row_height: u32,
|
| 1183 |
-
_bitmap_ptr: u32,
|
| 1184 |
-
_flags: u32,
|
| 1185 |
-
_target: u32,
|
| 1186 |
-
) {
|
| 1187 |
-
core::arch::naked_asm!(
|
| 1188 |
-
"mov eax, [esp + 4]", // EAX = this
|
| 1189 |
-
"mov edx, [esp + 8]", // EDX = col_width
|
| 1190 |
-
"push [esp + 28]", // flags
|
| 1191 |
-
"push [esp + 28]", // bitmap_ptr (shifted by our push)
|
| 1192 |
-
"push [esp + 28]", // row_height
|
| 1193 |
-
"push [esp + 28]", // dst_y
|
| 1194 |
-
"push [esp + 28]", // dst_x
|
| 1195 |
-
"call [esp + 52]", // target (offset: 5 pushes × 4 + 32 original)
|
| 1196 |
-
"ret",
|
| 1197 |
-
);
|
| 1198 |
-
}
|
| 1199 |
-
|
| 1200 |
/// Port of DisplayGfx::DrawPixelStrip (vtable slot 15, 0x56BE10).
|
| 1201 |
///
|
| 1202 |
/// Draws `count + 1` pixels starting at (x, y), stepping by (dx, dy) each
|
|
@@ -2764,12 +2711,11 @@ unsafe fn cbitmap_blit_via_wrapper(
|
|
| 2764 |
/// render lock if held, and dispatches the actual blit through
|
| 2765 |
/// `cbitmap_blit_via_wrapper`.
|
| 2766 |
///
|
| 2767 |
-
///
|
| 2768 |
-
///
|
| 2769 |
-
///
|
| 2770 |
-
///
|
| 2771 |
-
///
|
| 2772 |
-
/// caller to validate against.)
|
| 2773 |
///
|
| 2774 |
/// # Safety
|
| 2775 |
/// `this` must be a valid `*mut DisplayGfx`. `surface` must be a valid
|
|
|
|
| 1126 |
|
| 1127 |
let bitmap_ptr = *bitmap_array.add(bitmap_idx as usize);
|
| 1128 |
|
| 1129 |
+
blit_bitmap_clipped_native(
|
| 1130 |
this,
|
|
|
|
| 1131 |
pixel_x + x_offset,
|
| 1132 |
pixel_y + y_offset,
|
| 1133 |
+
col_w,
|
| 1134 |
row_h,
|
| 1135 |
bitmap_ptr,
|
| 1136 |
blit_flags,
|
|
|
|
| 1144 |
}
|
| 1145 |
}
|
| 1146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1147 |
/// Port of DisplayGfx::DrawPixelStrip (vtable slot 15, 0x56BE10).
|
| 1148 |
///
|
| 1149 |
/// Draws `count + 1` pixels starting at (x, y), stepping by (dx, dy) each
|
|
|
|
| 2711 |
/// render lock if held, and dispatches the actual blit through
|
| 2712 |
/// `cbitmap_blit_via_wrapper`.
|
| 2713 |
///
|
| 2714 |
+
/// Used by ported slots 11 (`DrawTiledBitmap`) and 22 (`DrawTiledTerrain`).
|
| 2715 |
+
/// The original WA function at `0x56A700` still exists and is called by
|
| 2716 |
+
/// the slot-19 bitmap-sprite fallback path (`wa_blit_bitmap_clipped`) —
|
| 2717 |
+
/// see follow-up plan item 4. The address constant
|
| 2718 |
+
/// `DISPLAY_GFX_BLIT_BITMAP_CLIPPED` is kept until that path is ported.
|
|
|
|
| 2719 |
///
|
| 2720 |
/// # Safety
|
| 2721 |
/// `this` must be a valid `*mut DisplayGfx`. `surface` must be a valid
|