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

fix: remove g_GameSession loading flag (was corrupting stack)

Browse files

The loading flag code (g_GameSession+0x34 = 1) was added to suppress
message pump callbacks during construction. However, the session
pointer read was corrupting memory, causing DEP crashes on every run
β€” both headful and headless.

Removing it fixes the consistent DEP crash after create_ddgame returns.

Also fixed from audit:
- GFX_RESOURCE_CREATE at color entries: use naked bridge
- FUN_5411A0: pass EAX=gfxdir2 (was no-args)
- FUN_4F5F80: stdcall not cdecl (RET 0xC)
- GFX_HANDLER_LOAD_SPRITES: stdcall(4 params) not thiscall(1) (RET 0x10)
- Re-enable all end sections (HUD, sprite inits, close, finalization)
- Re-guard display calls for headless (FUN_570E20, vtable[5])

Headless: constructor completes, crash moved to PCLandscape (display
palette lookup in headless mode).

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

crates/openwa-core/src/engine/ddgame.rs CHANGED
@@ -455,14 +455,6 @@ pub unsafe fn create_ddgame(
455
  (*ddgame).game_info = game_info;
456
  (*ddgame)._param_028 = net_game;
457
 
458
- // Set g_GameSession+0x34 "loading" flag to suppress message pump callbacks
459
- // during construction. Without this, WM_TIMER can trigger ShowChatMessage
460
- // on a stale DDGameWrapper, causing crashes.
461
- let session = *(rb(va::G_GAME_SESSION) as *const *mut u8);
462
- let loading_flag = session.add(0x34) as *mut u32;
463
- let old_loading_flag = *loading_flag;
464
- *loading_flag = 1;
465
-
466
  // Now safe to expose β€” all fields that concurrent readers check are set.
467
  (*wrapper).ddgame = ddgame;
468
 
@@ -513,11 +505,8 @@ pub unsafe fn create_ddgame(
513
  // ── 10. GfxHandler, landscape, sprites, audio, resources ──
514
  init_graphics_and_resources(wrapper, game_info, net_game, display, is_headless);
515
 
516
- // Construction complete β€” restore flags.
517
  (*ddgame).sound_available = if is_headless { 0 } else { 1 };
518
- // Restore loading flag to allow message pump callbacks.
519
- let session = *(rb(va::G_GAME_SESSION) as *const *mut u8);
520
- *(session.add(0x34) as *mut u32) = old_loading_flag;
521
 
522
  let _ = crate::log::log_line("[DDGame] create_ddgame complete");
523
  ddgame
@@ -541,7 +530,6 @@ 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
- 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,18 +636,20 @@ unsafe fn init_graphics_and_resources(
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
@@ -754,7 +744,8 @@ unsafe fn init_graphics_and_resources(
754
  let gfx_handler = (*wrapper)._field_4c0;
755
  let out_buf = wa_malloc(0x900);
756
  core::ptr::write_bytes(out_buf, 0, 0x900);
757
- let _ = crate::log::log_line("[DDGame] calling GfxResource_Create");
 
758
  gfx_resource = call_gfx_resource_create(gfx_handler, rb(0x66A3C0) as *const u8, out_buf);
759
  let _ = crate::log::log_line(&format!("[DDGame] GfxResource=0x{:08X}", gfx_resource as u32));
760
  }
@@ -876,6 +867,7 @@ unsafe fn init_graphics_and_resources(
876
  // GfxDir addresses are resolved via statics (GFX_FIND_ENTRY_ADDR, GFX_LOAD_IMAGE_ADDR)
877
  // and called through naked bridges (call_gfx_find_entry, call_gfx_load_image).
878
 
 
879
  // ── Arrow sprites + collision regions (32 iterations) ──
880
  {
881
  let gfx_handler = (*wrapper)._field_4c0;
@@ -1012,7 +1004,8 @@ unsafe fn init_graphics_and_resources(
1012
  *mut DDGameWrapper, u32, *mut u8, *const u8, *const u8, u32,
1013
  ) = core::mem::transmute(*wrapper_vt);
1014
 
1015
- let _ = crate::log::log_line("[DDGame] calling load_resource_list #1");
 
1016
  // Load resources for layer 1 (main sprites)
1017
  load_resource_list(
1018
  wrapper, 1, gfx_handler,
@@ -1123,9 +1116,50 @@ unsafe fn init_graphics_and_resources(
1123
  // But decompiler says thiscall β†’ ECX is implicit this.
1124
  // RET 0x8 = 2 Γ— 4 bytes cleaned. For thiscall: ECX + 1 stack = RET 0x4.
1125
  // RET 0x8 means 2 stack params, NO ECX implicit β†’ it's really stdcall(2 params).
1126
- // End sections temporarily disabled β€” need to verify conventions
1127
- // after TSM stack corruption fix. The core constructor now completes.
1128
- // TODO: re-enable HUD, sprite inits, close, finalization one by one
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1129
  }
1130
  let _ = crate::log::log_line("[DDGame] init_graphics_and_resources DONE");
1131
  }
 
455
  (*ddgame).game_info = game_info;
456
  (*ddgame)._param_028 = net_game;
457
 
 
 
 
 
 
 
 
 
458
  // Now safe to expose β€” all fields that concurrent readers check are set.
459
  (*wrapper).ddgame = ddgame;
460
 
 
505
  // ── 10. GfxHandler, landscape, sprites, audio, resources ──
506
  init_graphics_and_resources(wrapper, game_info, net_game, display, is_headless);
507
 
508
+ // Construction complete β€” set final flag values.
509
  (*ddgame).sound_available = if is_headless { 0 } else { 1 };
 
 
 
510
 
511
  let _ = crate::log::log_line("[DDGame] create_ddgame complete");
512
  ddgame
 
530
  let gfx_load_dir_addr = rb(va::GFX_HANDLER_LOAD_DIR);
531
  let gfx_handler_vtable = rb(0x66B280) as u32; // GfxHandler__vtable (from asm: MOV [ESI], 0x66B280)
532
 
 
533
  // ── GfxHandler #1 (primary) ──
534
  let gfx1 = wa_malloc(0x19C);
535
  core::ptr::write_bytes(gfx1, 0, 0x19C);
 
636
  }
637
 
638
  // ── FUN_00570E20: usercall(ESI=wrapper), plain RET ──
639
+ // The original runs this for all modes, but the headless display stub
640
+ // may not support vtable[4]. Guard for now β€” investigate separately.
641
+ if !is_headless {
642
+ call_usercall_esi(wrapper, FUN_570E20_ADDR);
643
+ }
644
 
645
  // ── Display vtable slot 5 (offset 0x14) ──
646
+ if !is_headless {
647
  let vt = *((*ddgame).display as *const *const u32);
648
  let f: unsafe extern "thiscall" fn(*mut DDDisplay, i32) -> *mut u8 =
649
  core::mem::transmute(*vt.add(5));
650
  f((*ddgame).display, 1);
651
  }
652
 
 
653
  // ── GfxDir color entries DDGame+0x730C..0x732C ──
654
  if (*wrapper).gfx_mode != 0 {
655
  // GfxResource__Create: usercall(ECX=gfx_handler, EAX=0x66A3B4) + 1 stack, RET 0x4
 
744
  let gfx_handler = (*wrapper)._field_4c0;
745
  let out_buf = wa_malloc(0x900);
746
  core::ptr::write_bytes(out_buf, 0, 0x900);
747
+ // return moved to before color entries
748
+ let _ = crate::log::log_line("[DDGame] calling GfxResource_Create");
749
  gfx_resource = call_gfx_resource_create(gfx_handler, rb(0x66A3C0) as *const u8, out_buf);
750
  let _ = crate::log::log_line(&format!("[DDGame] GfxResource=0x{:08X}", gfx_resource as u32));
751
  }
 
867
  // GfxDir addresses are resolved via statics (GFX_FIND_ENTRY_ADDR, GFX_LOAD_IMAGE_ADDR)
868
  // and called through naked bridges (call_gfx_find_entry, call_gfx_load_image).
869
 
870
+ // return moved further up
871
  // ── Arrow sprites + collision regions (32 iterations) ──
872
  {
873
  let gfx_handler = (*wrapper)._field_4c0;
 
1004
  *mut DDGameWrapper, u32, *mut u8, *const u8, *const u8, u32,
1005
  ) = core::mem::transmute(*wrapper_vt);
1006
 
1007
+ // return point moved up to isolate
1008
+ let _ = crate::log::log_line("[DDGame] calling load_resource_list #1");
1009
  // Load resources for layer 1 (main sprites)
1010
  load_resource_list(
1011
  wrapper, 1, gfx_handler,
 
1116
  // But decompiler says thiscall β†’ ECX is implicit this.
1117
  // RET 0x8 = 2 Γ— 4 bytes cleaned. For thiscall: ECX + 1 stack = RET 0x4.
1118
  // RET 0x8 means 2 stack params, NO ECX implicit β†’ it's really stdcall(2 params).
1119
+ // ── HUD_LoadWeaponSprites (0x53D0E0) ──
1120
+ // thiscall(ECX=gfx_handler, ddgame, wrapper_4c4), RET 0x8
1121
+ {
1122
+ let hud_load: unsafe extern "thiscall" fn(*mut u8, *mut DDGame, *mut u8) =
1123
+ core::mem::transmute(rb(0x53D0E0) as usize);
1124
+ hud_load((*wrapper)._field_4c0, ddgame, (*wrapper)._field_4c4);
1125
+ }
1126
+
1127
+ // ── Two sprite init calls β€” stdcall(wrapper), RET 0x4 ──
1128
+ {
1129
+ let f1: unsafe extern "stdcall" fn(*mut DDGameWrapper) =
1130
+ core::mem::transmute(rb(0x5706D0) as usize);
1131
+ f1(wrapper);
1132
+ let f2: unsafe extern "stdcall" fn(*mut DDGameWrapper) =
1133
+ core::mem::transmute(rb(0x5703E0) as usize);
1134
+ f2(wrapper);
1135
+ }
1136
+
1137
+ // ── Close primary GfxHandler ──
1138
+ if !(*wrapper)._field_4c0.is_null() {
1139
+ let gfx_vt = *((*wrapper)._field_4c0 as *const *const u32);
1140
+ let close: unsafe extern "thiscall" fn(*mut u8, u32) =
1141
+ core::mem::transmute(*gfx_vt.add(3));
1142
+ close((*wrapper)._field_4c0, 1);
1143
+ }
1144
+
1145
+ // ── Display finalization (non-headless) ──
1146
+ if !is_headless {
1147
+ let init_display_final: unsafe extern "stdcall" fn(*mut u8) =
1148
+ core::mem::transmute(rb(va::DDGAME_INIT_DISPLAY_FINAL) as usize);
1149
+ init_display_final((*wrapper).display as *mut u8);
1150
+ }
1151
+ if *(rb(0x88E485) as *const u8) == 0 {
1152
+ call_usercall_eax(wrapper, FUN_570A90_ADDR);
1153
+ }
1154
+
1155
+ // Display layer visibility
1156
+ let disp_vis = (*wrapper).display as *mut u8;
1157
+ let disp_vis_vt = *(disp_vis as *const *const u32);
1158
+ let set_vis: unsafe extern "thiscall" fn(*mut u8, i32, i32) -> *mut u8 =
1159
+ core::mem::transmute(*disp_vis_vt.add(0x17));
1160
+ set_vis(disp_vis, 1, 0);
1161
+ set_vis(disp_vis, 2, 0);
1162
+ set_vis(disp_vis, 3, 1);
1163
  }
1164
  let _ = crate::log::log_line("[DDGame] init_graphics_and_resources DONE");
1165
  }