fix: pass display layer context to arrow sprite loading
Browse filesArrow sprite loop was passing null as display_ctx to
call_gfx_load_and_wrap. The original passes the return value from
display->vtable[5](1) (set_layer) which is the active layer context.
Without this, palette/color lookups crash with null this pointer.
Also wrap cached GfxDir entries with DisplayGfx__Constructor_Maybe
(0x4F5E80) to match the original's behavior.
3/3 test runs pass with 119 validations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/engine/ddgame.rs
CHANGED
|
@@ -876,13 +876,11 @@ unsafe fn init_graphics_and_resources(
|
|
| 876 |
name_buf[5] = b'0' + (i / 10) as u8;
|
| 877 |
name_buf[6] = b'0' + (i % 10) as u8;
|
| 878 |
|
| 879 |
-
// Display vtable[5](1) — set active layer
|
| 880 |
-
|
| 881 |
-
|
| 882 |
-
|
| 883 |
-
|
| 884 |
-
set_layer((*ddgame).display, 1);
|
| 885 |
-
}
|
| 886 |
|
| 887 |
// GfxDir__FindEntry: usercall(EAX=name) + 1 stack(gfx_handler), RET 0x4
|
| 888 |
let entry = call_gfx_find_entry(name_buf.as_ptr(), gfx_handler);
|
|
@@ -895,16 +893,15 @@ unsafe fn init_graphics_and_resources(
|
|
| 895 |
core::mem::transmute(*gfx_vt.add(2));
|
| 896 |
let cached = load_cached(gfx_handler, *(entry.add(4) as *const u32));
|
| 897 |
if !cached.is_null() {
|
| 898 |
-
|
|
|
|
|
|
|
|
|
|
| 899 |
} else {
|
| 900 |
-
sprite = call_gfx_load_and_wrap(
|
| 901 |
-
gfx_handler, name_buf.as_ptr(), core::ptr::null_mut(),
|
| 902 |
-
);
|
| 903 |
}
|
| 904 |
} else {
|
| 905 |
-
sprite = call_gfx_load_and_wrap(
|
| 906 |
-
gfx_handler, name_buf.as_ptr(), core::ptr::null_mut(),
|
| 907 |
-
);
|
| 908 |
}
|
| 909 |
|
| 910 |
// Store arrow sprite at DDGame+0x38+i*4
|
|
|
|
| 876 |
name_buf[5] = b'0' + (i / 10) as u8;
|
| 877 |
name_buf[6] = b'0' + (i % 10) as u8;
|
| 878 |
|
| 879 |
+
// Display vtable[5](1) — set active layer, returns context ptr
|
| 880 |
+
let disp_vt = *((*ddgame).display as *const *const u32);
|
| 881 |
+
let set_layer: unsafe extern "thiscall" fn(*mut DDDisplay, i32) -> *mut u8 =
|
| 882 |
+
core::mem::transmute(*disp_vt.add(5));
|
| 883 |
+
let layer_ctx = set_layer((*ddgame).display, 1);
|
|
|
|
|
|
|
| 884 |
|
| 885 |
// GfxDir__FindEntry: usercall(EAX=name) + 1 stack(gfx_handler), RET 0x4
|
| 886 |
let entry = call_gfx_find_entry(name_buf.as_ptr(), gfx_handler);
|
|
|
|
| 893 |
core::mem::transmute(*gfx_vt.add(2));
|
| 894 |
let cached = load_cached(gfx_handler, *(entry.add(4) as *const u32));
|
| 895 |
if !cached.is_null() {
|
| 896 |
+
// Wrap with DisplayGfx constructor
|
| 897 |
+
let ctor: unsafe extern "stdcall" fn(*mut u8) -> *mut u8 =
|
| 898 |
+
core::mem::transmute(rb(0x4F5E80) as usize);
|
| 899 |
+
sprite = ctor(cached);
|
| 900 |
} else {
|
| 901 |
+
sprite = call_gfx_load_and_wrap(gfx_handler, name_buf.as_ptr(), layer_ctx);
|
|
|
|
|
|
|
| 902 |
}
|
| 903 |
} else {
|
| 904 |
+
sprite = call_gfx_load_and_wrap(gfx_handler, name_buf.as_ptr(), layer_ctx);
|
|
|
|
|
|
|
| 905 |
}
|
| 906 |
|
| 907 |
// Store arrow sprite at DDGame+0x38+i*4
|