feat: port DisplayGfx::LoadFont (slot 34) to pure Rust
Browse filesAdds FontObject (0x1C), GlyphEntry (0xC) and FntHeader (0xC) structs and
ports the .fnt loader chain (FUN_004f9940 + FUN_004f99d0) and the slot 34
wrapper. Replaces the last bridged font-loading path so DisplayGfx vtable
patches now cover 30 of 38 slots.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/render/display/vtable.rs
CHANGED
|
@@ -2024,3 +2024,318 @@ pub unsafe fn load_sprite_by_layer(
|
|
| 2024 |
// LoadSpriteComplex (vtable slot 33, 0x5237C0) is NOT ported β the internal
|
| 2025 |
// dispatch functions (0x4FAD30, 0x4F9710) use ESI for sprite/bank pointers
|
| 2026 |
// in complex usercall conventions that are impractical to bridge.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2024 |
// LoadSpriteComplex (vtable slot 33, 0x5237C0) is NOT ported β the internal
|
| 2025 |
// dispatch functions (0x4FAD30, 0x4F9710) use ESI for sprite/bank pointers
|
| 2026 |
// in complex usercall conventions that are impractical to bridge.
|
| 2027 |
+
|
| 2028 |
+
// =========================================================================
|
| 2029 |
+
// Font loading
|
| 2030 |
+
// =========================================================================
|
| 2031 |
+
|
| 2032 |
+
/// Font object β 0x1C bytes.
|
| 2033 |
+
///
|
| 2034 |
+
/// Allocated by `load_font` (vtable slot 34) from a `.fnt` file loaded via
|
| 2035 |
+
/// GfxDir. Referenced from `DisplayBase::font_table` (offset 0x309C). Read by
|
| 2036 |
+
/// `Font__GetInfo` / `Font__GetMetric` / `Font__DrawText` / `Font__SetPalette`
|
| 2037 |
+
/// to render bitmap text.
|
| 2038 |
+
///
|
| 2039 |
+
/// Field semantics derived from `FUN_004f99d0` (the parser) and `Font__GetInfo`
|
| 2040 |
+
/// (0x4fa7d0, which confirms width at +0, width_div_5 at +2, glyph count at +4,
|
| 2041 |
+
/// and glyph table at +8 with 12-byte entries whose byte +4 is a max metric).
|
| 2042 |
+
#[repr(C)]
|
| 2043 |
+
pub struct FontObject {
|
| 2044 |
+
/// 0x00: Font max width in pixels (read from .fnt metadata).
|
| 2045 |
+
pub width: u16,
|
| 2046 |
+
/// 0x02: `width / 5 + 1` β initial max metric seed in `Font__GetInfo`.
|
| 2047 |
+
pub width_div_5: u16,
|
| 2048 |
+
/// 0x04: Glyph count / font height.
|
| 2049 |
+
pub height: u16,
|
| 2050 |
+
/// 0x06: Duplicate of height.
|
| 2051 |
+
pub _height2: u16,
|
| 2052 |
+
/// 0x08: Pointer to glyph table (height entries of 12 bytes each).
|
| 2053 |
+
pub glyph_table: *mut GlyphEntry,
|
| 2054 |
+
/// 0x0C: Pointer to end-of-palette position in the source buffer.
|
| 2055 |
+
pub palette_end: *mut u8,
|
| 2056 |
+
/// 0x10: Pointer to remapped pixel data in the source buffer.
|
| 2057 |
+
pub pixel_data: *mut u8,
|
| 2058 |
+
/// 0x14: Zeroed by `load_font`; purpose unknown.
|
| 2059 |
+
pub _unknown_14: u32,
|
| 2060 |
+
/// 0x18: Zeroed by `load_font`; purpose unknown.
|
| 2061 |
+
pub _unknown_18: u32,
|
| 2062 |
+
}
|
| 2063 |
+
|
| 2064 |
+
const _: () = assert!(core::mem::size_of::<FontObject>() == 0x1C);
|
| 2065 |
+
|
| 2066 |
+
/// Per-glyph metadata in a `.fnt` file (12 bytes).
|
| 2067 |
+
///
|
| 2068 |
+
/// Indexed by `FontObject::glyph_table`. `Font__GetInfo` (0x4fa7d0) reads
|
| 2069 |
+
/// `max_metric` at +0x4 to compute the maximum advance; the other fields are
|
| 2070 |
+
/// not yet exercised from Rust. Layout matches the loop stride of 0xC used by
|
| 2071 |
+
/// both the parser and `Font__GetInfo`.
|
| 2072 |
+
#[repr(C)]
|
| 2073 |
+
pub struct GlyphEntry {
|
| 2074 |
+
/// 0x00: Unknown / start coordinates.
|
| 2075 |
+
pub _unknown_00: u32,
|
| 2076 |
+
/// 0x04: Max metric seed read by `Font__GetInfo` (used as `max(current, val + 1)`).
|
| 2077 |
+
pub max_metric: u16,
|
| 2078 |
+
/// 0x06: Remaining per-glyph fields (bearing / advance / pixel offset).
|
| 2079 |
+
pub _unknown_06: [u8; 6],
|
| 2080 |
+
}
|
| 2081 |
+
|
| 2082 |
+
const _: () = assert!(core::mem::size_of::<GlyphEntry>() == 0xC);
|
| 2083 |
+
|
| 2084 |
+
/// Fixed prefix of a `.fnt` binary blob (0xC bytes).
|
| 2085 |
+
///
|
| 2086 |
+
/// Followed in memory by: packed RGB triplets (`palette_count * 3` bytes),
|
| 2087 |
+
/// a 0x100-byte character-width table, `max_width: i16`, `glyph_count: u16`,
|
| 2088 |
+
/// 4-byte alignment padding, `GlyphEntry[glyph_count]`, then the packed
|
| 2089 |
+
/// glyph pixel data. `font_parse_data` walks this variable-length tail using
|
| 2090 |
+
/// byte pointers rather than a single `#[repr(C)]` struct.
|
| 2091 |
+
#[repr(C)]
|
| 2092 |
+
pub struct FntHeader {
|
| 2093 |
+
/// 0x00: Unknown / version.
|
| 2094 |
+
pub _unknown_00: u32,
|
| 2095 |
+
/// 0x04: Total size of this record in bytes, including this field.
|
| 2096 |
+
pub data_size: i32,
|
| 2097 |
+
/// 0x08: Unknown u16 (skipped by the parser).
|
| 2098 |
+
pub _unknown_08: u16,
|
| 2099 |
+
/// 0x0A: Number of palette entries that follow this header.
|
| 2100 |
+
pub palette_count: u16,
|
| 2101 |
+
// +0x0C: packed RGB triplets (variable length)
|
| 2102 |
+
}
|
| 2103 |
+
|
| 2104 |
+
const _: () = assert!(core::mem::size_of::<FntHeader>() == 0xC);
|
| 2105 |
+
|
| 2106 |
+
/// Port of `FUN_004f99d0` β parses `.fnt` binary data into a `FontObject`.
|
| 2107 |
+
///
|
| 2108 |
+
/// Original convention: cdecl with 1 stack arg (buffer), `unaff_EDI = font_obj`.
|
| 2109 |
+
/// Ported as a plain Rust function.
|
| 2110 |
+
///
|
| 2111 |
+
/// Buffer layout:
|
| 2112 |
+
/// ```text
|
| 2113 |
+
/// +0x00 u32 (unknown / version)
|
| 2114 |
+
/// +0x04 i32 data_size (total bytes in this record, including this field)
|
| 2115 |
+
/// +0x08 u16 (unknown)
|
| 2116 |
+
/// +0x0A u16 palette_count
|
| 2117 |
+
/// +0x0C RGB[palette_count * 3] packed RGB triplets
|
| 2118 |
+
/// +... u8[0x100] character-width table (1 byte per codepoint)
|
| 2119 |
+
/// +0x100 u16 max_width
|
| 2120 |
+
/// +0x102 u16 glyph_count / height
|
| 2121 |
+
/// +... align to 4 from buffer start
|
| 2122 |
+
/// +... GlyphEntry[glyph_count] 12 bytes each (start_x/start_y/end_x/end_y/...)
|
| 2123 |
+
/// +... u8[] packed 4bpp glyph pixels (remapped in place via palette LUT)
|
| 2124 |
+
/// ```
|
| 2125 |
+
///
|
| 2126 |
+
/// # Safety
|
| 2127 |
+
/// `font_obj` must be a valid writable `FontObject`. `header` must point to a
|
| 2128 |
+
/// freshly-loaded `.fnt` blob at least `header.data_size` bytes long and
|
| 2129 |
+
/// followed in memory by the variable-length tail described above.
|
| 2130 |
+
/// `palette_ctx` must be a valid `PaletteContext`.
|
| 2131 |
+
pub unsafe fn font_parse_data(
|
| 2132 |
+
font_obj: *mut FontObject,
|
| 2133 |
+
header: *mut FntHeader,
|
| 2134 |
+
palette_ctx: *mut PaletteContext,
|
| 2135 |
+
) -> u32 {
|
| 2136 |
+
use crate::render::palette::{palette_map_color, remap_pixels_through_lut};
|
| 2137 |
+
|
| 2138 |
+
let data_size = (*header).data_size;
|
| 2139 |
+
let palette_count = (*header).palette_count as i32;
|
| 2140 |
+
let base_bytes = header as *mut u8;
|
| 2141 |
+
|
| 2142 |
+
// Cursor starts at the first byte after the fixed header (the RGB triplets).
|
| 2143 |
+
let mut cursor = base_bytes.add(core::mem::size_of::<FntHeader>());
|
| 2144 |
+
|
| 2145 |
+
// Build 256-byte palette LUT. Entry 0 = map_color(0); entries 1..=palette_count
|
| 2146 |
+
// come from the RGB triplets. The original reads 4 bytes per triplet but
|
| 2147 |
+
// advances by 3 β only the low 24 bits matter.
|
| 2148 |
+
let mut palette_lut = [0u8; 256];
|
| 2149 |
+
palette_lut[0] = palette_map_color(palette_ctx, 0) as u8;
|
| 2150 |
+
for i in 0..palette_count {
|
| 2151 |
+
let rgb = *(cursor as *const u32);
|
| 2152 |
+
palette_lut[(1 + i) as usize] = palette_map_color(palette_ctx, rgb) as u8;
|
| 2153 |
+
cursor = cursor.add(3);
|
| 2154 |
+
}
|
| 2155 |
+
|
| 2156 |
+
// palette_end = cursor after palette triplets.
|
| 2157 |
+
(*font_obj).palette_end = cursor;
|
| 2158 |
+
|
| 2159 |
+
// Read max_width (i16) at cursor + 0x100 β the character-width table sits
|
| 2160 |
+
// between the palette and the header.
|
| 2161 |
+
let max_width = *(cursor.add(0x100) as *const i16) as i32;
|
| 2162 |
+
cursor = cursor.add(0x100);
|
| 2163 |
+
(*font_obj).width = max_width as u16;
|
| 2164 |
+
|
| 2165 |
+
// Seed value for Font__GetInfo's max metric: signed (width / 5) + 1,
|
| 2166 |
+
// with a +1 correction when the quotient is negative (matches the
|
| 2167 |
+
// IMUL+SAR+SHR+LEA sequence in the original).
|
| 2168 |
+
let w_div_5 = max_width / 5;
|
| 2169 |
+
let seed = w_div_5.wrapping_add((w_div_5 >> 31) & 1).wrapping_add(1);
|
| 2170 |
+
(*font_obj).width_div_5 = seed as u16;
|
| 2171 |
+
|
| 2172 |
+
// Skip max_width, read glyph_count (u16).
|
| 2173 |
+
cursor = cursor.add(2);
|
| 2174 |
+
let glyph_count = *(cursor as *const u16);
|
| 2175 |
+
cursor = cursor.add(2);
|
| 2176 |
+
(*font_obj).height = glyph_count;
|
| 2177 |
+
(*font_obj)._height2 = glyph_count;
|
| 2178 |
+
|
| 2179 |
+
// Align cursor to 4 bytes relative to the buffer start.
|
| 2180 |
+
let base_addr = base_bytes as usize;
|
| 2181 |
+
while (cursor as usize - base_addr) & 3 != 0 {
|
| 2182 |
+
cursor = cursor.add(1);
|
| 2183 |
+
}
|
| 2184 |
+
|
| 2185 |
+
// Glyph table: glyph_count entries of 12 bytes each.
|
| 2186 |
+
let glyph_table = cursor as *mut GlyphEntry;
|
| 2187 |
+
(*font_obj).glyph_table = glyph_table;
|
| 2188 |
+
let pixel_data = glyph_table.add(glyph_count as usize) as *mut u8;
|
| 2189 |
+
(*font_obj).pixel_data = pixel_data;
|
| 2190 |
+
|
| 2191 |
+
// Pixel byte count: data_size - (pixel_data - buffer), rounded down to dwords.
|
| 2192 |
+
// Original uses CDQ+AND 3+ADD+SAR 2 which rounds toward zero.
|
| 2193 |
+
let remaining = data_size - (pixel_data as usize - base_addr) as i32;
|
| 2194 |
+
let dword_count = remaining
|
| 2195 |
+
.wrapping_add((remaining >> 31) & 3)
|
| 2196 |
+
.wrapping_shr(2) as u32;
|
| 2197 |
+
|
| 2198 |
+
remap_pixels_through_lut(pixel_data, 0, palette_lut.as_ptr(), dword_count, 1);
|
| 2199 |
+
|
| 2200 |
+
1
|
| 2201 |
+
}
|
| 2202 |
+
|
| 2203 |
+
/// Port of `FUN_004f9940` β loads a font resource by name from a GfxDir and
|
| 2204 |
+
/// feeds it into `font_parse_data`.
|
| 2205 |
+
///
|
| 2206 |
+
/// Original convention: usercall with `EAX = gfx_dir`, `ECX = font_obj`,
|
| 2207 |
+
/// `stack[0] = layer_ctx`, `stack[1] = filename`, `RET 0x8`. The function
|
| 2208 |
+
/// first tries `GfxDir__FindEntry` + cached-load via `gfx_dir->vtable[2]`;
|
| 2209 |
+
/// on miss it falls back to `GfxDir__LoadImage` + `image->vtable[4]` (get_size)
|
| 2210 |
+
/// + `image->vtable[5]` (read into caller-allocated buffer) + `image->vtable[0](1)`
|
| 2211 |
+
/// (destroy), then tail-calls the parser.
|
| 2212 |
+
///
|
| 2213 |
+
/// Note: `layer_ctx` is only used by `font_parse_data` (as the PaletteContext).
|
| 2214 |
+
/// The original never touches it in this function β it just forwards it via
|
| 2215 |
+
/// ECX to the tail call.
|
| 2216 |
+
///
|
| 2217 |
+
/// # Safety
|
| 2218 |
+
/// `font_obj` must be a valid writable `FontObject`. `gfx_dir` must be a valid
|
| 2219 |
+
/// GfxDir. `palette_ctx` must be a valid PaletteContext. `name` must be a
|
| 2220 |
+
/// valid null-terminated C string.
|
| 2221 |
+
pub unsafe fn font_load_from_gfx(
|
| 2222 |
+
font_obj: *mut FontObject,
|
| 2223 |
+
gfx_dir: *mut u8,
|
| 2224 |
+
palette_ctx: *mut PaletteContext,
|
| 2225 |
+
name: *const core::ffi::c_char,
|
| 2226 |
+
) -> u32 {
|
| 2227 |
+
use crate::render::sprite::gfx_dir::{call_gfx_load_image, gfx_dir_find_entry, GfxDirEntry};
|
| 2228 |
+
use crate::wa_alloc::wa_malloc;
|
| 2229 |
+
|
| 2230 |
+
// 1. Try FindEntry β gfx_dir->vtable[2](entry->value) for cached load.
|
| 2231 |
+
let entry = gfx_dir_find_entry(name, gfx_dir);
|
| 2232 |
+
if !entry.is_null() {
|
| 2233 |
+
let vt = *(gfx_dir as *const *const u32);
|
| 2234 |
+
let load_cached: unsafe extern "thiscall" fn(*mut u8, u32) -> *mut FntHeader =
|
| 2235 |
+
core::mem::transmute(*vt.add(2));
|
| 2236 |
+
let entry_val = (*(entry as *const GfxDirEntry)).value;
|
| 2237 |
+
let cached = load_cached(gfx_dir, entry_val);
|
| 2238 |
+
if !cached.is_null() {
|
| 2239 |
+
return font_parse_data(font_obj, cached, palette_ctx);
|
| 2240 |
+
}
|
| 2241 |
+
}
|
| 2242 |
+
|
| 2243 |
+
// 2. Fallback: LoadImage β vtable[4] (size) β wa_malloc β vtable[5] (read) β vtable[0] (destroy)
|
| 2244 |
+
let image = call_gfx_load_image(gfx_dir, name);
|
| 2245 |
+
if image.is_null() {
|
| 2246 |
+
return 0;
|
| 2247 |
+
}
|
| 2248 |
+
|
| 2249 |
+
let image_vt = *(image as *const *const u32);
|
| 2250 |
+
|
| 2251 |
+
// vtable[4] β get size (thiscall, no args)
|
| 2252 |
+
let get_size: unsafe extern "thiscall" fn(*mut u8) -> u32 =
|
| 2253 |
+
core::mem::transmute(*image_vt.add(4));
|
| 2254 |
+
let size = get_size(image);
|
| 2255 |
+
|
| 2256 |
+
// Match the original's allocation: round size up to 4-byte multiple, add 0x20 guard.
|
| 2257 |
+
let alloc_size = ((size + 3) & !3u32).wrapping_add(0x20);
|
| 2258 |
+
let buffer = wa_malloc(alloc_size);
|
| 2259 |
+
// Original memsets only `size` bytes, not the full allocation.
|
| 2260 |
+
if !buffer.is_null() {
|
| 2261 |
+
core::ptr::write_bytes(buffer, 0, size as usize);
|
| 2262 |
+
}
|
| 2263 |
+
|
| 2264 |
+
// vtable[5] β read into buffer (thiscall with 2 stack args: buffer, size)
|
| 2265 |
+
let read_into: unsafe extern "thiscall" fn(*mut u8, *mut u8, u32) =
|
| 2266 |
+
core::mem::transmute(*image_vt.add(5));
|
| 2267 |
+
read_into(image, buffer, size);
|
| 2268 |
+
|
| 2269 |
+
// vtable[0] β destroy image (thiscall with 1 arg: flags = 1)
|
| 2270 |
+
let destroy: unsafe extern "thiscall" fn(*mut u8, u32) = core::mem::transmute(*image_vt);
|
| 2271 |
+
destroy(image, 1);
|
| 2272 |
+
|
| 2273 |
+
font_parse_data(font_obj, buffer as *mut FntHeader, palette_ctx)
|
| 2274 |
+
}
|
| 2275 |
+
|
| 2276 |
+
/// Port of `DisplayGfx::LoadFont` (vtable slot 34, 0x523560).
|
| 2277 |
+
///
|
| 2278 |
+
/// Validates `mode` (1..=3) and `font_id` (1..=31), then allocates a
|
| 2279 |
+
/// zero-initialized 0x1C-byte `FontObject`, loads the named resource via
|
| 2280 |
+
/// `font_load_from_gfx`, and stores the object in `DisplayBase::font_table`.
|
| 2281 |
+
///
|
| 2282 |
+
/// On load failure, the partially-initialized font object is leaked to match
|
| 2283 |
+
/// the original's behavior (it calls a sprite-bank-style cleanup helper
|
| 2284 |
+
/// `FUN_005230c0` which is not exercised here).
|
| 2285 |
+
///
|
| 2286 |
+
/// # Safety
|
| 2287 |
+
/// `this` must be a valid `*mut DisplayGfx`. `_gfx` must be a GfxDir pointer
|
| 2288 |
+
/// (or null if only the cached path is needed). `filename` must be a valid
|
| 2289 |
+
/// null-terminated C string.
|
| 2290 |
+
pub unsafe fn load_font(
|
| 2291 |
+
this: *mut DisplayGfx,
|
| 2292 |
+
mode: i32,
|
| 2293 |
+
font_id: i32,
|
| 2294 |
+
_gfx: *mut u8,
|
| 2295 |
+
filename: *const core::ffi::c_char,
|
| 2296 |
+
) -> u32 {
|
| 2297 |
+
use crate::wa_alloc::wa_malloc_struct_zeroed;
|
| 2298 |
+
|
| 2299 |
+
// Validate mode (1..=3) and that the layer context exists.
|
| 2300 |
+
if !(1..=3).contains(&mode) {
|
| 2301 |
+
return 0;
|
| 2302 |
+
}
|
| 2303 |
+
let base = &mut (*this).base;
|
| 2304 |
+
let layer_ctx = base.layer_contexts[mode as usize];
|
| 2305 |
+
if layer_ctx == 0 {
|
| 2306 |
+
return 0;
|
| 2307 |
+
}
|
| 2308 |
+
|
| 2309 |
+
// Validate font_id (1..=31) and that the slot is empty.
|
| 2310 |
+
if !(1..=31).contains(&font_id) {
|
| 2311 |
+
return 0;
|
| 2312 |
+
}
|
| 2313 |
+
if base.font_table[font_id as usize] != 0 {
|
| 2314 |
+
return 0;
|
| 2315 |
+
}
|
| 2316 |
+
|
| 2317 |
+
// Allocate zeroed FontObject. The original uses WA_MallocMemset(0x1C)
|
| 2318 |
+
// which only memsets the requested size; wa_malloc_struct_zeroed matches.
|
| 2319 |
+
let font_obj = wa_malloc_struct_zeroed::<FontObject>();
|
| 2320 |
+
if font_obj.is_null() {
|
| 2321 |
+
return 0;
|
| 2322 |
+
}
|
| 2323 |
+
|
| 2324 |
+
// Load and parse the font data.
|
| 2325 |
+
let result = font_load_from_gfx(font_obj, _gfx, layer_ctx as *mut PaletteContext, filename);
|
| 2326 |
+
if result == 0 {
|
| 2327 |
+
// Original leaks here on failure (via an unported cleanup helper).
|
| 2328 |
+
// We match that behavior rather than introducing a free path that
|
| 2329 |
+
// might differ from WA's.
|
| 2330 |
+
return 0;
|
| 2331 |
+
}
|
| 2332 |
+
|
| 2333 |
+
// Install into font_table. The original also records which mode owns this
|
| 2334 |
+
// font slot at DisplayBase + 0x301C + font_id*4 (the gap region next to
|
| 2335 |
+
// font_table), then bumps the layer visibility counter.
|
| 2336 |
+
base.font_table[font_id as usize] = font_obj as u32;
|
| 2337 |
+
base._gap_301c[font_id as usize] = mode as u32;
|
| 2338 |
+
base.layer_visibility[mode as usize] += 1;
|
| 2339 |
+
|
| 2340 |
+
1
|
| 2341 |
+
}
|
crates/openwa-dll/src/replacements/render.rs
CHANGED
|
@@ -1617,6 +1617,16 @@ unsafe extern "thiscall" fn load_sprite_by_layer(
|
|
| 1617 |
display_vtable::load_sprite_by_layer(this, layer, id, gfx, name)
|
| 1618 |
}
|
| 1619 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1620 |
// ---------------------------------------------------------------------------
|
| 1621 |
// Display installation
|
| 1622 |
// ---------------------------------------------------------------------------
|
|
@@ -1685,8 +1695,9 @@ fn install_display() -> Result<(), String> {
|
|
| 1685 |
set_font_palette => set_font_palette,
|
| 1686 |
slot 19 => blit_sprite,
|
| 1687 |
load_sprite_by_layer => load_sprite_by_layer,
|
|
|
|
| 1688 |
})?;
|
| 1689 |
-
let _ = log_line("[Display] DisplayGfx: patched
|
| 1690 |
}
|
| 1691 |
|
| 1692 |
Ok(())
|
|
|
|
| 1617 |
display_vtable::load_sprite_by_layer(this, layer, id, gfx, name)
|
| 1618 |
}
|
| 1619 |
|
| 1620 |
+
unsafe extern "thiscall" fn load_font(
|
| 1621 |
+
this: *mut DisplayGfx,
|
| 1622 |
+
mode: i32,
|
| 1623 |
+
font_id: i32,
|
| 1624 |
+
gfx: *mut u8,
|
| 1625 |
+
filename: *const core::ffi::c_char,
|
| 1626 |
+
) -> u32 {
|
| 1627 |
+
display_vtable::load_font(this, mode, font_id, gfx, filename)
|
| 1628 |
+
}
|
| 1629 |
+
|
| 1630 |
// ---------------------------------------------------------------------------
|
| 1631 |
// Display installation
|
| 1632 |
// ---------------------------------------------------------------------------
|
|
|
|
| 1695 |
set_font_palette => set_font_palette,
|
| 1696 |
slot 19 => blit_sprite,
|
| 1697 |
load_sprite_by_layer => load_sprite_by_layer,
|
| 1698 |
+
load_font => load_font,
|
| 1699 |
})?;
|
| 1700 |
+
let _ = log_line("[Display] DisplayGfx: patched 30 methods -> Rust");
|
| 1701 |
}
|
| 1702 |
|
| 1703 |
Ok(())
|