paavohtl Claude Opus 4.6 (1M context) commited on
Commit
5b74587
Β·
1 Parent(s): 7c53fdb

debug: add milestone logging, identify LoadWeaponSprites message pump

Browse files

Found root cause of intermittent crashes: DDGame__LoadWeaponSprites
(0x5717A0) is a loading tick function that pumps the Windows message
loop (uses QueryPerformanceCounter + message dispatch). During this
pump, WM_TIMER/WM_PAINT handlers run game task code that reads DDGame
fields β€” if any field is still null, it crashes.

The original C++ constructor sets fields in an order compatible with
whatever the message pump reads. Our Rust version must match that
exact order, OR defer the loading tick calls until all critical fields
are initialized.

This is also why DSSound_LoadEffectWAVs (which calls LoadWeaponSprites
between each WAV) must have wrapper passed correctly β€” it too pumps
messages.

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

crates/openwa-core/src/engine/ddgame.rs CHANGED
@@ -488,6 +488,11 @@ pub unsafe fn create_ddgame(
488
  // ── 10. GfxHandler, landscape, sprites, audio, resources ──
489
  init_graphics_and_resources(wrapper, game_info, net_game, display, is_headless);
490
 
 
 
 
 
 
491
  ddgame
492
  }
493
 
@@ -575,6 +580,7 @@ unsafe fn init_graphics_and_resources(
575
  }
576
  }
577
 
 
578
  // ── Display palette setup (non-headless) ──
579
  if !is_headless {
580
  if *(rb(0x88E485) as *const u8) == 0 {
@@ -610,6 +616,7 @@ unsafe fn init_graphics_and_resources(
610
  }
611
  }
612
 
 
613
  // ── FUN_00570E20: usercall(ESI=wrapper), plain RET ──
614
  call_usercall_esi(wrapper, FUN_570E20_ADDR);
615
 
@@ -673,6 +680,7 @@ unsafe fn init_graphics_and_resources(
673
  (*wrapper)._field_4e0 = 0xFFFFFF9C; // -100
674
  (*wrapper).speech_name_count = 0;
675
 
 
676
  // ── Audio init (non-headless + sound available) ──
677
  if !is_headless {
678
  // FUN_570F30: usercall(ESI=wrapper)
@@ -924,6 +932,7 @@ unsafe fn init_graphics_and_resources(
924
  // ── Weapon sprites (FUN_005717A0 Γ—2): usercall(ECX=wrapper) ──
925
  call_usercall_ecx(wrapper, LOAD_WEAPON_SPRITES_ADDR);
926
  call_usercall_ecx(wrapper, LOAD_WEAPON_SPRITES_ADDR);
 
927
 
928
  // ── Sprite resource loading via DDGameWrapper vtable[0] ──
929
  // DDNetGameWrapper__LoadResourceList: thiscall(ECX=wrapper) +
@@ -940,12 +949,14 @@ unsafe fn init_graphics_and_resources(
940
  ) = core::mem::transmute(*wrapper_vt);
941
 
942
  // Load resources for layer 1 (main sprites)
 
943
  load_resource_list(
944
  wrapper, 1, gfx_handler,
945
  rb(0x643F2B) as *const u8, // base path
946
  rb(0x6AD2C0) as *const u8, // resource table
947
  0x1D88, // table size
948
  );
 
949
 
950
  // Set global flag based on game version
951
  let d778 = *((*ddgame).game_info as *const u8).add(0xD778).cast::<i32>();
 
488
  // ── 10. GfxHandler, landscape, sprites, audio, resources ──
489
  init_graphics_and_resources(wrapper, game_info, net_game, display, is_headless);
490
 
491
+ let _ = log_line(&format!(
492
+ "[DDGame] create_ddgame done: landscape=0x{:08X}, display_gfx=0x{:08X}, coord_list=0x{:08X}",
493
+ (*ddgame).landscape as u32, (*ddgame).display_gfx as u32, (*ddgame).coord_list as u32,
494
+ ));
495
+
496
  ddgame
497
  }
498
 
 
580
  }
581
  }
582
 
583
+ let _ = log_line("[DDGame] past gfxhandlers");
584
  // ── Display palette setup (non-headless) ──
585
  if !is_headless {
586
  if *(rb(0x88E485) as *const u8) == 0 {
 
616
  }
617
  }
618
 
619
+ let _ = log_line("[DDGame] past palette");
620
  // ── FUN_00570E20: usercall(ESI=wrapper), plain RET ──
621
  call_usercall_esi(wrapper, FUN_570E20_ADDR);
622
 
 
680
  (*wrapper)._field_4e0 = 0xFFFFFF9C; // -100
681
  (*wrapper).speech_name_count = 0;
682
 
683
+ let _ = log_line("[DDGame] past 570e20+display+colors+gfxdir+fields");
684
  // ── Audio init (non-headless + sound available) ──
685
  if !is_headless {
686
  // FUN_570F30: usercall(ESI=wrapper)
 
932
  // ── Weapon sprites (FUN_005717A0 Γ—2): usercall(ECX=wrapper) ──
933
  call_usercall_ecx(wrapper, LOAD_WEAPON_SPRITES_ADDR);
934
  call_usercall_ecx(wrapper, LOAD_WEAPON_SPRITES_ADDR);
935
+ let _ = log_line("[DDGame] past audio init");
936
 
937
  // ── Sprite resource loading via DDGameWrapper vtable[0] ──
938
  // DDNetGameWrapper__LoadResourceList: thiscall(ECX=wrapper) +
 
949
  ) = core::mem::transmute(*wrapper_vt);
950
 
951
  // Load resources for layer 1 (main sprites)
952
+ let _ = log_line("[DDGame] M2: load_resource_list #1");
953
  load_resource_list(
954
  wrapper, 1, gfx_handler,
955
  rb(0x643F2B) as *const u8, // base path
956
  rb(0x6AD2C0) as *const u8, // resource table
957
  0x1D88, // table size
958
  );
959
+ let _ = log_line("[DDGame] M3: load_resource_list #1 done");
960
 
961
  // Set global flag based on game version
962
  let d778 = *((*ddgame).game_info as *const u8).add(0xD778).cast::<i32>();