fix: add missing LoadSprites fallback in DDGame constructor for gfx_mode=0
Browse filesThe 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).
|