refactor: propagate type info through display vtable.rs and friends
Browse filesA series of small type-safety improvements that compound to delete a
lot of raw byte arithmetic and ad-hoc transmutes:
- `LayerSpriteFrame.bitmap_vtable` retyped from `u32` to
`*const c_void`, and `LayerSpriteFrame.surface` from `u32` to
`*mut Surface`. The two existing consumers (the slot 0 destructor's
`layer_sprite_frame_destructor` and the `load_sprite_by_name` /
`free_layer_sprite` pair) now access these via named fields
instead of raw `elem.add(0x08)` / `elem.add(0x0C)` arithmetic.
- `SurfaceVtable::set_color_key` (slot 7) added as a typed binding,
matching the existing `lock_surface` / `unlock_surface` /
`init_surface` / `release` slots. `load_sprite_by_name`'s per-frame
surface init loop now uses the typed binding for slots 3, 4, 5, 6,
7 instead of hand-rolled raw transmutes — drops ~50 lines.
- `RenderContextVtable::lock_surface_write`'s `out_data` retyped
from `*mut u32` to `*mut *mut u8`, and `unlock_surface_write`'s
`p2: u32` renamed to `data: *mut u8`. The acquire/flush helpers
in `vtable.rs` propagate the typed pointer through into
`(*layer_0).data` without the `as *mut u8` cast.
- `font_extend`'s `char_map` and slot 35 `load_font_extension`'s
`char_map` retyped from `*const u8` to `*const c_char` (the
original is a null-terminated C-string passed via the .fex file
format). Similarly `font_draw_text_impl`'s `msg` is now
`*const c_char` to match the slot 7 caller signature, with one
internal cast to `*const u8` at function entry.
- `palette_commit_bridge` now takes `*mut DisplayGfx` instead of
`*mut u8`.
- `LayerSprite` field reads in `load_sprite_by_name` now use
`&raw mut (*sprite).field_name` instead of `sp.add(0x60)` style
raw byte offsets — eight call sites cleaned up.
- Hand-derived counted-array allocation in `load_sprite_by_name` now
pulls the per-element size from `size_of::<LayerSpriteFrame>()`
via a local const, removing the bare `0x14` magic numbers from
the loops.
- New named constants: `MAX_FONT_ID = 31`,
`SPRITE_LOAD_ALREADY_DONE = 0x0080_0000`, plus a new
`is_valid_font_id` helper. Replaces seven scattered
`(1..=31).contains(&font_id)` and two `id & 0x80_0000` magic
checks across `load_font` / `load_font_extension` / `get_font_info`
/ `get_font_metric` / `set_font_param` / `draw_text_on_bitmap` /
`load_sprite` / `load_sprite_by_layer`.
- `set_camera_offset`'s vtable docstring updated to reference
`camera_x` / `camera_y` instead of the now-stale offsets
`+0x3560` / `+0x3564`.
No behavior changes — just type-system tightening. Headless replay
tests pass; one parallel run had a 2/15 spurious flake but the
serial re-run was 15/15.
- crates/openwa-core/src/engine/ddgame_load_fonts.rs +1 -1
- crates/openwa-core/src/render/display/context.rs +18 -2
- crates/openwa-core/src/render/display/destructor.rs +6 -4
- crates/openwa-core/src/render/display/font.rs +3 -1
- crates/openwa-core/src/render/display/vtable.rs +88 -124
- crates/openwa-core/src/render/sprite/sprite.rs +4 -4
- crates/openwa-dll/src/replacements/render.rs +1 -1
|
@@ -115,7 +115,7 @@ unsafe fn load_font_extension(wrapper: *mut DDGameWrapper, font_id: u32) {
|
|
| 115 |
display,
|
| 116 |
font_id as i32,
|
| 117 |
path.as_ptr() as *const c_char,
|
| 118 |
-
char_map.as_ptr(),
|
| 119 |
palette_value,
|
| 120 |
0,
|
| 121 |
);
|
|
|
|
| 115 |
display,
|
| 116 |
font_id as i32,
|
| 117 |
path.as_ptr() as *const c_char,
|
| 118 |
+
char_map.as_ptr() as *const c_char,
|
| 119 |
palette_value,
|
| 120 |
0,
|
| 121 |
);
|
|
@@ -187,15 +187,19 @@ pub struct RenderContextVtable {
|
|
| 187 |
pub lock_surface_write: unsafe extern "fastcall" fn(
|
| 188 |
this: *mut RenderContext,
|
| 189 |
result: *mut FastcallResult,
|
| 190 |
-
out_data: *mut
|
| 191 |
out_stride: *mut u32,
|
| 192 |
) -> *mut FastcallResult,
|
| 193 |
/// unlock surface after writing (0x5A2C10 — same fn as slot 16, RET 0x4)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
#[slot(18)]
|
| 195 |
pub unlock_surface_write: unsafe extern "fastcall" fn(
|
| 196 |
this: *mut RenderContext,
|
| 197 |
result: *mut FastcallResult,
|
| 198 |
-
|
| 199 |
) -> *mut FastcallResult,
|
| 200 |
/// fill rectangle — lock, memset rows, unlock (0x5A25C0)
|
| 201 |
///
|
|
@@ -351,6 +355,18 @@ pub struct SurfaceVtable {
|
|
| 351 |
this: *mut Surface,
|
| 352 |
result: *mut FastcallResult,
|
| 353 |
) -> *mut FastcallResult,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
}
|
| 355 |
|
| 356 |
/// Backend-specific surface object created by
|
|
|
|
| 187 |
pub lock_surface_write: unsafe extern "fastcall" fn(
|
| 188 |
this: *mut RenderContext,
|
| 189 |
result: *mut FastcallResult,
|
| 190 |
+
out_data: *mut *mut u8,
|
| 191 |
out_stride: *mut u32,
|
| 192 |
) -> *mut FastcallResult,
|
| 193 |
/// unlock surface after writing (0x5A2C10 — same fn as slot 16, RET 0x4)
|
| 194 |
+
///
|
| 195 |
+
/// `data` is the framebuffer pointer previously returned from
|
| 196 |
+
/// `lock_surface_write`. The original ignores it; we still pass it
|
| 197 |
+
/// through for fidelity.
|
| 198 |
#[slot(18)]
|
| 199 |
pub unlock_surface_write: unsafe extern "fastcall" fn(
|
| 200 |
this: *mut RenderContext,
|
| 201 |
result: *mut FastcallResult,
|
| 202 |
+
data: *mut u8,
|
| 203 |
) -> *mut FastcallResult,
|
| 204 |
/// fill rectangle — lock, memset rows, unlock (0x5A25C0)
|
| 205 |
///
|
|
|
|
| 355 |
this: *mut Surface,
|
| 356 |
result: *mut FastcallResult,
|
| 357 |
) -> *mut FastcallResult,
|
| 358 |
+
/// set color-key (transparency) (slot 7).
|
| 359 |
+
///
|
| 360 |
+
/// Used by `load_sprite_by_name` after `init_surface` to enable
|
| 361 |
+
/// transparency on the freshly-allocated frame surface (color key
|
| 362 |
+
/// `0`, flag `0x10`). The result code is unused by all known callers.
|
| 363 |
+
#[slot(7)]
|
| 364 |
+
pub set_color_key: unsafe extern "fastcall" fn(
|
| 365 |
+
this: *mut Surface,
|
| 366 |
+
result: *mut FastcallResult,
|
| 367 |
+
color_key: u32,
|
| 368 |
+
flags: u32,
|
| 369 |
+
) -> *mut FastcallResult,
|
| 370 |
}
|
| 371 |
|
| 372 |
/// Backend-specific surface object created by
|
|
@@ -211,14 +211,16 @@ unsafe fn free_layer_sprite_table(this: *mut DisplayGfx) {
|
|
| 211 |
unsafe fn layer_sprite_frame_destructor(frame: *mut LayerSpriteFrame) {
|
| 212 |
use crate::render::sprite::sprite::CBITMAP_VTABLE_MAYBE;
|
| 213 |
|
| 214 |
-
(*frame).bitmap_vtable = rb(CBITMAP_VTABLE_MAYBE);
|
| 215 |
-
let surface = (*frame).surface
|
| 216 |
if !surface.is_null() {
|
| 217 |
// surface->vtable[0](this, 1) — scalar deleting destructor.
|
| 218 |
-
|
|
|
|
|
|
|
| 219 |
let dtor: unsafe extern "thiscall" fn(*mut c_void, u32) -> *mut c_void =
|
| 220 |
core::mem::transmute(*vtable);
|
| 221 |
-
dtor(surface, 1);
|
| 222 |
}
|
| 223 |
}
|
| 224 |
|
|
|
|
| 211 |
unsafe fn layer_sprite_frame_destructor(frame: *mut LayerSpriteFrame) {
|
| 212 |
use crate::render::sprite::sprite::CBITMAP_VTABLE_MAYBE;
|
| 213 |
|
| 214 |
+
(*frame).bitmap_vtable = rb(CBITMAP_VTABLE_MAYBE) as *const c_void;
|
| 215 |
+
let surface = (*frame).surface;
|
| 216 |
if !surface.is_null() {
|
| 217 |
// surface->vtable[0](this, 1) — scalar deleting destructor.
|
| 218 |
+
// Surface's vtable doesn't yet expose the destructor as a typed
|
| 219 |
+
// slot (slots 3-7 are typed; slot 0 is class-specific dtor).
|
| 220 |
+
let vtable = (*surface).vtable as *const usize;
|
| 221 |
let dtor: unsafe extern "thiscall" fn(*mut c_void, u32) -> *mut c_void =
|
| 222 |
core::mem::transmute(*vtable);
|
| 223 |
+
dtor(surface as *mut c_void, 1);
|
| 224 |
}
|
| 225 |
}
|
| 226 |
|
|
@@ -398,7 +398,7 @@ pub unsafe fn font_extend(
|
|
| 398 |
font_obj: *mut Font,
|
| 399 |
palette_ctx: *mut PaletteContext,
|
| 400 |
filename: *const c_char,
|
| 401 |
-
char_map: *const
|
| 402 |
palette_value: u32,
|
| 403 |
) {
|
| 404 |
use crate::address::va;
|
|
@@ -407,6 +407,8 @@ pub unsafe fn font_extend(
|
|
| 407 |
use crate::wa_alloc::wa_malloc;
|
| 408 |
use core::ffi::c_char;
|
| 409 |
|
|
|
|
|
|
|
| 410 |
// Open the .fex file via WA's CRT _fopen.
|
| 411 |
let fopen: unsafe extern "cdecl" fn(*const c_char, *const c_char) -> *mut u8 =
|
| 412 |
core::mem::transmute(rb(va::WA_FOPEN) as usize);
|
|
|
|
| 398 |
font_obj: *mut Font,
|
| 399 |
palette_ctx: *mut PaletteContext,
|
| 400 |
filename: *const c_char,
|
| 401 |
+
char_map: *const c_char,
|
| 402 |
palette_value: u32,
|
| 403 |
) {
|
| 404 |
use crate::address::va;
|
|
|
|
| 407 |
use crate::wa_alloc::wa_malloc;
|
| 408 |
use core::ffi::c_char;
|
| 409 |
|
| 410 |
+
let char_map = char_map as *const u8;
|
| 411 |
+
|
| 412 |
// Open the .fex file via WA's CRT _fopen.
|
| 413 |
let fopen: unsafe extern "cdecl" fn(*const c_char, *const c_char) -> *mut u8 =
|
| 414 |
core::mem::transmute(rb(va::WA_FOPEN) as usize);
|
|
@@ -255,7 +255,8 @@ pub struct DisplayGfxVtable {
|
|
| 255 |
pub flush_render: fn(this: *mut DisplayGfx),
|
| 256 |
/// set camera offset (0x56CC40, RET 0x8)
|
| 257 |
///
|
| 258 |
-
/// Fixed-point input; internally `>> 16` to pixel integers stored
|
|
|
|
| 259 |
#[slot(27)]
|
| 260 |
pub set_camera_offset: fn(this: *mut DisplayGfx, x: Fixed, y: Fixed),
|
| 261 |
/// set clip rectangle (0x56CC60, RET 0x10)
|
|
@@ -315,7 +316,7 @@ pub struct DisplayGfxVtable {
|
|
| 315 |
this: *mut DisplayGfx,
|
| 316 |
font_id: i32,
|
| 317 |
path: *const c_char,
|
| 318 |
-
char_map: *const
|
| 319 |
palette_value: u32,
|
| 320 |
flag: i32,
|
| 321 |
) -> u32,
|
|
@@ -448,7 +449,7 @@ pub unsafe extern "thiscall" fn set_clip_rect(
|
|
| 448 |
unsafe fn flush_render_lock(gfx: *mut DisplayGfx) {
|
| 449 |
if (*gfx).render_lock != 0 {
|
| 450 |
let wrapper = *(rb(va::G_RENDER_CONTEXT) as *const *mut RenderContext);
|
| 451 |
-
let data = (*(*gfx).layer_0).data
|
| 452 |
let mut buf = FastcallResult::default();
|
| 453 |
RenderContext::unlock_surface_write_raw(wrapper, &mut buf, data);
|
| 454 |
(*gfx).render_lock = 0;
|
|
@@ -469,7 +470,7 @@ pub unsafe fn acquire_render_lock(gfx: *mut DisplayGfx) {
|
|
| 469 |
let fb_width = dims[0];
|
| 470 |
let fb_height = dims[1];
|
| 471 |
|
| 472 |
-
let mut data_ptr:
|
| 473 |
let mut stride: u32 = 0;
|
| 474 |
RenderContext::lock_surface_write_raw(wrapper, &mut buf, &mut data_ptr, &mut stride);
|
| 475 |
|
|
@@ -477,7 +478,7 @@ pub unsafe fn acquire_render_lock(gfx: *mut DisplayGfx) {
|
|
| 477 |
if (*layer).external_buffer != 0 {
|
| 478 |
(*layer).width = fb_width;
|
| 479 |
(*layer).height = fb_height;
|
| 480 |
-
(*layer).data = data_ptr
|
| 481 |
(*layer).row_stride = stride;
|
| 482 |
(*layer).clip_left = 0;
|
| 483 |
(*layer).clip_top = 0;
|
|
@@ -760,12 +761,28 @@ pub unsafe extern "thiscall" fn is_sprite_loaded(this: *mut DisplayGfx, id: i32)
|
|
| 760 |
}
|
| 761 |
}
|
| 762 |
|
| 763 |
-
///
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 764 |
#[inline]
|
| 765 |
fn is_valid_sprite_id(id: i32) -> bool {
|
| 766 |
(1..=0x3FF).contains(&id)
|
| 767 |
}
|
| 768 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 769 |
/// Port of DisplayGfx::GetSpriteInfo (slot 6, 0x523500).
|
| 770 |
///
|
| 771 |
/// Returns the static "sprite" string (0x664170) on success, or 0 if
|
|
@@ -1506,17 +1523,12 @@ pub unsafe extern "thiscall" fn update_palette(
|
|
| 1506 |
unsafe fn palette_commit(gfx: *mut DisplayGfx) {
|
| 1507 |
let dirty_min = (*gfx).palette_dirty_min;
|
| 1508 |
let dirty_max = (*gfx).palette_dirty_max;
|
| 1509 |
-
palette_commit_bridge(
|
| 1510 |
-
gfx as *mut u8,
|
| 1511 |
-
dirty_min,
|
| 1512 |
-
dirty_max,
|
| 1513 |
-
crate::rebase::rb(0x0056_CD20),
|
| 1514 |
-
);
|
| 1515 |
}
|
| 1516 |
|
| 1517 |
#[unsafe(naked)]
|
| 1518 |
unsafe extern "cdecl" fn palette_commit_bridge(
|
| 1519 |
-
_gfx: *mut
|
| 1520 |
_dirty_min: u32,
|
| 1521 |
_dirty_max: u32,
|
| 1522 |
_target: u32,
|
|
@@ -1589,8 +1601,7 @@ pub unsafe fn load_sprite(
|
|
| 1589 |
layer_ctx: *mut PaletteContext,
|
| 1590 |
) -> i32,
|
| 1591 |
) -> i32 {
|
| 1592 |
-
|
| 1593 |
-
if id & 0x80_0000 != 0 {
|
| 1594 |
return 1;
|
| 1595 |
}
|
| 1596 |
|
|
@@ -1660,14 +1671,12 @@ pub unsafe fn load_sprite_by_name(
|
|
| 1660 |
) -> i32 {
|
| 1661 |
use crate::address::va;
|
| 1662 |
use crate::rebase::rb;
|
| 1663 |
-
use crate::render::display::context::{FastcallResult, RenderContext};
|
| 1664 |
use crate::render::palette::{palette_map_color, remap_pixels_through_lut};
|
| 1665 |
use crate::render::sprite::gfx_dir::{call_gfx_load_image, GfxDirStream};
|
| 1666 |
|
| 1667 |
use crate::wa_alloc::wa_malloc;
|
| 1668 |
|
| 1669 |
-
let sp = sprite as *mut u8;
|
| 1670 |
-
|
| 1671 |
// Copy name into sprite.name (max 0x4F chars + null terminator).
|
| 1672 |
let name_dest = (*sprite).name.as_mut_ptr();
|
| 1673 |
let mut i = 0usize;
|
|
@@ -1732,38 +1741,38 @@ pub unsafe fn load_sprite_by_name(
|
|
| 1732 |
|
| 1733 |
// Sprite metadata fields, in the original's read order
|
| 1734 |
// (field_60 before flags/cell_width/cell_height).
|
| 1735 |
-
GfxDirStream::read_raw(stream,
|
| 1736 |
-
GfxDirStream::read_raw(stream,
|
| 1737 |
-
GfxDirStream::read_raw(stream,
|
| 1738 |
-
GfxDirStream::read_raw(stream,
|
| 1739 |
|
| 1740 |
(*sprite).frame_count = 0;
|
| 1741 |
-
GfxDirStream::read_raw(stream,
|
| 1742 |
let frame_count = (*sprite).frame_count as usize;
|
| 1743 |
|
| 1744 |
-
// Counted LayerSpriteFrame array: 0x14 bytes
|
| 1745 |
-
// 4-byte count prefix at [-4]. Saturate the allocation size on
|
| 1746 |
// overflow to match the original's checked-mul behavior.
|
| 1747 |
-
|
| 1748 |
-
let checked_count =
|
| 1749 |
-
let checked_size = checked_count.checked_mul(
|
| 1750 |
let checked_alloc = checked_size.checked_add(4).unwrap_or(u32::MAX);
|
| 1751 |
|
| 1752 |
let array_base = wa_malloc(checked_alloc);
|
| 1753 |
-
let frame_array = if !array_base.is_null() {
|
| 1754 |
*(array_base as *mut u32) = checked_count;
|
| 1755 |
-
let arr = array_base.add(4);
|
| 1756 |
-
let
|
| 1757 |
-
for j in 0..
|
| 1758 |
-
let elem = arr.add(j
|
| 1759 |
-
|
| 1760 |
-
|
| 1761 |
}
|
| 1762 |
arr
|
| 1763 |
} else {
|
| 1764 |
core::ptr::null_mut()
|
| 1765 |
};
|
| 1766 |
-
(*sprite).frame_array = frame_array
|
| 1767 |
|
| 1768 |
// Skip alignment padding: while (remaining() & 3) != 0, read 1 byte.
|
| 1769 |
loop {
|
|
@@ -1778,13 +1787,13 @@ pub unsafe fn load_sprite_by_name(
|
|
| 1778 |
// Frame headers: 4-byte discarded prefix, then start_x/y, end_x/y.
|
| 1779 |
if frame_count > 0 && !frame_array.is_null() {
|
| 1780 |
for j in 0..frame_count {
|
| 1781 |
-
let
|
| 1782 |
let mut frame_hdr = [0u8; 4];
|
| 1783 |
GfxDirStream::read_raw(stream, frame_hdr.as_mut_ptr(), 4);
|
| 1784 |
-
GfxDirStream::read_raw(stream,
|
| 1785 |
-
GfxDirStream::read_raw(stream,
|
| 1786 |
-
GfxDirStream::read_raw(stream,
|
| 1787 |
-
GfxDirStream::read_raw(stream,
|
| 1788 |
}
|
| 1789 |
}
|
| 1790 |
|
|
@@ -1793,14 +1802,9 @@ pub unsafe fn load_sprite_by_name(
|
|
| 1793 |
let render_ctx = *(rb(va::G_RENDER_CONTEXT) as *const *mut RenderContext);
|
| 1794 |
|
| 1795 |
for j in 0..frame_count {
|
| 1796 |
-
let
|
| 1797 |
-
let
|
| 1798 |
-
let
|
| 1799 |
-
let end_x = *(elem.add(4) as *const i16) as i32;
|
| 1800 |
-
let end_y = *(elem.add(6) as *const i16) as i32;
|
| 1801 |
-
|
| 1802 |
-
let width = end_x - start_x;
|
| 1803 |
-
let height = end_y - start_y;
|
| 1804 |
|
| 1805 |
if width * height == 0 {
|
| 1806 |
continue;
|
|
@@ -1808,84 +1812,41 @@ pub unsafe fn load_sprite_by_name(
|
|
| 1808 |
|
| 1809 |
// alloc_surface returns the surface pointer in EAX, NOT via
|
| 1810 |
// the FastcallResult buffer — see feedback_alloc_surface_return.md.
|
| 1811 |
-
|
| 1812 |
-
if *surface_ptr == 0 {
|
| 1813 |
let mut buf = FastcallResult::default();
|
| 1814 |
let ret = RenderContext::alloc_surface_raw(render_ctx, &mut buf);
|
| 1815 |
-
*
|
| 1816 |
}
|
| 1817 |
-
let surface = *
|
| 1818 |
if surface.is_null() {
|
| 1819 |
continue;
|
| 1820 |
}
|
| 1821 |
|
| 1822 |
-
|
| 1823 |
-
|
| 1824 |
-
|
| 1825 |
-
let init_fn: unsafe extern "fastcall" fn(
|
| 1826 |
-
*mut u8,
|
| 1827 |
-
*mut FastcallResult,
|
| 1828 |
-
u32,
|
| 1829 |
-
u32,
|
| 1830 |
-
u32,
|
| 1831 |
-
) = core::mem::transmute(*vt.add(5));
|
| 1832 |
-
let mut buf = FastcallResult::default();
|
| 1833 |
-
init_fn(surface, &mut buf, width as u32, height as u32, 0);
|
| 1834 |
-
}
|
| 1835 |
-
|
| 1836 |
-
// surface->vtable[7](0, 0x10) — SetColorKey.
|
| 1837 |
-
{
|
| 1838 |
-
let vt = *(surface as *const *const u32);
|
| 1839 |
-
let set_ck_fn: unsafe extern "fastcall" fn(
|
| 1840 |
-
*mut u8,
|
| 1841 |
-
*mut FastcallResult,
|
| 1842 |
-
u32,
|
| 1843 |
-
u32,
|
| 1844 |
-
) = core::mem::transmute(*vt.add(7));
|
| 1845 |
-
let mut buf = FastcallResult::default();
|
| 1846 |
-
set_ck_fn(surface, &mut buf, 0, 0x10);
|
| 1847 |
-
}
|
| 1848 |
|
| 1849 |
-
|
| 1850 |
-
let mut
|
| 1851 |
-
|
| 1852 |
-
{
|
| 1853 |
-
let vt = *(surface as *const *const u32);
|
| 1854 |
-
let lock_fn: unsafe extern "fastcall" fn(
|
| 1855 |
-
*mut u8,
|
| 1856 |
-
*mut FastcallResult,
|
| 1857 |
-
*mut u32,
|
| 1858 |
-
*mut u32,
|
| 1859 |
-
) = core::mem::transmute(*vt.add(3));
|
| 1860 |
-
let mut buf = FastcallResult::default();
|
| 1861 |
-
lock_fn(surface, &mut buf, &mut data_ptr, &mut pitch);
|
| 1862 |
-
}
|
| 1863 |
|
| 1864 |
-
if data_ptr
|
| 1865 |
-
let data = data_ptr as *mut u8;
|
| 1866 |
for row in 0..height {
|
| 1867 |
-
let row_dest =
|
| 1868 |
GfxDirStream::read_raw(stream, row_dest, width as u32);
|
| 1869 |
}
|
| 1870 |
|
| 1871 |
let width_dwords = ((width as u32) + 3) / 4;
|
| 1872 |
remap_pixels_through_lut(
|
| 1873 |
-
|
| 1874 |
-
pitch,
|
| 1875 |
palette_lut.as_ptr(),
|
| 1876 |
width_dwords,
|
| 1877 |
height as u32,
|
| 1878 |
);
|
| 1879 |
}
|
| 1880 |
|
| 1881 |
-
|
| 1882 |
-
{
|
| 1883 |
-
let vt = *(surface as *const *const u32);
|
| 1884 |
-
let unlock_fn: unsafe extern "fastcall" fn(*mut u8, *mut FastcallResult, u32) =
|
| 1885 |
-
core::mem::transmute(*vt.add(4));
|
| 1886 |
-
let mut buf = FastcallResult::default();
|
| 1887 |
-
unlock_fn(surface, &mut buf, data_ptr);
|
| 1888 |
-
}
|
| 1889 |
}
|
| 1890 |
}
|
| 1891 |
}
|
|
@@ -1898,18 +1859,21 @@ pub unsafe fn load_sprite_by_name(
|
|
| 1898 |
pub unsafe fn free_layer_sprite(sprite: *mut LayerSprite) {
|
| 1899 |
use crate::wa_alloc::wa_free;
|
| 1900 |
|
| 1901 |
-
let frame_array = (*sprite).frame_array
|
| 1902 |
if !frame_array.is_null() {
|
|
|
|
| 1903 |
let count_ptr = (frame_array as *mut u32).sub(1);
|
| 1904 |
let count = *count_ptr as usize;
|
| 1905 |
|
| 1906 |
// Reverse-order destruction matches eh_vector_destructor_iterator.
|
| 1907 |
for i in (0..count).rev() {
|
| 1908 |
-
let
|
| 1909 |
-
let surface =
|
| 1910 |
-
if surface
|
| 1911 |
-
|
| 1912 |
-
let
|
|
|
|
|
|
|
| 1913 |
dtor(surface, 1);
|
| 1914 |
}
|
| 1915 |
}
|
|
@@ -1934,8 +1898,7 @@ pub unsafe fn load_sprite_by_layer(
|
|
| 1934 |
) -> i32 {
|
| 1935 |
use crate::wa_alloc::wa_malloc_zeroed;
|
| 1936 |
|
| 1937 |
-
|
| 1938 |
-
if id & 0x80_0000 != 0 {
|
| 1939 |
return 1;
|
| 1940 |
}
|
| 1941 |
|
|
@@ -1999,7 +1962,7 @@ pub unsafe fn load_font(
|
|
| 1999 |
return 0;
|
| 2000 |
}
|
| 2001 |
|
| 2002 |
-
if !(
|
| 2003 |
return 0;
|
| 2004 |
}
|
| 2005 |
if !base.font_table[font_id as usize].is_null() {
|
|
@@ -2029,13 +1992,13 @@ pub unsafe fn load_font_extension(
|
|
| 2029 |
this: *mut DisplayGfx,
|
| 2030 |
font_id: i32,
|
| 2031 |
path: *const c_char,
|
| 2032 |
-
char_map: *const
|
| 2033 |
palette_value: u32,
|
| 2034 |
_flag: i32,
|
| 2035 |
) -> u32 {
|
| 2036 |
use crate::render::palette::palette_context_lookup_entry;
|
| 2037 |
|
| 2038 |
-
if !(
|
| 2039 |
return 0;
|
| 2040 |
}
|
| 2041 |
let base = &mut (*this).base;
|
|
@@ -2074,7 +2037,7 @@ pub unsafe extern "thiscall" fn get_font_info(
|
|
| 2074 |
out_1: *mut u32,
|
| 2075 |
out_2: *mut u32,
|
| 2076 |
) -> u32 {
|
| 2077 |
-
if !(
|
| 2078 |
return 0;
|
| 2079 |
}
|
| 2080 |
let font_obj = (*this).base.font_table[font_id as usize] as *const Font;
|
|
@@ -2095,7 +2058,7 @@ pub unsafe extern "thiscall" fn get_font_metric(
|
|
| 2095 |
out_1: *mut u32,
|
| 2096 |
out_2: *mut u32,
|
| 2097 |
) -> u32 {
|
| 2098 |
-
if !(
|
| 2099 |
return 0;
|
| 2100 |
}
|
| 2101 |
let font_obj = (*this).base.font_table[font_id as usize] as *const Font;
|
|
@@ -2121,7 +2084,7 @@ pub unsafe extern "thiscall" fn set_font_param(
|
|
| 2121 |
p4: u32,
|
| 2122 |
p5: u32,
|
| 2123 |
) -> u32 {
|
| 2124 |
-
if !(
|
| 2125 |
return 0;
|
| 2126 |
}
|
| 2127 |
let font_obj = (*this).base.font_table[font_id as usize] as *const Font;
|
|
@@ -2198,11 +2161,12 @@ pub unsafe fn font_draw_text_impl(
|
|
| 2198 |
bitmap: *const BitGrid,
|
| 2199 |
pen_x: i32,
|
| 2200 |
pen_y: i32,
|
| 2201 |
-
msg: *const
|
| 2202 |
out_pen_x: *mut i32,
|
| 2203 |
out_width: *mut i32,
|
| 2204 |
font_id_high: i32,
|
| 2205 |
) -> i32 {
|
|
|
|
| 2206 |
let font = &*font_obj;
|
| 2207 |
let font_width = font.width as i16 as i32;
|
| 2208 |
|
|
@@ -2339,8 +2303,8 @@ pub unsafe extern "thiscall" fn draw_text_on_bitmap(
|
|
| 2339 |
out_pen_x: *mut i32,
|
| 2340 |
out_width: *mut i32,
|
| 2341 |
) -> i32 {
|
| 2342 |
-
let font_id_low =
|
| 2343 |
-
if !(
|
| 2344 |
return 0;
|
| 2345 |
}
|
| 2346 |
let font_obj = (*this).base.font_table[font_id_low as usize] as *const Font;
|
|
@@ -2353,7 +2317,7 @@ pub unsafe extern "thiscall" fn draw_text_on_bitmap(
|
|
| 2353 |
bitmap as *const BitGrid,
|
| 2354 |
pen_x,
|
| 2355 |
pen_y,
|
| 2356 |
-
msg
|
| 2357 |
out_pen_x,
|
| 2358 |
out_width,
|
| 2359 |
font_id_high,
|
|
|
|
| 255 |
pub flush_render: fn(this: *mut DisplayGfx),
|
| 256 |
/// set camera offset (0x56CC40, RET 0x8)
|
| 257 |
///
|
| 258 |
+
/// Fixed-point input; internally `>> 16` to pixel integers stored
|
| 259 |
+
/// in `DisplayGfx::camera_x` / `camera_y`.
|
| 260 |
#[slot(27)]
|
| 261 |
pub set_camera_offset: fn(this: *mut DisplayGfx, x: Fixed, y: Fixed),
|
| 262 |
/// set clip rectangle (0x56CC60, RET 0x10)
|
|
|
|
| 316 |
this: *mut DisplayGfx,
|
| 317 |
font_id: i32,
|
| 318 |
path: *const c_char,
|
| 319 |
+
char_map: *const c_char,
|
| 320 |
palette_value: u32,
|
| 321 |
flag: i32,
|
| 322 |
) -> u32,
|
|
|
|
| 449 |
unsafe fn flush_render_lock(gfx: *mut DisplayGfx) {
|
| 450 |
if (*gfx).render_lock != 0 {
|
| 451 |
let wrapper = *(rb(va::G_RENDER_CONTEXT) as *const *mut RenderContext);
|
| 452 |
+
let data = (*(*gfx).layer_0).data;
|
| 453 |
let mut buf = FastcallResult::default();
|
| 454 |
RenderContext::unlock_surface_write_raw(wrapper, &mut buf, data);
|
| 455 |
(*gfx).render_lock = 0;
|
|
|
|
| 470 |
let fb_width = dims[0];
|
| 471 |
let fb_height = dims[1];
|
| 472 |
|
| 473 |
+
let mut data_ptr: *mut u8 = core::ptr::null_mut();
|
| 474 |
let mut stride: u32 = 0;
|
| 475 |
RenderContext::lock_surface_write_raw(wrapper, &mut buf, &mut data_ptr, &mut stride);
|
| 476 |
|
|
|
|
| 478 |
if (*layer).external_buffer != 0 {
|
| 479 |
(*layer).width = fb_width;
|
| 480 |
(*layer).height = fb_height;
|
| 481 |
+
(*layer).data = data_ptr;
|
| 482 |
(*layer).row_stride = stride;
|
| 483 |
(*layer).clip_left = 0;
|
| 484 |
(*layer).clip_top = 0;
|
|
|
|
| 761 |
}
|
| 762 |
}
|
| 763 |
|
| 764 |
+
/// Maximum valid font slot index — `font_table` has 32 entries, slot 0
|
| 765 |
+
/// is reserved (the original validates `1..=31` everywhere).
|
| 766 |
+
pub const MAX_FONT_ID: i32 = 31;
|
| 767 |
+
|
| 768 |
+
/// Sentinel bit on the `id` parameter of `load_sprite` / `load_sprite_by_layer`
|
| 769 |
+
/// meaning "already loaded — return success without doing any work".
|
| 770 |
+
const SPRITE_LOAD_ALREADY_DONE: u32 = 0x0080_0000;
|
| 771 |
+
|
| 772 |
+
/// Check if a sprite ID is in the valid range `[1, 0x3FF]`. Note that
|
| 773 |
+
/// the slot 33 dispatcher uses a slightly tighter bound (`1..=0x3FE`)
|
| 774 |
+
/// matching the original — see `get_sprite_frame_for_blit`.
|
| 775 |
#[inline]
|
| 776 |
fn is_valid_sprite_id(id: i32) -> bool {
|
| 777 |
(1..=0x3FF).contains(&id)
|
| 778 |
}
|
| 779 |
|
| 780 |
+
/// Check if `font_id` is a valid 1-based index into `font_table`.
|
| 781 |
+
#[inline]
|
| 782 |
+
fn is_valid_font_id(id: i32) -> bool {
|
| 783 |
+
(1..=MAX_FONT_ID).contains(&id)
|
| 784 |
+
}
|
| 785 |
+
|
| 786 |
/// Port of DisplayGfx::GetSpriteInfo (slot 6, 0x523500).
|
| 787 |
///
|
| 788 |
/// Returns the static "sprite" string (0x664170) on success, or 0 if
|
|
|
|
| 1523 |
unsafe fn palette_commit(gfx: *mut DisplayGfx) {
|
| 1524 |
let dirty_min = (*gfx).palette_dirty_min;
|
| 1525 |
let dirty_max = (*gfx).palette_dirty_max;
|
| 1526 |
+
palette_commit_bridge(gfx, dirty_min, dirty_max, crate::rebase::rb(0x0056_CD20));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1527 |
}
|
| 1528 |
|
| 1529 |
#[unsafe(naked)]
|
| 1530 |
unsafe extern "cdecl" fn palette_commit_bridge(
|
| 1531 |
+
_gfx: *mut DisplayGfx,
|
| 1532 |
_dirty_min: u32,
|
| 1533 |
_dirty_max: u32,
|
| 1534 |
_target: u32,
|
|
|
|
| 1601 |
layer_ctx: *mut PaletteContext,
|
| 1602 |
) -> i32,
|
| 1603 |
) -> i32 {
|
| 1604 |
+
if id & SPRITE_LOAD_ALREADY_DONE != 0 {
|
|
|
|
| 1605 |
return 1;
|
| 1606 |
}
|
| 1607 |
|
|
|
|
| 1671 |
) -> i32 {
|
| 1672 |
use crate::address::va;
|
| 1673 |
use crate::rebase::rb;
|
| 1674 |
+
use crate::render::display::context::{FastcallResult, RenderContext, Surface};
|
| 1675 |
use crate::render::palette::{palette_map_color, remap_pixels_through_lut};
|
| 1676 |
use crate::render::sprite::gfx_dir::{call_gfx_load_image, GfxDirStream};
|
| 1677 |
|
| 1678 |
use crate::wa_alloc::wa_malloc;
|
| 1679 |
|
|
|
|
|
|
|
| 1680 |
// Copy name into sprite.name (max 0x4F chars + null terminator).
|
| 1681 |
let name_dest = (*sprite).name.as_mut_ptr();
|
| 1682 |
let mut i = 0usize;
|
|
|
|
| 1741 |
|
| 1742 |
// Sprite metadata fields, in the original's read order
|
| 1743 |
// (field_60 before flags/cell_width/cell_height).
|
| 1744 |
+
GfxDirStream::read_raw(stream, &raw mut (*sprite).field_60 as *mut u8, 4);
|
| 1745 |
+
GfxDirStream::read_raw(stream, &raw mut (*sprite).flags as *mut u8, 2);
|
| 1746 |
+
GfxDirStream::read_raw(stream, &raw mut (*sprite).cell_width as *mut u8, 2);
|
| 1747 |
+
GfxDirStream::read_raw(stream, &raw mut (*sprite).cell_height as *mut u8, 2);
|
| 1748 |
|
| 1749 |
(*sprite).frame_count = 0;
|
| 1750 |
+
GfxDirStream::read_raw(stream, &raw mut (*sprite).frame_count as *mut u8, 2);
|
| 1751 |
let frame_count = (*sprite).frame_count as usize;
|
| 1752 |
|
| 1753 |
+
// Counted `LayerSpriteFrame` array: `count * 0x14` bytes plus a
|
| 1754 |
+
// 4-byte count prefix at `[-4]`. Saturate the allocation size on
|
| 1755 |
// overflow to match the original's checked-mul behavior.
|
| 1756 |
+
const LSF_SIZE: u32 = core::mem::size_of::<LayerSpriteFrame>() as u32;
|
| 1757 |
+
let checked_count = frame_count as u32;
|
| 1758 |
+
let checked_size = checked_count.checked_mul(LSF_SIZE).unwrap_or(u32::MAX);
|
| 1759 |
let checked_alloc = checked_size.checked_add(4).unwrap_or(u32::MAX);
|
| 1760 |
|
| 1761 |
let array_base = wa_malloc(checked_alloc);
|
| 1762 |
+
let frame_array: *mut LayerSpriteFrame = if !array_base.is_null() {
|
| 1763 |
*(array_base as *mut u32) = checked_count;
|
| 1764 |
+
let arr = array_base.add(4) as *mut LayerSpriteFrame;
|
| 1765 |
+
let cbitmap_vt = rb(va::CBITMAP_VTABLE_MAYBE) as *const core::ffi::c_void;
|
| 1766 |
+
for j in 0..frame_count {
|
| 1767 |
+
let elem = arr.add(j);
|
| 1768 |
+
(*elem).bitmap_vtable = cbitmap_vt;
|
| 1769 |
+
(*elem).surface = core::ptr::null_mut();
|
| 1770 |
}
|
| 1771 |
arr
|
| 1772 |
} else {
|
| 1773 |
core::ptr::null_mut()
|
| 1774 |
};
|
| 1775 |
+
(*sprite).frame_array = frame_array;
|
| 1776 |
|
| 1777 |
// Skip alignment padding: while (remaining() & 3) != 0, read 1 byte.
|
| 1778 |
loop {
|
|
|
|
| 1787 |
// Frame headers: 4-byte discarded prefix, then start_x/y, end_x/y.
|
| 1788 |
if frame_count > 0 && !frame_array.is_null() {
|
| 1789 |
for j in 0..frame_count {
|
| 1790 |
+
let frame = frame_array.add(j);
|
| 1791 |
let mut frame_hdr = [0u8; 4];
|
| 1792 |
GfxDirStream::read_raw(stream, frame_hdr.as_mut_ptr(), 4);
|
| 1793 |
+
GfxDirStream::read_raw(stream, &raw mut (*frame).start_x as *mut u8, 2);
|
| 1794 |
+
GfxDirStream::read_raw(stream, &raw mut (*frame).start_y as *mut u8, 2);
|
| 1795 |
+
GfxDirStream::read_raw(stream, &raw mut (*frame).end_x as *mut u8, 2);
|
| 1796 |
+
GfxDirStream::read_raw(stream, &raw mut (*frame).end_y as *mut u8, 2);
|
| 1797 |
}
|
| 1798 |
}
|
| 1799 |
|
|
|
|
| 1802 |
let render_ctx = *(rb(va::G_RENDER_CONTEXT) as *const *mut RenderContext);
|
| 1803 |
|
| 1804 |
for j in 0..frame_count {
|
| 1805 |
+
let frame = frame_array.add(j);
|
| 1806 |
+
let width = ((*frame).end_x - (*frame).start_x) as i32;
|
| 1807 |
+
let height = ((*frame).end_y - (*frame).start_y) as i32;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1808 |
|
| 1809 |
if width * height == 0 {
|
| 1810 |
continue;
|
|
|
|
| 1812 |
|
| 1813 |
// alloc_surface returns the surface pointer in EAX, NOT via
|
| 1814 |
// the FastcallResult buffer — see feedback_alloc_surface_return.md.
|
| 1815 |
+
if (*frame).surface.is_null() {
|
|
|
|
| 1816 |
let mut buf = FastcallResult::default();
|
| 1817 |
let ret = RenderContext::alloc_surface_raw(render_ctx, &mut buf);
|
| 1818 |
+
(*frame).surface = ret as *mut Surface;
|
| 1819 |
}
|
| 1820 |
+
let surface = (*frame).surface;
|
| 1821 |
if surface.is_null() {
|
| 1822 |
continue;
|
| 1823 |
}
|
| 1824 |
|
| 1825 |
+
let mut buf = FastcallResult::default();
|
| 1826 |
+
Surface::init_surface_raw(surface, &mut buf, width, height, 0);
|
| 1827 |
+
Surface::set_color_key_raw(surface, &mut buf, 0, 0x10);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1828 |
|
| 1829 |
+
let mut data_ptr: *mut u8 = core::ptr::null_mut();
|
| 1830 |
+
let mut pitch: i32 = 0;
|
| 1831 |
+
Surface::lock_surface_raw(surface, &mut buf, &mut data_ptr, &mut pitch);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1832 |
|
| 1833 |
+
if !data_ptr.is_null() && pitch != 0 {
|
|
|
|
| 1834 |
for row in 0..height {
|
| 1835 |
+
let row_dest = data_ptr.add((row * pitch) as usize);
|
| 1836 |
GfxDirStream::read_raw(stream, row_dest, width as u32);
|
| 1837 |
}
|
| 1838 |
|
| 1839 |
let width_dwords = ((width as u32) + 3) / 4;
|
| 1840 |
remap_pixels_through_lut(
|
| 1841 |
+
data_ptr,
|
| 1842 |
+
pitch as u32,
|
| 1843 |
palette_lut.as_ptr(),
|
| 1844 |
width_dwords,
|
| 1845 |
height as u32,
|
| 1846 |
);
|
| 1847 |
}
|
| 1848 |
|
| 1849 |
+
Surface::unlock_surface_raw(surface, &mut buf, data_ptr);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1850 |
}
|
| 1851 |
}
|
| 1852 |
}
|
|
|
|
| 1859 |
pub unsafe fn free_layer_sprite(sprite: *mut LayerSprite) {
|
| 1860 |
use crate::wa_alloc::wa_free;
|
| 1861 |
|
| 1862 |
+
let frame_array = (*sprite).frame_array;
|
| 1863 |
if !frame_array.is_null() {
|
| 1864 |
+
// Counted array: count lives at `frame_array[-4]`.
|
| 1865 |
let count_ptr = (frame_array as *mut u32).sub(1);
|
| 1866 |
let count = *count_ptr as usize;
|
| 1867 |
|
| 1868 |
// Reverse-order destruction matches eh_vector_destructor_iterator.
|
| 1869 |
for i in (0..count).rev() {
|
| 1870 |
+
let frame = frame_array.add(i);
|
| 1871 |
+
let surface = (*frame).surface;
|
| 1872 |
+
if !surface.is_null() {
|
| 1873 |
+
// Surface destructor is vtable[0]; not yet a typed slot.
|
| 1874 |
+
let vt = (*surface).vtable as *const usize;
|
| 1875 |
+
let dtor: unsafe extern "thiscall" fn(*mut Surface, u32) =
|
| 1876 |
+
core::mem::transmute(*vt);
|
| 1877 |
dtor(surface, 1);
|
| 1878 |
}
|
| 1879 |
}
|
|
|
|
| 1898 |
) -> i32 {
|
| 1899 |
use crate::wa_alloc::wa_malloc_zeroed;
|
| 1900 |
|
| 1901 |
+
if id & SPRITE_LOAD_ALREADY_DONE != 0 {
|
|
|
|
| 1902 |
return 1;
|
| 1903 |
}
|
| 1904 |
|
|
|
|
| 1962 |
return 0;
|
| 1963 |
}
|
| 1964 |
|
| 1965 |
+
if !is_valid_font_id(font_id) {
|
| 1966 |
return 0;
|
| 1967 |
}
|
| 1968 |
if !base.font_table[font_id as usize].is_null() {
|
|
|
|
| 1992 |
this: *mut DisplayGfx,
|
| 1993 |
font_id: i32,
|
| 1994 |
path: *const c_char,
|
| 1995 |
+
char_map: *const c_char,
|
| 1996 |
palette_value: u32,
|
| 1997 |
_flag: i32,
|
| 1998 |
) -> u32 {
|
| 1999 |
use crate::render::palette::palette_context_lookup_entry;
|
| 2000 |
|
| 2001 |
+
if !is_valid_font_id(font_id) {
|
| 2002 |
return 0;
|
| 2003 |
}
|
| 2004 |
let base = &mut (*this).base;
|
|
|
|
| 2037 |
out_1: *mut u32,
|
| 2038 |
out_2: *mut u32,
|
| 2039 |
) -> u32 {
|
| 2040 |
+
if !is_valid_font_id(font_id) {
|
| 2041 |
return 0;
|
| 2042 |
}
|
| 2043 |
let font_obj = (*this).base.font_table[font_id as usize] as *const Font;
|
|
|
|
| 2058 |
out_1: *mut u32,
|
| 2059 |
out_2: *mut u32,
|
| 2060 |
) -> u32 {
|
| 2061 |
+
if !is_valid_font_id(font_id) {
|
| 2062 |
return 0;
|
| 2063 |
}
|
| 2064 |
let font_obj = (*this).base.font_table[font_id as usize] as *const Font;
|
|
|
|
| 2084 |
p4: u32,
|
| 2085 |
p5: u32,
|
| 2086 |
) -> u32 {
|
| 2087 |
+
if !is_valid_font_id(font_id) {
|
| 2088 |
return 0;
|
| 2089 |
}
|
| 2090 |
let font_obj = (*this).base.font_table[font_id as usize] as *const Font;
|
|
|
|
| 2161 |
bitmap: *const BitGrid,
|
| 2162 |
pen_x: i32,
|
| 2163 |
pen_y: i32,
|
| 2164 |
+
msg: *const c_char,
|
| 2165 |
out_pen_x: *mut i32,
|
| 2166 |
out_width: *mut i32,
|
| 2167 |
font_id_high: i32,
|
| 2168 |
) -> i32 {
|
| 2169 |
+
let msg = msg as *const u8;
|
| 2170 |
let font = &*font_obj;
|
| 2171 |
let font_width = font.width as i16 as i32;
|
| 2172 |
|
|
|
|
| 2303 |
out_pen_x: *mut i32,
|
| 2304 |
out_width: *mut i32,
|
| 2305 |
) -> i32 {
|
| 2306 |
+
let font_id_low = font_id & 0xFFFF;
|
| 2307 |
+
if !is_valid_font_id(font_id_low) {
|
| 2308 |
return 0;
|
| 2309 |
}
|
| 2310 |
let font_obj = (*this).base.font_table[font_id_low as usize] as *const Font;
|
|
|
|
| 2317 |
bitmap as *const BitGrid,
|
| 2318 |
pen_x,
|
| 2319 |
pen_y,
|
| 2320 |
+
msg,
|
| 2321 |
out_pen_x,
|
| 2322 |
out_width,
|
| 2323 |
font_id_high,
|
|
@@ -473,10 +473,10 @@ pub struct LayerSpriteFrame {
|
|
| 473 |
pub end_x: i16,
|
| 474 |
/// 0x06: Frame end Y coordinate.
|
| 475 |
pub end_y: i16,
|
| 476 |
-
/// 0x08: CBitmap vtable pointer (set by constructor).
|
| 477 |
-
pub bitmap_vtable:
|
| 478 |
-
/// 0x0C:
|
| 479 |
-
pub surface:
|
| 480 |
/// 0x10: Reserved/padding.
|
| 481 |
pub _pad_10: u32,
|
| 482 |
}
|
|
|
|
| 473 |
pub end_x: i16,
|
| 474 |
/// 0x06: Frame end Y coordinate.
|
| 475 |
pub end_y: i16,
|
| 476 |
+
/// 0x08: CBitmap vtable pointer (`0x643F64`, set by constructor).
|
| 477 |
+
pub bitmap_vtable: *const core::ffi::c_void,
|
| 478 |
+
/// 0x0C: Backend surface object pointer (lazy via `alloc_surface`).
|
| 479 |
+
pub surface: *mut crate::render::display::context::Surface,
|
| 480 |
/// 0x10: Reserved/padding.
|
| 481 |
pub _pad_10: u32,
|
| 482 |
}
|
|
@@ -1284,7 +1284,7 @@ unsafe extern "thiscall" fn load_font_extension(
|
|
| 1284 |
this: *mut DisplayGfx,
|
| 1285 |
font_id: i32,
|
| 1286 |
path: *const c_char,
|
| 1287 |
-
char_map: *const
|
| 1288 |
palette_value: u32,
|
| 1289 |
flag: i32,
|
| 1290 |
) -> u32 {
|
|
|
|
| 1284 |
this: *mut DisplayGfx,
|
| 1285 |
font_id: i32,
|
| 1286 |
path: *const c_char,
|
| 1287 |
+
char_map: *const c_char,
|
| 1288 |
palette_value: u32,
|
| 1289 |
flag: i32,
|
| 1290 |
) -> u32 {
|