fix: 3 calling convention bugs found by systematic audit
Browse filesBugs found by audit agent:
1. FUN_004F5F80: was cdecl, is stdcall with RET 0xC (3 params)
β 12 bytes stack corruption per call
2. FUN_005411A0: was extern "C" fn() with no args, is usercall(EAX=ptr)
β reads garbage from EAX
3. GFX_RESOURCE_CREATE at color entries: was extern "C" fn() with no
args, should use call_gfx_resource_create naked bridge
4. GFX_HANDLER_LOAD_SPRITES: was thiscall(1 param), is stdcall(4 params)
with RET 0x10 β 16 bytes stack corruption per call!
Also removed incorrect headless guards around FUN_570E20 and display
vtable[5] β the original runs these for all modes.
Headless now reaches create_ddgame complete. DEP crash after return
suggests remaining stack corruption from another call.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@@ -541,6 +541,7 @@ unsafe fn init_graphics_and_resources(
|
|
| 541 |
let gfx_load_dir_addr = rb(va::GFX_HANDLER_LOAD_DIR);
|
| 542 |
let gfx_handler_vtable = rb(0x66B280) as u32; // GfxHandler__vtable (from asm: MOV [ESI], 0x66B280)
|
| 543 |
|
|
|
|
| 544 |
// ββ GfxHandler #1 (primary) ββ
|
| 545 |
let gfx1 = wa_malloc(0x19C);
|
| 546 |
core::ptr::write_bytes(gfx1, 0, 0x19C);
|
|
@@ -647,24 +648,25 @@ unsafe fn init_graphics_and_resources(
|
|
| 647 |
}
|
| 648 |
|
| 649 |
// ββ FUN_00570E20: usercall(ESI=wrapper), plain RET ββ
|
| 650 |
-
//
|
| 651 |
-
|
| 652 |
-
call_usercall_esi(wrapper, FUN_570E20_ADDR);
|
| 653 |
-
}
|
| 654 |
|
| 655 |
// ββ Display vtable slot 5 (offset 0x14) ββ
|
| 656 |
-
|
| 657 |
let vt = *((*ddgame).display as *const *const u32);
|
| 658 |
-
let f: unsafe extern "thiscall" fn(*mut DDDisplay) -> *mut u8 =
|
| 659 |
core::mem::transmute(*vt.add(5));
|
| 660 |
-
f((*ddgame).display);
|
| 661 |
}
|
| 662 |
|
|
|
|
| 663 |
// ββ GfxDir color entries DDGame+0x730C..0x732C ββ
|
| 664 |
if (*wrapper).gfx_mode != 0 {
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
let res =
|
|
|
|
|
|
|
| 668 |
if !res.is_null() {
|
| 669 |
let rvt = *(res as *const *const u32);
|
| 670 |
let get_color: unsafe extern "thiscall" fn(*mut u8) -> u32 =
|
|
@@ -679,26 +681,29 @@ unsafe fn init_graphics_and_resources(
|
|
| 679 |
release(res);
|
| 680 |
}
|
| 681 |
} else {
|
| 682 |
-
|
|
|
|
|
|
|
| 683 |
core::mem::transmute(rb(va::GFX_HANDLER_LOAD_SPRITES) as usize);
|
| 684 |
-
f(wrapper as *mut u8);
|
| 685 |
}
|
| 686 |
|
|
|
|
| 687 |
// ββ Secondary GfxDir object (DDGame+0x2C, conditional) ββ
|
| 688 |
if !(*wrapper)._field_4c4.is_null() {
|
| 689 |
let gfxdir2 = wa_malloc(0x70C);
|
| 690 |
core::ptr::write_bytes(gfxdir2, 0, 0x70C);
|
| 691 |
*(gfxdir2 as *mut u16) = 1;
|
| 692 |
*(gfxdir2.add(2) as *mut u16) = 0x5A;
|
| 693 |
-
|
| 694 |
-
|
| 695 |
-
f();
|
| 696 |
-
}
|
| 697 |
*(gfxdir2.add(0x708) as *mut u16) = 0;
|
| 698 |
(*ddgame).secondary_gfxdir = gfxdir2;
|
| 699 |
-
|
|
|
|
|
|
|
| 700 |
core::mem::transmute(rb(va::GFX_HANDLER_LOAD_SPRITES) as usize);
|
| 701 |
-
f(wrapper as *mut u8);
|
| 702 |
}
|
| 703 |
|
| 704 |
// ββ DDGameWrapper field inits ββ
|
|
@@ -1305,8 +1310,8 @@ unsafe fn call_gfx_load_and_wrap(
|
|
| 1305 |
if image.is_null() {
|
| 1306 |
return core::ptr::null_mut();
|
| 1307 |
}
|
| 1308 |
-
// FUN_004F5F80(display_ctx, image, 1) β
|
| 1309 |
-
let f: unsafe extern "
|
| 1310 |
core::mem::transmute(rb(0x4F5F80) as usize);
|
| 1311 |
let result = f(display_ctx, image, 1);
|
| 1312 |
// Release the raw image
|
|
|
|
| 541 |
let gfx_load_dir_addr = rb(va::GFX_HANDLER_LOAD_DIR);
|
| 542 |
let gfx_handler_vtable = rb(0x66B280) as u32; // GfxHandler__vtable (from asm: MOV [ESI], 0x66B280)
|
| 543 |
|
| 544 |
+
let _ = crate::log::log_line("[DDGame] gfx_and_res: entry");
|
| 545 |
// ββ GfxHandler #1 (primary) ββ
|
| 546 |
let gfx1 = wa_malloc(0x19C);
|
| 547 |
core::ptr::write_bytes(gfx1, 0, 0x19C);
|
|
|
|
| 648 |
}
|
| 649 |
|
| 650 |
// ββ FUN_00570E20: usercall(ESI=wrapper), plain RET ββ
|
| 651 |
+
// Runs for all modes (including headless) β the original doesn't guard this.
|
| 652 |
+
call_usercall_esi(wrapper, FUN_570E20_ADDR);
|
|
|
|
|
|
|
| 653 |
|
| 654 |
// ββ Display vtable slot 5 (offset 0x14) ββ
|
| 655 |
+
{
|
| 656 |
let vt = *((*ddgame).display as *const *const u32);
|
| 657 |
+
let f: unsafe extern "thiscall" fn(*mut DDDisplay, i32) -> *mut u8 =
|
| 658 |
core::mem::transmute(*vt.add(5));
|
| 659 |
+
f((*ddgame).display, 1);
|
| 660 |
}
|
| 661 |
|
| 662 |
+
let _ = crate::log::log_line("[DDGame] past gfxhandler+palette");
|
| 663 |
// ββ GfxDir color entries DDGame+0x730C..0x732C ββ
|
| 664 |
if (*wrapper).gfx_mode != 0 {
|
| 665 |
+
// GfxResource__Create: usercall(ECX=gfx_handler, EAX=0x66A3B4) + 1 stack, RET 0x4
|
| 666 |
+
// In original: PUSH ESI(display_layer?), MOV EAX=0x66A3B4, CALL 0x4F6300
|
| 667 |
+
let res = call_gfx_resource_create(
|
| 668 |
+
(*wrapper)._field_4c0, rb(0x66A3B4) as *const u8, core::ptr::null_mut(),
|
| 669 |
+
);
|
| 670 |
if !res.is_null() {
|
| 671 |
let rvt = *(res as *const *const u32);
|
| 672 |
let get_color: unsafe extern "thiscall" fn(*mut u8) -> u32 =
|
|
|
|
| 681 |
release(res);
|
| 682 |
}
|
| 683 |
} else {
|
| 684 |
+
// GFX_HANDLER_LOAD_SPRITES: stdcall(wrapper, ddgame+0x7308, game_info+0xF374, 0), RET 0x10
|
| 685 |
+
let gi = (*ddgame).game_info as *const u8;
|
| 686 |
+
let f: unsafe extern "stdcall" fn(*mut DDGameWrapper, *mut u8, u32, u32) =
|
| 687 |
core::mem::transmute(rb(va::GFX_HANDLER_LOAD_SPRITES) as usize);
|
| 688 |
+
f(wrapper, (ddgame as *mut u8).add(0x7308), *(gi.add(0xF374) as *const u32), 0);
|
| 689 |
}
|
| 690 |
|
| 691 |
+
let _ = crate::log::log_line("[DDGame] past color entries");
|
| 692 |
// ββ Secondary GfxDir object (DDGame+0x2C, conditional) ββ
|
| 693 |
if !(*wrapper)._field_4c4.is_null() {
|
| 694 |
let gfxdir2 = wa_malloc(0x70C);
|
| 695 |
core::ptr::write_bytes(gfxdir2, 0, 0x70C);
|
| 696 |
*(gfxdir2 as *mut u16) = 1;
|
| 697 |
*(gfxdir2.add(2) as *mut u16) = 0x5A;
|
| 698 |
+
// FUN_5411A0: usercall(EAX=gfxdir2), plain RET
|
| 699 |
+
call_usercall_eax(gfxdir2 as *mut DDGameWrapper, rb(0x5411A0));
|
|
|
|
|
|
|
| 700 |
*(gfxdir2.add(0x708) as *mut u16) = 0;
|
| 701 |
(*ddgame).secondary_gfxdir = gfxdir2;
|
| 702 |
+
// GFX_HANDLER_LOAD_SPRITES: stdcall(wrapper, ddgame+0x7308, game_info+0xF374, 0), RET 0x10
|
| 703 |
+
let gi = (*ddgame).game_info as *const u8;
|
| 704 |
+
let f: unsafe extern "stdcall" fn(*mut DDGameWrapper, *mut u8, u32, u32) =
|
| 705 |
core::mem::transmute(rb(va::GFX_HANDLER_LOAD_SPRITES) as usize);
|
| 706 |
+
f(wrapper, (ddgame as *mut u8).add(0x7308), *(gi.add(0xF374) as *const u32), 0);
|
| 707 |
}
|
| 708 |
|
| 709 |
// ββ DDGameWrapper field inits ββ
|
|
|
|
| 1310 |
if image.is_null() {
|
| 1311 |
return core::ptr::null_mut();
|
| 1312 |
}
|
| 1313 |
+
// FUN_004F5F80(display_ctx, image, 1) β stdcall, RET 0xC (3 params)
|
| 1314 |
+
let f: unsafe extern "stdcall" fn(*mut u8, *mut u8, u32) -> *mut u8 =
|
| 1315 |
core::mem::transmute(rb(0x4F5F80) as usize);
|
| 1316 |
let result = f(display_ctx, image, 1);
|
| 1317 |
// Release the raw image
|