refactor: type set_active_layer return + propagate PaletteContext
Browse filesCascades a typed `*mut PaletteContext` return through the chain that
starts at `DisplayGfx::set_active_layer` (slot 5) and feeds the GfxDir
loaders.
- Slot 5 vtable signature retyped from `*mut u8` to
`*mut PaletteContext`. Doc-comment promoted from one line to
explain what the result is used for downstream.
- `gfx_resource_create` (`FUN_004F6300` port), `call_gfx_find_and_load`,
and `call_gfx_load_and_wrap` now take `display_ctx: *mut PaletteContext`
instead of `*mut u8`. The single internal `IMG_Decode` call still
casts back to `*mut u8` since `IMG_Decode` is WA-side.
- `compute_complex_gradient` (`render/display/gradient.rs`) takes
`layer3_ctx: *mut PaletteContext` and passes it through to
`call_gfx_find_and_load` without any casts.
- `wa_load_sprites` naked-asm bridge's `_layer_ctx` parameter retyped
to `*mut PaletteContext` to match what its only caller passes after
`set_active_layer`. The naked asm body is unchanged — pointer types
are zero-cost on x86.
- The `+0x606` byte arithmetic at
`ddgame_constructor.rs:969` (the gradient sky-height read) is now
a clean `(*layer3_ctx).cache_count` field access. The cast/offset
pair `*(layer3_ctx.add(0x606) as *const i16)` is gone.
- One redundant `as *mut PaletteContext` cast removed from
`load_sprite_by_layer`'s call to `set_active_layer`.
- `gfx_resource_create_trampoline`'s impl wrapper in DLL adds one
explicit `as *mut PaletteContext` cast since the trampoline catches
an opaque stack pointer from the WA-side caller. Documented in a
comment that all known WA call sites pass a `set_active_layer`
return value (i.e. a `PaletteContext*`).
No behavior changes — all pointer ops are zero-cost casts on x86.
Headless replay tests pass 15/15.
|
@@ -182,7 +182,7 @@ unsafe extern "C" fn wa_load_sprites(
|
|
| 182 |
_sprite_data: *mut u8,
|
| 183 |
_display_flags: u32,
|
| 184 |
_param4: u32,
|
| 185 |
-
_layer_ctx: *mut
|
| 186 |
) {
|
| 187 |
core::arch::naked_asm!(
|
| 188 |
"push esi",
|
|
@@ -664,7 +664,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 664 |
gfx_resource = gfx_resource_create(
|
| 665 |
gfx_dir,
|
| 666 |
rb(va::STR_MASKS_IMG) as *const core::ffi::c_char,
|
| 667 |
-
palette_ctx,
|
| 668 |
);
|
| 669 |
}
|
| 670 |
|
|
@@ -966,7 +966,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 966 |
// ── Gradient image (0x030) ──
|
| 967 |
let level_height = (*ddgame).level_height as i32;
|
| 968 |
let layer3_ctx = (*disp).set_active_layer(3);
|
| 969 |
-
let s_var1 =
|
| 970 |
|
| 971 |
if s_var1 < 0x61 && level_height == 0x2B8 {
|
| 972 |
// Simple gradient: load gradient.img directly
|
|
|
|
| 182 |
_sprite_data: *mut u8,
|
| 183 |
_display_flags: u32,
|
| 184 |
_param4: u32,
|
| 185 |
+
_layer_ctx: *mut crate::render::palette::PaletteContext, // → ESI
|
| 186 |
) {
|
| 187 |
core::arch::naked_asm!(
|
| 188 |
"push esi",
|
|
|
|
| 664 |
gfx_resource = gfx_resource_create(
|
| 665 |
gfx_dir,
|
| 666 |
rb(va::STR_MASKS_IMG) as *const core::ffi::c_char,
|
| 667 |
+
palette_ctx as *mut crate::render::palette::PaletteContext,
|
| 668 |
);
|
| 669 |
}
|
| 670 |
|
|
|
|
| 966 |
// ── Gradient image (0x030) ──
|
| 967 |
let level_height = (*ddgame).level_height as i32;
|
| 968 |
let layer3_ctx = (*disp).set_active_layer(3);
|
| 969 |
+
let s_var1 = (*layer3_ctx).cache_count;
|
| 970 |
|
| 971 |
if s_var1 < 0x61 && level_height == 0x2B8 {
|
| 972 |
// Simple gradient: load gradient.img directly
|
|
@@ -111,7 +111,7 @@ impl PaletteContext {
|
|
| 111 |
pub(crate) unsafe fn compute_complex_gradient(
|
| 112 |
ddgame: *mut DDGame,
|
| 113 |
land_dir: *mut GfxDir,
|
| 114 |
-
layer3_ctx: *mut
|
| 115 |
sky_height: i16,
|
| 116 |
) {
|
| 117 |
let mut palette = PaletteContext::new();
|
|
|
|
| 111 |
pub(crate) unsafe fn compute_complex_gradient(
|
| 112 |
ddgame: *mut DDGame,
|
| 113 |
land_dir: *mut GfxDir,
|
| 114 |
+
layer3_ctx: *mut crate::render::palette::PaletteContext,
|
| 115 |
sky_height: i16,
|
| 116 |
) {
|
| 117 |
let mut palette = PaletteContext::new();
|
|
@@ -67,9 +67,14 @@ pub struct DisplayGfxVtable {
|
|
| 67 |
/// set layer color (0x5231E0, RET 0x8)
|
| 68 |
#[slot(4)]
|
| 69 |
pub set_layer_color: fn(this: *mut DisplayGfx, layer: i32, color: i32),
|
| 70 |
-
/// set active layer, returns layer context ptr (0x523270, RET 0x4)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
#[slot(5)]
|
| 72 |
-
pub set_active_layer: fn(this: *mut DisplayGfx, layer: i32) -> *mut
|
| 73 |
/// get sprite info by layer and id (0x523500, RET 0x10)
|
| 74 |
///
|
| 75 |
/// Looks up sprite metadata for the given layer ID. Checks sprite_ptrs
|
|
@@ -1457,9 +1462,12 @@ unsafe fn palette_context_init(ctx: *mut PaletteContext, range_min: i16, range_m
|
|
| 1457 |
///
|
| 1458 |
/// Returns the `PaletteContext*` for `layer` (1-3), or null if out of
|
| 1459 |
/// range. Used as palette data input for `update_palette`.
|
| 1460 |
-
pub unsafe extern "thiscall" fn set_active_layer(
|
|
|
|
|
|
|
|
|
|
| 1461 |
match Layer::try_from_i32(layer) {
|
| 1462 |
-
Some(layer) => (*this).base.layer_contexts[layer.idx()]
|
| 1463 |
None => core::ptr::null_mut(),
|
| 1464 |
}
|
| 1465 |
}
|
|
@@ -1902,7 +1910,7 @@ pub unsafe fn load_sprite_by_layer(
|
|
| 1902 |
return 1;
|
| 1903 |
}
|
| 1904 |
|
| 1905 |
-
let palette_ctx = set_active_layer(this, layer as i32)
|
| 1906 |
|
| 1907 |
if !is_valid_sprite_id(id as i32) {
|
| 1908 |
return 0;
|
|
|
|
| 67 |
/// set layer color (0x5231E0, RET 0x8)
|
| 68 |
#[slot(4)]
|
| 69 |
pub set_layer_color: fn(this: *mut DisplayGfx, layer: i32, color: i32),
|
| 70 |
+
/// set active layer, returns layer context ptr (0x523270, RET 0x4).
|
| 71 |
+
///
|
| 72 |
+
/// Returns the `PaletteContext*` for the requested layer (1-3),
|
| 73 |
+
/// or null if the index is out of range. Callers feed the result to
|
| 74 |
+
/// `update_palette` (slot 24) and to GfxDir loaders that need a
|
| 75 |
+
/// palette context for color remapping.
|
| 76 |
#[slot(5)]
|
| 77 |
+
pub set_active_layer: fn(this: *mut DisplayGfx, layer: i32) -> *mut PaletteContext,
|
| 78 |
/// get sprite info by layer and id (0x523500, RET 0x10)
|
| 79 |
///
|
| 80 |
/// Looks up sprite metadata for the given layer ID. Checks sprite_ptrs
|
|
|
|
| 1462 |
///
|
| 1463 |
/// Returns the `PaletteContext*` for `layer` (1-3), or null if out of
|
| 1464 |
/// range. Used as palette data input for `update_palette`.
|
| 1465 |
+
pub unsafe extern "thiscall" fn set_active_layer(
|
| 1466 |
+
this: *mut DisplayGfx,
|
| 1467 |
+
layer: i32,
|
| 1468 |
+
) -> *mut PaletteContext {
|
| 1469 |
match Layer::try_from_i32(layer) {
|
| 1470 |
+
Some(layer) => (*this).base.layer_contexts[layer.idx()],
|
| 1471 |
None => core::ptr::null_mut(),
|
| 1472 |
}
|
| 1473 |
}
|
|
|
|
| 1910 |
return 1;
|
| 1911 |
}
|
| 1912 |
|
| 1913 |
+
let palette_ctx = set_active_layer(this, layer as i32);
|
| 1914 |
|
| 1915 |
if !is_valid_sprite_id(id as i32) {
|
| 1916 |
return 0;
|
|
@@ -350,7 +350,7 @@ pub unsafe fn gfx_dir_load_dir(handler: *mut u8) -> i32 {
|
|
| 350 |
pub unsafe fn gfx_resource_create(
|
| 351 |
gfx_dir: *mut GfxDir,
|
| 352 |
name: *const core::ffi::c_char,
|
| 353 |
-
output: *mut
|
| 354 |
) -> *mut u8 {
|
| 355 |
// 1. Try FindEntry → cached load → DisplayGfx wrap
|
| 356 |
let entry = gfx_dir_find_entry(name, gfx_dir);
|
|
@@ -375,7 +375,7 @@ pub unsafe fn gfx_resource_create(
|
|
| 375 |
// IMG_Decode: stdcall(output, raw_image, 1), RET 0xC
|
| 376 |
let decode: unsafe extern "stdcall" fn(*mut u8, *mut u8, i32) -> *mut u8 =
|
| 377 |
core::mem::transmute(rb(va::IMG_DECODE) as usize);
|
| 378 |
-
let result = decode(output, raw_image as *mut u8, 1);
|
| 379 |
|
| 380 |
GfxDirStream::destroy_raw(raw_image);
|
| 381 |
|
|
@@ -388,7 +388,7 @@ pub unsafe fn gfx_resource_create(
|
|
| 388 |
pub(crate) unsafe fn call_gfx_find_and_load(
|
| 389 |
gfx_dir: *mut GfxDir,
|
| 390 |
name: &core::ffi::CStr,
|
| 391 |
-
display_ctx: *mut
|
| 392 |
) -> *mut u8 {
|
| 393 |
let name_ptr = name.as_ptr() as *const u8;
|
| 394 |
let entry = gfx_dir_find_entry(name_ptr.cast(), gfx_dir);
|
|
@@ -415,7 +415,7 @@ pub(crate) unsafe fn call_gfx_find_and_load(
|
|
| 415 |
pub(crate) unsafe fn call_gfx_load_and_wrap(
|
| 416 |
gfx_dir: *mut GfxDir,
|
| 417 |
name: *const core::ffi::c_char,
|
| 418 |
-
display_ctx: *mut
|
| 419 |
) -> *mut u8 {
|
| 420 |
let image = call_gfx_load_image(gfx_dir, name);
|
| 421 |
if image.is_null() {
|
|
@@ -424,7 +424,7 @@ pub(crate) unsafe fn call_gfx_load_and_wrap(
|
|
| 424 |
// FUN_004F5F80(display_ctx, image, 1) — stdcall, RET 0xC (3 params)
|
| 425 |
let f: unsafe extern "stdcall" fn(*mut u8, *mut u8, u32) -> *mut u8 =
|
| 426 |
core::mem::transmute(rb(va::IMG_DECODE) as usize);
|
| 427 |
-
let result = f(display_ctx, image as *mut u8, 1);
|
| 428 |
GfxDirStream::destroy_raw(image);
|
| 429 |
result
|
| 430 |
}
|
|
|
|
| 350 |
pub unsafe fn gfx_resource_create(
|
| 351 |
gfx_dir: *mut GfxDir,
|
| 352 |
name: *const core::ffi::c_char,
|
| 353 |
+
output: *mut crate::render::palette::PaletteContext,
|
| 354 |
) -> *mut u8 {
|
| 355 |
// 1. Try FindEntry → cached load → DisplayGfx wrap
|
| 356 |
let entry = gfx_dir_find_entry(name, gfx_dir);
|
|
|
|
| 375 |
// IMG_Decode: stdcall(output, raw_image, 1), RET 0xC
|
| 376 |
let decode: unsafe extern "stdcall" fn(*mut u8, *mut u8, i32) -> *mut u8 =
|
| 377 |
core::mem::transmute(rb(va::IMG_DECODE) as usize);
|
| 378 |
+
let result = decode(output as *mut u8, raw_image as *mut u8, 1);
|
| 379 |
|
| 380 |
GfxDirStream::destroy_raw(raw_image);
|
| 381 |
|
|
|
|
| 388 |
pub(crate) unsafe fn call_gfx_find_and_load(
|
| 389 |
gfx_dir: *mut GfxDir,
|
| 390 |
name: &core::ffi::CStr,
|
| 391 |
+
display_ctx: *mut crate::render::palette::PaletteContext,
|
| 392 |
) -> *mut u8 {
|
| 393 |
let name_ptr = name.as_ptr() as *const u8;
|
| 394 |
let entry = gfx_dir_find_entry(name_ptr.cast(), gfx_dir);
|
|
|
|
| 415 |
pub(crate) unsafe fn call_gfx_load_and_wrap(
|
| 416 |
gfx_dir: *mut GfxDir,
|
| 417 |
name: *const core::ffi::c_char,
|
| 418 |
+
display_ctx: *mut crate::render::palette::PaletteContext,
|
| 419 |
) -> *mut u8 {
|
| 420 |
let image = call_gfx_load_image(gfx_dir, name);
|
| 421 |
if image.is_null() {
|
|
|
|
| 424 |
// FUN_004F5F80(display_ctx, image, 1) — stdcall, RET 0xC (3 params)
|
| 425 |
let f: unsafe extern "stdcall" fn(*mut u8, *mut u8, u32) -> *mut u8 =
|
| 426 |
core::mem::transmute(rb(va::IMG_DECODE) as usize);
|
| 427 |
+
let result = f(display_ctx as *mut u8, image as *mut u8, 1);
|
| 428 |
GfxDirStream::destroy_raw(image);
|
| 429 |
result
|
| 430 |
}
|
|
@@ -102,6 +102,10 @@ extern "cdecl" fn impl_gfx_resource_create(
|
|
| 102 |
name: *const c_char,
|
| 103 |
output: *mut u8,
|
| 104 |
) -> u32 {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
let result = unsafe { gfx_resource_create(gfx_dir, name, output) };
|
| 106 |
result as u32
|
| 107 |
}
|
|
|
|
| 102 |
name: *const c_char,
|
| 103 |
output: *mut u8,
|
| 104 |
) -> u32 {
|
| 105 |
+
// The trampoline catches an opaque stack pointer; the underlying
|
| 106 |
+
// WA caller always passes a `PaletteContext*` (verified at all known
|
| 107 |
+
// call sites — `set_active_layer`'s return value).
|
| 108 |
+
let output = output as *mut openwa_core::render::palette::PaletteContext;
|
| 109 |
let result = unsafe { gfx_resource_create(gfx_dir, name, output) };
|
| 110 |
result as u32
|
| 111 |
}
|