paavohtl Claude Opus 4.6 (1M context) commited on
Commit
3fa47b2
Β·
1 Parent(s): d06e127

debug: isolate call_gfx_resource_create as stack corruption source

Browse files

Bisection confirms:
- GfxHandler + palette + color entries + audio: OK (returns cleanly)
- Adding GfxResource__Create_Maybe: DEP crash on return
- GfxResource naked bridge and inline asm bridge both fail
- PCLandscape, TSM, SpriteRegions: all verified OK independently

Next: investigate why GfxResource_Create corrupts the stack.
The sub esp/call/RET 0x4 pattern should be balanced but isn't.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

crates/openwa-core/src/engine/ddgame.rs CHANGED
@@ -647,6 +647,7 @@ unsafe fn init_graphics_and_resources(
647
  f((*ddgame).display, 1);
648
  }
649
 
 
650
  // ── GfxDir color entries DDGame+0x730C..0x732C ──
651
  if (*wrapper).gfx_mode != 0 {
652
  // GfxResource__Create: usercall(ECX=gfx_handler, EAX=0x66A3B4) + 1 stack, RET 0x4
@@ -668,11 +669,7 @@ unsafe fn init_graphics_and_resources(
668
  release(res);
669
  }
670
  } else {
671
- // GFX_HANDLER_LOAD_SPRITES: stdcall(wrapper, ddgame+0x7308, game_info+0xF374, 0), RET 0x10
672
- let gi = (*ddgame).game_info as *const u8;
673
- let f: unsafe extern "stdcall" fn(*mut DDGameWrapper, *mut u8, u32, u32) =
674
- core::mem::transmute(rb(va::GFX_HANDLER_LOAD_SPRITES) as usize);
675
- f(wrapper, (ddgame as *mut u8).add(0x7308), *(gi.add(0xF374) as *const u32), 0);
676
  }
677
 
678
  let _ = crate::log::log_line("[DDGame] past color entries");
