paavohtl Claude Opus 4.6 (1M context) commited on
Commit
cbecb43
Β·
1 Parent(s): 5587829

fix: add missing LoadSprites fallback in DDGame constructor for gfx_mode=0

Browse files

The original DDGame constructor calls GfxHandler__LoadSprites (0x570B50)
when gfx_mode=0 (headless) or when GfxResource__Create fails. Our Rust
replacement skipped this call entirely, leaving DDGame+0x730C (color
table) unpopulated. This caused checksum mismatches in the longbow
replay.

Added the missing call with correct parameters (primary_gfx_dir as 4th
param for gfx_mode=0, 0 for resource creation failure). The color table
is still not fully matching vanilla (0xFFFFFFFF vs 1-9) β€” further
investigation needed in LoadSprites or CTaskGameState initialization.

Also: cleaned up bisect debug code, removed unused call_original
bridge functions.

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

crates/openwa-core/src/engine/ddgame.rs CHANGED
@@ -849,8 +849,10 @@ unsafe fn init_graphics_and_resources(
849
  let layer_ctx = DDDisplay::set_active_layer((*ddgame).display, 1);
850
 
851
  // ── GfxDir color entries DDGame+0x730C..0x732C ──
 
 
 
852
  if (*wrapper).gfx_mode != 0 {
853
- // The layer_ctx is used as the output buffer, not a plain stack alloc.
854
  let res = gfx_resource_create(
855
  (*wrapper).primary_gfx_dir,
856
  rb(va::STR_COLOURS_IMG) as *const c_char,
@@ -858,18 +860,31 @@ unsafe fn init_graphics_and_resources(
858
  );
859
  if !res.is_null() {
860
  let rvt = *(res as *const *const u32);
861
- // vtable[4] = get_pixel: thiscall(this, x, y) -> color, RET 0x8.
862
  let get_color: unsafe extern "thiscall" fn(*mut u8, u32, u32) -> u32 =
863
  core::mem::transmute(*rvt.add(4));
864
  for i in 0..9u32 {
865
  (*ddgame).gfx_color_table[i as usize] = get_color(res, i, 0);
866
  }
867
- // DisplayGfx__vmethod_3: thiscall(this, byte param_2), RET 4.
868
- // param_2 & 1 = free the object itself.
869
  let release: unsafe extern "thiscall" fn(*mut u8, u8) =
870
  core::mem::transmute(*rvt.add(3));
871
  release(res, 1);
 
 
 
 
 
 
 
 
872
  }
 
 
 
 
 
 
 
 
873
  }
874
 
875
  // ── Secondary GfxDir object (DDGame+0x2C, conditional) ──
 
849
  let layer_ctx = DDDisplay::set_active_layer((*ddgame).display, 1);
850
 
851
  // ── GfxDir color entries DDGame+0x730C..0x732C ──
852
+ // Original logic: if gfx_mode!=0, try GfxResource__Create for colours.img.
853
+ // If gfx_mode==0 OR resource creation fails, fall back to LoadSprites.
854
+ // The fallback's 4th param is primary_gfx_dir when gfx_mode==0, or 0 on resource fail.
855
  if (*wrapper).gfx_mode != 0 {
 
856
  let res = gfx_resource_create(
857
  (*wrapper).primary_gfx_dir,
858
  rb(va::STR_COLOURS_IMG) as *const c_char,
 
860
  );
861
  if !res.is_null() {
862
  let rvt = *(res as *const *const u32);
 
863
  let get_color: unsafe extern "thiscall" fn(*mut u8, u32, u32) -> u32 =
864
  core::mem::transmute(*rvt.add(4));
865
  for i in 0..9u32 {
866
  (*ddgame).gfx_color_table[i as usize] = get_color(res, i, 0);
867
  }
 
 
868
  let release: unsafe extern "thiscall" fn(*mut u8, u8) =
869
  core::mem::transmute(*rvt.add(3));
870
  release(res, 1);
871
+ } else {
872
+ // Resource creation failed β€” fallback with param4=0
873
+ wa_load_sprites(
874
+ wrapper,
875
+ (*ddgame).gfx_sprite_data.as_mut_ptr(),
876
+ (*game_info).display_flags,
877
+ 0,
878
+ );
879
  }
880
+ } else {
881
+ // gfx_mode==0 (headless): fallback LoadSprites with param4=primary_gfx_dir
882
+ wa_load_sprites(
883
+ wrapper,
884
+ (*ddgame).gfx_sprite_data.as_mut_ptr(),
885
+ (*game_info).display_flags,
886
+ (*wrapper).primary_gfx_dir as u32,
887
+ );
888
  }
889
 
890
  // ── Secondary GfxDir object (DDGame+0x2C, conditional) ──
crates/openwa-wormkit/src/replacements/game_session.rs CHANGED
@@ -95,6 +95,7 @@ pub(crate) unsafe fn construct_ddgame_wrapper(
95
  core::mem::transmute(rb(va::DDGAME_INIT_GAME_STATE) as usize);
96
  init_state(this);
97
 
 
98
  let _ = log_line(&format!(
99
  "[GameSession] DDGameWrapper::Constructor done: wrapper=0x{:08X} ddgame=0x{:08X}",
100
  this as u32, (*this).ddgame as u32,
@@ -106,9 +107,7 @@ pub(crate) unsafe fn construct_ddgame_wrapper(
106
  pub fn install() -> Result<(), String> {
107
  unsafe {
108
  INIT_REPLAY_ADDR = rb(va::DDGAMEWRAPPER_INIT_REPLAY);
109
- // Initialize runtime addresses for create_ddgame bridges.
110
  init_constructor_addrs();
111
- // DDGameWrapper__Constructor is fully converted β€” trap the original.
112
  hook::install_trap!("DDGameWrapper__Constructor", va::CONSTRUCT_DD_GAME_WRAPPER);
113
  }
114
  Ok(())
 
95
  core::mem::transmute(rb(va::DDGAME_INIT_GAME_STATE) as usize);
96
  init_state(this);
97
 
98
+
99
  let _ = log_line(&format!(
100
  "[GameSession] DDGameWrapper::Constructor done: wrapper=0x{:08X} ddgame=0x{:08X}",
101
  this as u32, (*this).ddgame as u32,
 
107
  pub fn install() -> Result<(), String> {
108
  unsafe {
109
  INIT_REPLAY_ADDR = rb(va::DDGAMEWRAPPER_INIT_REPLAY);
 
110
  init_constructor_addrs();
 
111
  hook::install_trap!("DDGameWrapper__Constructor", va::CONSTRUCT_DD_GAME_WRAPPER);
112
  }
113
  Ok(())
crates/openwa-wormkit/src/replacements/weapon.rs CHANGED
@@ -180,22 +180,6 @@ unsafe extern "C" fn trampoline_fire_weapon() {
180
  );
181
  }
182
 
183
- /// Call the original FireWeapon (trampoline) for debugging.
184
- /// Restores EAX=entry, ECX=local_struct, stack=worm.
185
- #[unsafe(naked)]
186
- unsafe extern "C" fn call_original_fire_weapon(
187
- _entry: *const WeaponEntry, _local_struct: u32, _worm: *mut CTaskWorm,
188
- ) {
189
- core::arch::naked_asm!(
190
- "mov eax, [esp+4]", // entry
191
- "mov ecx, [esp+8]", // local_struct
192
- "push [esp+12]", // worm
193
- "call [{orig}]",
194
- "ret",
195
- orig = sym ORIG_FIRE_WEAPON,
196
- );
197
- }
198
-
199
  /// Rust implementation of FireWeapon dispatch.
200
  ///
201
  /// `entry`: EAX = active WeaponEntry pointer (from CTaskWorm+0x36C).
 
180
  );
181
  }
182
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  /// Rust implementation of FireWeapon dispatch.
184
  ///
185
  /// `entry`: EAX = active WeaponEntry pointer (from CTaskWorm+0x36C).