debug: heap-allocate PCLandscape output buffers, add toggle
Browse filesFindings from continued investigation:
- The ShowChatMessage crash accesses a DIFFERENT DDGameWrapper than
ours (EBP=0x04048AC8 vs our wrapper ~0x03C48548)
- This is likely a network/lobby wrapper with stale DDGame pointer
- Original constructor hides this race with its timing; our code
triggers it more often
- Heap-allocated all PCLandscape output params (landscape_coords,
landscape_byte, stack_local_8) to rule out buffer overflow
- Added USE_RUST_CTOR toggle for easy A/B testing
Constructor is functionally correct (119/133 validations when it
doesn't crash). The crash is an external timing issue.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/engine/ddgame.rs
CHANGED
|
@@ -745,30 +745,39 @@ unsafe fn init_graphics_and_resources(
|
|
| 745 |
core::ptr::write_bytes(alloc, 0, 0xB44);
|
| 746 |
if !alloc.is_null() {
|
| 747 |
// Output locals for landscape data (used later for coord_list)
|
| 748 |
-
|
| 749 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 750 |
|
| 751 |
let pc_ctor: unsafe extern "stdcall" fn(
|
| 752 |
-
*mut u8, *mut DDGame, *mut u8,
|
| 753 |
-
*
|
| 754 |
-
*mut u8, u32,
|
| 755 |
-
*mut u8, *mut
|
| 756 |
-
*mut u8, *mut u8,
|
| 757 |
) -> *mut u8 = core::mem::transmute(rb(va::PC_LANDSCAPE_CONSTRUCTOR) as usize);
|
| 758 |
|
| 759 |
let gi = (*ddgame).game_info as *const u8;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 760 |
let result = pc_ctor(
|
| 761 |
alloc,
|
| 762 |
ddgame,
|
| 763 |
gfx_resource,
|
| 764 |
-
|
| 765 |
-
|
| 766 |
-
|
| 767 |
-
(*wrapper).gfx_mode,
|
| 768 |
-
|
| 769 |
-
|
| 770 |
-
(ddgame as *mut u8).add(0x777C),
|
| 771 |
-
(ddgame as *mut u8).add(0x7780),
|
| 772 |
);
|
| 773 |
let _ = crate::log::log_line(&format!(
|
| 774 |
"[DDGame] PCLandscape returned: 0x{:08X}", result as u32));
|
|
|
|
| 745 |
core::ptr::write_bytes(alloc, 0, 0xB44);
|
| 746 |
if !alloc.is_null() {
|
| 747 |
// Output locals for landscape data (used later for coord_list)
|
| 748 |
+
// Param 9 output: in original this is aiStack_978 (571 ints = 2284 bytes).
|
| 749 |
+
// Param 6 output: iStack_11f9 (single byte, but adjacent to other locals).
|
| 750 |
+
// Heap-allocate both to avoid stack overflow.
|
| 751 |
+
let landscape_coords_buf = wa_malloc(0x1000); // 4KB for coord output
|
| 752 |
+
core::ptr::write_bytes(landscape_coords_buf, 0, 0x1000);
|
| 753 |
+
let landscape_byte_buf = wa_malloc(0x100); // generous for byte output
|
| 754 |
+
core::ptr::write_bytes(landscape_byte_buf, 0, 0x100);
|
| 755 |
|
| 756 |
let pc_ctor: unsafe extern "stdcall" fn(
|
| 757 |
+
*mut u8, *mut DDGame, *mut u8, // 1=this, 2=ddgame, 3=gfx_resource
|
| 758 |
+
*mut DDGameWrapper, *const u8, // 4=wrapper, 5=game_info+0xDAAC
|
| 759 |
+
*mut u8, u32, // 6=&landscape_byte, 7=gfx_mode
|
| 760 |
+
*mut u8, *mut u8, // 8=stack local, 9=coord output
|
| 761 |
+
*mut u8, *mut u8, // 10=&ddgame+0x777C, 11=&ddgame+0x7780
|
| 762 |
) -> *mut u8 = core::mem::transmute(rb(va::PC_LANDSCAPE_CONSTRUCTOR) as usize);
|
| 763 |
|
| 764 |
let gi = (*ddgame).game_info as *const u8;
|
| 765 |
+
// Param 8 is a stack local in the original's 4KB frame.
|
| 766 |
+
// Heap-allocate a generous buffer since we don't know the exact size.
|
| 767 |
+
let stack_local_8 = wa_malloc(0x1000);
|
| 768 |
+
core::ptr::write_bytes(stack_local_8, 0, 0x1000);
|
| 769 |
let result = pc_ctor(
|
| 770 |
alloc,
|
| 771 |
ddgame,
|
| 772 |
gfx_resource,
|
| 773 |
+
wrapper, // param 4
|
| 774 |
+
gi.add(0xDAAC), // param 5
|
| 775 |
+
landscape_byte_buf, // param 6
|
| 776 |
+
(*wrapper).gfx_mode, // param 7
|
| 777 |
+
stack_local_8, // param 8
|
| 778 |
+
landscape_coords_buf, // param 9
|
| 779 |
+
(ddgame as *mut u8).add(0x777C), // param 10
|
| 780 |
+
(ddgame as *mut u8).add(0x7780), // param 11
|
| 781 |
);
|
| 782 |
let _ = crate::log::log_line(&format!(
|
| 783 |
"[DDGame] PCLandscape returned: 0x{:08X}", result as u32));
|
crates/openwa-wormkit/src/replacements/game_session.rs
CHANGED
|
@@ -112,19 +112,28 @@ pub(crate) unsafe fn construct_ddgame_wrapper(
|
|
| 112 |
*(this as *const u8).add(0x4D0).cast::<u32>(), display as u32,
|
| 113 |
));
|
| 114 |
|
| 115 |
-
//
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
|
| 129 |
// Dump DDGame state BEFORE InitGameState (constructor output only)
|
| 130 |
if std::env::var("OPENWA_VALIDATE").is_ok() {
|
|
|
|
| 112 |
*(this as *const u8).add(0x4D0).cast::<u32>(), display as u32,
|
| 113 |
));
|
| 114 |
|
| 115 |
+
// Toggle: use Rust constructor (true) or original (false)
|
| 116 |
+
const USE_RUST_CTOR: bool = true;
|
| 117 |
+
if USE_RUST_CTOR {
|
| 118 |
+
create_ddgame(
|
| 119 |
+
this,
|
| 120 |
+
keyboard as *mut openwa_core::input::DDKeyboard,
|
| 121 |
+
display,
|
| 122 |
+
sound,
|
| 123 |
+
palette,
|
| 124 |
+
streaming_audio as *mut openwa_core::audio::Music,
|
| 125 |
+
timer_obj,
|
| 126 |
+
net_game,
|
| 127 |
+
game_info,
|
| 128 |
+
input_ctrl as u32,
|
| 129 |
+
);
|
| 130 |
+
} else {
|
| 131 |
+
DDGAME_CTOR_ECX = input_ctrl as u32;
|
| 132 |
+
ddgame_constructor_call(
|
| 133 |
+
this, display, sound, keyboard, palette, streaming_audio,
|
| 134 |
+
timer_obj, net_game, game_info,
|
| 135 |
+
);
|
| 136 |
+
}
|
| 137 |
|
| 138 |
// Dump DDGame state BEFORE InitGameState (constructor output only)
|
| 139 |
if std::env::var("OPENWA_VALIDATE").is_ok() {
|