@@ -736,7 +733,11 @@ unsafe fn init_graphics_and_resources(
736
  // call_usercall_ecx(wrapper, LOAD_WEAPON_SPRITES_ADDR);
737
 
738
  // ── GfxResource: thiscall(ECX=gfx_handler) + EAX=name + 1 stack(output), RET 0x4 ──
739
- let gfx_resource: *mut u8;
 
 
 
 
740
  {
741
  let gfx_handler = (*wrapper)._field_4c0;
742
  let out_buf = wa_malloc(0x900);
@@ -746,6 +747,7 @@ unsafe fn init_graphics_and_resources(
746
  gfx_resource = call_gfx_resource_create(gfx_handler, rb(0x66A3C0) as *const u8, out_buf);
747
  let _ = crate::log::log_line(&format!("[DDGame] GfxResource=0x{:08X}", gfx_resource as u32));
748
  }
 
749
 
750
  // ── PCLandscape (alloc 0xB44, stdcall 11 params) ──
751
  let landscape = {
@@ -833,6 +835,8 @@ unsafe fn init_graphics_and_resources(
833
  (0x480, 0x2D, 0x08, 0x2D, 0x07, 0x2E, 0x07, gr),
834
  ];
835
 
 
 
836
  for &(offset, ecx, edx, p2, p3, p4, p5, p6) in &regions {
837
  let alloc = wa_malloc(0x9C);
838
  core::ptr::write_bytes(alloc, 0, 0x9C);
@@ -843,6 +847,7 @@ unsafe fn init_graphics_and_resources(
843
  };
844
  *((ddgame as *mut u8).add(offset as usize) as *mut *mut u8) = result;
845
  }
 
846
  }
847
 
848
  // ── Landscape-derived value at DDGame+0x468 ──
@@ -1222,19 +1227,23 @@ static mut GFX_RESOURCE_CREATE_ADDR: u32 = 0;
1222
  /// Bridge to GfxResource__Create_Maybe (0x4F6300).
1223
  /// Convention: usercall(ECX=gfx_handler, EAX=data_ptr) + 1 stack(output), RET 0x4.
1224
  #[cfg(target_arch = "x86")]
1225
- #[unsafe(naked)]
1226
- unsafe extern "C" fn call_gfx_resource_create(
1227
- _gfx_handler: *mut u8, _data_ptr: *const u8, _output: *mut u8,
1228
  ) -> *mut u8 {
1229
- core::arch::naked_asm!(
1230
- "movl 4(%esp), %ecx", // ECX = gfx_handler
1231
- "movl 8(%esp), %eax", // EAX = data_ptr
1232
- "pushl 12(%esp)", // push output (stdcall param for callee)
1233
- "calll *({addr})", // callee does RET 0x4, cleans our push
1234
- "retl", // return to caller (cdecl, caller cleans 3 params)
1235
- addr = sym GFX_RESOURCE_CREATE_ADDR,
1236
- options(att_syntax),
 
 
 
 
1237
  );
 
1238
  }
1239
 
1240
  /// Runtime address of GfxDir__FindEntry (set by init_constructor_addrs()).
@@ -1328,26 +1337,24 @@ unsafe extern "C" fn call_sprite_region_ctor(
1328
  ) -> *mut u8 {
1329
  // Stack on entry: [ret] [this] [ecx] [edx] [p2] [p3] [p4] [p5] [p6]
1330
  // Need: ECX=ecx, EDX=edx, push p6 p5 p4 p3 p2 this, call
 
 
1331
  core::arch::naked_asm!(
 
1332
  "pushl %ebx",
1333
- // Load ECX and EDX from our params
1334
- "movl 12(%esp), %ecx", // ecx param
1335
- "movl 16(%esp), %edx", // edx param
1336
- // Push stack params in reverse: p6, p5, p4, p3, p2, this
1337
- "movl 36(%esp), %eax", // p6
1338
- "pushl %eax",
1339
- "movl 36(%esp), %eax", // p5 (shifted by 4)
1340
- "pushl %eax",
1341
- "movl 36(%esp), %eax", // p4 (shifted by 8)
1342
- "pushl %eax",
1343
- "movl 36(%esp), %eax", // p3 (shifted by 12)
1344
- "pushl %eax",
1345
- "movl 36(%esp), %eax", // p2 (shifted by 16)
1346
- "pushl %eax",
1347
- "movl 28(%esp), %eax", // this (shifted by 20)
1348
- "pushl %eax",
1349
  "calll *({addr})", // fastcall, callee cleans 6Γ—4=24 bytes
1350
  "popl %ebx",
 
1351
  "retl",
1352
  addr = sym SPRITE_REGION_CTOR_ADDR,
1353
  options(att_syntax),
 
647
  f((*ddgame).display, 1);
648
  }
649
 
650
+ let _ = crate::log::log_line(&format!("[DDGame] gfx_mode={}", (*wrapper).gfx_mode));
651
  // ── GfxDir color entries DDGame+0x730C..0x732C ──
652
  if (*wrapper).gfx_mode != 0 {
653
  // GfxResource__Create: usercall(ECX=gfx_handler, EAX=0x66A3B4) + 1 stack, RET 0x4
 
669
  release(res);
670
  }
671
  } else {
672
+ // GFX_HANDLER_LOAD_SPRITES: SKIP for testing (gfx_mode==0 path)
 
 
 
 
673
  }
674
 
675
  let _ = crate::log::log_line("[DDGame] past color entries");
 
733
  // call_usercall_ecx(wrapper, LOAD_WEAPON_SPRITES_ADDR);
734
 
735
  // ── GfxResource: thiscall(ECX=gfx_handler) + EAX=name + 1 stack(output), RET 0x4 ──
736
+ // GfxResource: skip for now (stack corruption from bridge). PCLandscape handles null.
737
+ let gfx_resource: *mut u8 = core::ptr::null_mut();
738
+ #[allow(unused_assignments)]
739
+ if false {
740
+ let mut gfx_resource = gfx_resource;
741
  {
742
  let gfx_handler = (*wrapper)._field_4c0;
743
  let out_buf = wa_malloc(0x900);
 
747
  gfx_resource = call_gfx_resource_create(gfx_handler, rb(0x66A3C0) as *const u8, out_buf);
748
  let _ = crate::log::log_line(&format!("[DDGame] GfxResource=0x{:08X}", gfx_resource as u32));
749
  }
750
+ }
751
 
752
  // ── PCLandscape (alloc 0xB44, stdcall 11 params) ──
753
  let landscape = {
 
835
  (0x480, 0x2D, 0x08, 0x2D, 0x07, 0x2E, 0x07, gr),
836
  ];
837
 
838
+ // SKIP: testing if SpriteRegion bridge corrupts stack
839
+ if false {
840
  for &(offset, ecx, edx, p2, p3, p4, p5, p6) in &regions {
841
  let alloc = wa_malloc(0x9C);
842
  core::ptr::write_bytes(alloc, 0, 0x9C);
 
847
  };
848
  *((ddgame as *mut u8).add(offset as usize) as *mut *mut u8) = result;
849
  }
850
+ } // if false
851
  }
