fix: _field_4dc reads game_info+0x44C, not net_game+0x44C
Browse filesGhidra mislabeled param8 as net_game in the DDGame constructor, but
the actual code reads game_info+0x44C for the loading progress total.
net_game is null in offline/bot replays; game_info is always valid.
Wrapper+0x04DC now matches original (0x39B). Only remaining wrapper
diff is +0x04D8 tick counter (off by 2).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/engine/ddgame.rs
CHANGED
|
@@ -992,10 +992,8 @@ pub unsafe fn create_ddgame(
|
|
| 992 |
|
| 993 |
// Verify game_info is set
|
| 994 |
let _ = crate::log::log_line(&format!(
|
| 995 |
-
"[DDGame] exposed: ddgame=0x{:08X}, game_info=0x{:08X},
|
| 996 |
-
ddgame as u32,
|
| 997 |
-
game_info as u32,
|
| 998 |
-
(*ddgame).game_info as u32,
|
| 999 |
));
|
| 1000 |
|
| 1001 |
// ββ 5. Set g_GameInfo global ββ
|
|
@@ -1237,12 +1235,13 @@ unsafe fn init_graphics_and_resources(
|
|
| 1237 |
|
| 1238 |
// ββ DDGameWrapper field inits ββ
|
| 1239 |
(*wrapper)._field_4d8 = 0;
|
| 1240 |
-
// Original
|
| 1241 |
-
//
|
| 1242 |
-
|
|
|
|
| 1243 |
(*wrapper)._field_4dc = 0x2AD;
|
| 1244 |
} else {
|
| 1245 |
-
let byte_val = *
|
| 1246 |
(*wrapper)._field_4dc = byte_val * 0x38 + 0x7E + 0x2AD;
|
| 1247 |
}
|
| 1248 |
(*wrapper)._field_4e0 = 0xFFFFFF9C; // -100
|
|
|
|
| 992 |
|
| 993 |
// Verify game_info is set
|
| 994 |
let _ = crate::log::log_line(&format!(
|
| 995 |
+
"[DDGame] exposed: ddgame=0x{:08X}, game_info=0x{:08X}, net_game=0x{:08X}",
|
| 996 |
+
ddgame as u32, game_info as u32, net_game as u32,
|
|
|
|
|
|
|
| 997 |
));
|
| 998 |
|
| 999 |
// ββ 5. Set g_GameInfo global ββ
|
|
|
|
| 1235 |
|
| 1236 |
// ββ DDGameWrapper field inits ββ
|
| 1237 |
(*wrapper)._field_4d8 = 0;
|
| 1238 |
+
// Original: if (display == NULL) iVar4=0 else iVar4=*(byte*)(game_info+0x44C)*0x38+0x7E
|
| 1239 |
+
// Ghidra mislabeled game_info as param8 β the original reads game_info, not net_game.
|
| 1240 |
+
// Our headless display is non-null, so use is_headless as the condition.
|
| 1241 |
+
if is_headless {
|
| 1242 |
(*wrapper)._field_4dc = 0x2AD;
|
| 1243 |
} else {
|
| 1244 |
+
let byte_val = *(game_info as *const u8).add(0x44C) as u32;
|
| 1245 |
(*wrapper)._field_4dc = byte_val * 0x38 + 0x7E + 0x2AD;
|
| 1246 |
}
|
| 1247 |
(*wrapper)._field_4e0 = 0xFFFFFF9C; // -100
|
crates/openwa-wormkit/src/replacements/game_session.rs
CHANGED
|
@@ -107,9 +107,18 @@ pub(crate) unsafe fn construct_ddgame_wrapper(
|
|
| 107 |
let timer_obj = (*session).timer_obj;
|
| 108 |
let net_game = (*session).net_game;
|
| 109 |
|
|
|
|
|
|
|
| 110 |
let _ = log_line(&format!(
|
| 111 |
-
"[GameSession]
|
| 112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
));
|
| 114 |
|
| 115 |
// Toggle: use Rust constructor (true) or original (false)
|
|
|
|
| 107 |
let timer_obj = (*session).timer_obj;
|
| 108 |
let net_game = (*session).net_game;
|
| 109 |
|
| 110 |
+
// Debug: dump session fields around net_game (0xBC-0xC8)
|
| 111 |
+
let session_base = session as *const u8;
|
| 112 |
let _ = log_line(&format!(
|
| 113 |
+
"[GameSession] session=0x{:08X} +0xBC=0x{:08X} +0xC0=0x{:08X} +0xC4=0x{:08X}",
|
| 114 |
+
session as u32,
|
| 115 |
+
*(session_base.add(0xBC) as *const u32),
|
| 116 |
+
*(session_base.add(0xC0) as *const u32),
|
| 117 |
+
*(session_base.add(0xC4) as *const u32),
|
| 118 |
+
));
|
| 119 |
+
let _ = log_line(&format!(
|
| 120 |
+
"[GameSession] display=0x{:08X}, net_game=0x{:08X}, timer=0x{:08X}, game_info(EDI)=0x{:08X}",
|
| 121 |
+
display as u32, net_game as u32, timer_obj as u32, game_info as u32,
|
| 122 |
));
|
| 123 |
|
| 124 |
// Toggle: use Rust constructor (true) or original (false)
|