paavohtl Claude Opus 4.6 (1M context) commited on
Commit
29e5d17
·
1 Parent(s): dfe2f8e

debug: heap-allocate PCLandscape output buffers, add toggle

Browse files

Findings 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
- let mut landscape_coords: [i32; 2] = [0; 2]; // aiStack_11d4
749
- let mut landscape_byte: u8 = 0; // iStack_11f9
 
 
 
 
 
750
 
751
  let pc_ctor: unsafe extern "stdcall" fn(
752
- *mut u8, *mut DDGame, *mut u8, // this, ddgame, gfx_resource
753
- *const u8, *mut DDGameWrapper, // game_info+0xDAAC, wrapper
754
- *mut u8, u32, // &landscape_byte, gfx_mode
755
- *mut u8, *mut [i32; 2], // stack local, coord output
756
- *mut u8, *mut u8, // &ddgame+0x777C, &ddgame+0x7780
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
- gi.add(0xDAAC), // param 4
765
- wrapper, // param 5
766
- &mut landscape_byte as *mut u8,
767
- (*wrapper).gfx_mode,
768
- &mut landscape_byte as *mut u8, // param 8 (stack local)
769
- &mut landscape_coords as *mut [i32; 2],
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
- // Call our Rust DDGame constructor.
116
- create_ddgame(
117
- this,
118
- keyboard as *mut openwa_core::input::DDKeyboard,
119
- display,
120
- sound,
121
- palette,
122
- streaming_audio as *mut openwa_core::audio::Music,
123
- timer_obj,
124
- net_game,
125
- game_info,
126
- input_ctrl as u32,
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() {