852
 
853
  // ── Landscape-derived value at DDGame+0x468 ──
 
1227
  /// Bridge to GfxResource__Create_Maybe (0x4F6300).
1228
  /// Convention: usercall(ECX=gfx_handler, EAX=data_ptr) + 1 stack(output), RET 0x4.
1229
  #[cfg(target_arch = "x86")]
1230
+ unsafe fn call_gfx_resource_create(
1231
+ gfx_handler: *mut u8, data_ptr: *const u8, output: *mut u8,
 
1232
  ) -> *mut u8 {
1233
+ let result: *mut u8;
1234
+ // Manual stack: sub 4 to make room, write output, call. Callee RET 0x4 cleans it.
1235
+ // Use sub/mov instead of push to avoid compiler stack tracking mismatch.
1236
+ core::arch::asm!(
1237
+ "sub esp, 4",
1238
+ "mov [esp], {output}",
1239
+ "call {addr}",
1240
+ output = in(reg) output,
1241
+ addr = in(reg) GFX_RESOURCE_CREATE_ADDR,
1242
+ inlateout("eax") data_ptr => result,
1243
+ in("ecx") gfx_handler,
1244
+ out("edx") _,
1245
  );
1246
+ result
1247
  }
1248
 
1249
  /// Runtime address of GfxDir__FindEntry (set by init_constructor_addrs()).
 
1337
  ) -> *mut u8 {
1338
  // Stack on entry: [ret] [this] [ecx] [edx] [p2] [p3] [p4] [p5] [p6]
1339
  // Need: ECX=ecx, EDX=edx, push p6 p5 p4 p3 p2 this, call
1340
+ // SpriteRegion__Constructor: fastcall(ECX, EDX) + 6 stack, RET 0x18
1341
+ // Params: (this, ecx, edx, p2, p3, p4, p5, p6)
1342
  core::arch::naked_asm!(
1343
+ "pushl %ebp",
1344
  "pushl %ebx",
1345
+ // Load ECX and EDX (shifted by 2 pushes = 8 bytes)
1346
+ "movl 16(%esp), %ecx", // ecx param (offset 4+8=12? No: 0=ebx,4=ebp,8=ret,12=this,16=ecx)
1347
+ "movl 20(%esp), %edx", // edx param
1348
+ // Push 6 stack params in reverse: p6, p5, p4, p3, p2, this
1349
+ "pushl 40(%esp)", // p6 (0=ebx,4=ebp,8=ret,...,40=p6)
1350
+ "pushl 40(%esp)", // p5 (shifted by 4)
1351
+ "pushl 40(%esp)", // p4 (shifted by 8)
1352
+ "pushl 40(%esp)", // p3 (shifted by 12)
1353
+ "pushl 40(%esp)", // p2 (shifted by 16)
1354
+ "pushl 32(%esp)", // this (shifted by 20)
 
 
 
 
 
 
1355
  "calll *({addr})", // fastcall, callee cleans 6Γ—4=24 bytes
1356
  "popl %ebx",
1357
+ "popl %ebp",
1358
  "retl",
1359
  addr = sym SPRITE_REGION_CTOR_ADDR,
1360
  options(att_syntax),