refactor: improve naming, typing, and readability in hardware_init
Browse files- Fix misleading param names: bridge _hwndβ_param3, construct_ddgame_wrapper
gfxβkeyboard, musicβstreaming_audio, networkβinput_ctrl
- Add GameSession fields: init_flag (0x24), fullscreen_flag (0x28)
- Expand Palette struct with _field_004 and 0x28 size assert
- Type WABox allocations: DisplayGfx, StreamingAudio, DDNetGameWrapper,
GameStats ctors use typed pointers instead of *mut u8
- Rename d778_valβcrosshair_threshold, document game_info+4 offset
- Add PLAY_VSLOT constant for IDirectSoundBuffer vtable call
- Remove redundant write_bytes for DDKeyboard (covered by WABox zeroing)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/game_session.rs
CHANGED
|
@@ -29,7 +29,11 @@ pub struct GameSession {
|
|
| 29 |
pub _unknown_004: [u8; 4],
|
| 30 |
/// 0x008: main window HWND, stored from `hWnd` in `GameSession__Run`
|
| 31 |
pub hwnd: u32,
|
| 32 |
-
pub _unknown_00c: [u8;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
/// 0x02C: set to 1 on init (role TBD)
|
| 34 |
pub flag_2c: u32,
|
| 35 |
pub _unknown_030: [u8; 8],
|
|
|
|
| 29 |
pub _unknown_004: [u8; 4],
|
| 30 |
/// 0x008: main window HWND, stored from `hWnd` in `GameSession__Run`
|
| 31 |
pub hwnd: u32,
|
| 32 |
+
pub _unknown_00c: [u8; 0x18],
|
| 33 |
+
/// 0x024: Always set to 1 by GameEngine__InitHardware.
|
| 34 |
+
pub init_flag: u32,
|
| 35 |
+
/// 0x028: Fullscreen flag β `(GameInfo.home_lock != 0) as u32`.
|
| 36 |
+
pub fullscreen_flag: u32,
|
| 37 |
/// 0x02C: set to 1 on init (role TBD)
|
| 38 |
pub flag_2c: u32,
|
| 39 |
pub _unknown_030: [u8; 8],
|
crates/openwa-core/src/palette.rs
CHANGED
|
@@ -2,10 +2,17 @@
|
|
| 2 |
///
|
| 3 |
/// Vtable: 0x66A2E4.
|
| 4 |
/// Passed as "palette" parameter to DDGame__Constructor, stored at DDGame+0x010.
|
|
|
|
| 5 |
///
|
| 6 |
-
///
|
| 7 |
#[repr(C)]
|
| 8 |
pub struct Palette {
|
| 9 |
/// 0x000: Vtable pointer (0x66A2E4)
|
| 10 |
pub vtable: *mut u8,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
}
|
|
|
|
|
|
|
|
|
| 2 |
///
|
| 3 |
/// Vtable: 0x66A2E4.
|
| 4 |
/// Passed as "palette" parameter to DDGame__Constructor, stored at DDGame+0x010.
|
| 5 |
+
/// Size: 0x28 bytes.
|
| 6 |
///
|
| 7 |
+
/// PARTIAL: Only vtable and first init field known.
|
| 8 |
#[repr(C)]
|
| 9 |
pub struct Palette {
|
| 10 |
/// 0x000: Vtable pointer (0x66A2E4)
|
| 11 |
pub vtable: *mut u8,
|
| 12 |
+
/// 0x004: Initialized to 0xFFFFFFFF during inline construction.
|
| 13 |
+
pub _field_004: u32,
|
| 14 |
+
/// 0x008-0x027: Unknown
|
| 15 |
+
pub _unknown_008: [u8; 0x20],
|
| 16 |
}
|
| 17 |
+
|
| 18 |
+
const _: () = assert!(core::mem::size_of::<Palette>() == 0x28);
|
crates/openwa-wormkit/src/replacements/game_session.rs
CHANGED
|
@@ -98,15 +98,15 @@ unsafe extern "stdcall" fn ddgame_constructor_call(
|
|
| 98 |
_this: *mut DDGameWrapper,
|
| 99 |
_display: *mut DDDisplay,
|
| 100 |
_sound: *mut DSSound,
|
| 101 |
-
|
| 102 |
_palette: *mut Palette,
|
| 103 |
-
|
| 104 |
_timer_obj: *mut u8,
|
| 105 |
_net_game: *mut u8,
|
| 106 |
_game_info: *mut u8,
|
| 107 |
) -> *mut u8 {
|
| 108 |
core::arch::naked_asm!(
|
| 109 |
-
"movl {ecx_val}, %ecx", // ECX =
|
| 110 |
"jmpl *({fn})", // tail-jump; DDGame::ctor's RET 0x24 returns to caller
|
| 111 |
ecx_val = sym DDGAME_CTOR_ECX,
|
| 112 |
fn = sym DDGAME_CTOR_ADDR,
|
|
@@ -125,10 +125,10 @@ unsafe extern "cdecl" fn ctor_impl(
|
|
| 125 |
this: *mut DDGameWrapper,
|
| 126 |
display: *mut DDDisplay,
|
| 127 |
sound: *mut DSSound,
|
| 128 |
-
|
| 129 |
palette: *mut Palette,
|
| 130 |
-
|
| 131 |
-
|
| 132 |
) -> *mut DDGameWrapper {
|
| 133 |
let game_info = GAME_INFO as *mut u8;
|
| 134 |
|
|
@@ -147,10 +147,10 @@ unsafe extern "cdecl" fn ctor_impl(
|
|
| 147 |
let timer_obj = (*session).timer_obj;
|
| 148 |
let net_game = (*session).net_game;
|
| 149 |
|
| 150 |
-
// Store
|
| 151 |
-
DDGAME_CTOR_ECX =
|
| 152 |
ddgame_constructor_call(
|
| 153 |
-
this, display, sound,
|
| 154 |
timer_obj, net_game, game_info,
|
| 155 |
);
|
| 156 |
|
|
@@ -219,13 +219,13 @@ pub(crate) unsafe fn construct_ddgame_wrapper(
|
|
| 219 |
this: *mut DDGameWrapper,
|
| 220 |
display: *mut DDDisplay,
|
| 221 |
sound: *mut DSSound,
|
| 222 |
-
|
| 223 |
palette: *mut Palette,
|
| 224 |
-
|
| 225 |
-
|
| 226 |
) -> *mut DDGameWrapper {
|
| 227 |
GAME_INFO = game_info as u32;
|
| 228 |
-
ctor_impl(this, display, sound,
|
| 229 |
}
|
| 230 |
|
| 231 |
pub fn install() -> Result<(), String> {
|
|
|
|
| 98 |
_this: *mut DDGameWrapper,
|
| 99 |
_display: *mut DDDisplay,
|
| 100 |
_sound: *mut DSSound,
|
| 101 |
+
_keyboard: *mut u8,
|
| 102 |
_palette: *mut Palette,
|
| 103 |
+
_streaming_audio: *mut u8,
|
| 104 |
_timer_obj: *mut u8,
|
| 105 |
_net_game: *mut u8,
|
| 106 |
_game_info: *mut u8,
|
| 107 |
) -> *mut u8 {
|
| 108 |
core::arch::naked_asm!(
|
| 109 |
+
"movl {ecx_val}, %ecx", // ECX = input_ctrl (implicit param for DDGame::ctor)
|
| 110 |
"jmpl *({fn})", // tail-jump; DDGame::ctor's RET 0x24 returns to caller
|
| 111 |
ecx_val = sym DDGAME_CTOR_ECX,
|
| 112 |
fn = sym DDGAME_CTOR_ADDR,
|
|
|
|
| 125 |
this: *mut DDGameWrapper,
|
| 126 |
display: *mut DDDisplay,
|
| 127 |
sound: *mut DSSound,
|
| 128 |
+
keyboard: *mut u8,
|
| 129 |
palette: *mut Palette,
|
| 130 |
+
streaming_audio: *mut u8,
|
| 131 |
+
input_ctrl: *mut u8,
|
| 132 |
) -> *mut DDGameWrapper {
|
| 133 |
let game_info = GAME_INFO as *mut u8;
|
| 134 |
|
|
|
|
| 147 |
let timer_obj = (*session).timer_obj;
|
| 148 |
let net_game = (*session).net_game;
|
| 149 |
|
| 150 |
+
// Store input_ctrl as the implicit ECX for DDGame::ctor, then tail-jump-call.
|
| 151 |
+
DDGAME_CTOR_ECX = input_ctrl as u32;
|
| 152 |
ddgame_constructor_call(
|
| 153 |
+
this, display, sound, keyboard, palette, streaming_audio,
|
| 154 |
timer_obj, net_game, game_info,
|
| 155 |
);
|
| 156 |
|
|
|
|
| 219 |
this: *mut DDGameWrapper,
|
| 220 |
display: *mut DDDisplay,
|
| 221 |
sound: *mut DSSound,
|
| 222 |
+
keyboard: *mut u8,
|
| 223 |
palette: *mut Palette,
|
| 224 |
+
streaming_audio: *mut u8,
|
| 225 |
+
input_ctrl: *mut u8,
|
| 226 |
) -> *mut DDGameWrapper {
|
| 227 |
GAME_INFO = game_info as u32;
|
| 228 |
+
ctor_impl(this, display, sound, keyboard, palette, streaming_audio, input_ctrl)
|
| 229 |
}
|
| 230 |
|
| 231 |
pub fn install() -> Result<(), String> {
|
crates/openwa-wormkit/src/replacements/hardware_init.rs
CHANGED
|
@@ -53,6 +53,9 @@ use openwa_core::palette::Palette;
|
|
| 53 |
use openwa_core::input_ctrl::InputCtrl;
|
| 54 |
use openwa_core::game_timer::GameTimer;
|
| 55 |
use openwa_core::game_stats::GameStats;
|
|
|
|
|
|
|
|
|
|
| 56 |
use openwa_core::wa_alloc::WABox;
|
| 57 |
use crate::hook;
|
| 58 |
use crate::log_line;
|
|
@@ -90,16 +93,16 @@ static mut DSSOUND_INIT_EAX: u32 = 0;
|
|
| 90 |
// so the callee sees its actual stack args at [esp+4] / [esp+8] etc. (not
|
| 91 |
// displaced by an extra return address).
|
| 92 |
|
| 93 |
-
/// Timer constructor: `usercall(ESI=timer_ptr, EAX=
|
| 94 |
/// Returns whatever EAX holds after the call.
|
| 95 |
#[unsafe(naked)]
|
| 96 |
-
unsafe extern "cdecl" fn call_timer_ctor(_timer_ptr: *mut GameTimer,
|
| 97 |
core::arch::naked_asm!(
|
| 98 |
-
// [esp+0]=bridge_ret, [esp+4]=timer_ptr, [esp+8]=
|
| 99 |
"pushl %esi",
|
| 100 |
// [esp+0]=old_esi, [esp+4]=bridge_ret, [esp+8]=timer_ptr, [esp+c]=d778_val
|
| 101 |
"movl 8(%esp), %esi", // ESI = timer_ptr
|
| 102 |
-
"movl 0xc(%esp), %eax", // EAX =
|
| 103 |
"calll *({fn})", // FUN_0053E950: plain RET (no stack args)
|
| 104 |
"popl %esi",
|
| 105 |
"retl", // cdecl; caller cleans 2 Γ u32
|
|
@@ -166,9 +169,9 @@ unsafe extern "stdcall" fn call_dssound_init_buffers(
|
|
| 166 |
#[unsafe(naked)]
|
| 167 |
unsafe extern "stdcall" fn call_input_ctrl_init(
|
| 168 |
_game_info_p4: *mut u8,
|
| 169 |
-
_hwnd: u32,
|
| 170 |
_param3: u32,
|
| 171 |
-
|
|
|
|
| 172 |
) -> u32 {
|
| 173 |
core::arch::naked_asm!(
|
| 174 |
// [esp+0]=bridge_ret, [esp+4]=gip4, [esp+8]=hwnd, [esp+c]=p3, [esp+10]=d778
|
|
@@ -255,7 +258,7 @@ unsafe extern "cdecl" fn impl_init_hardware(
|
|
| 255 |
let _ = log_line("[hardware_init] GameEngine::InitHardware");
|
| 256 |
let session = *(rb(va::G_GAME_SESSION) as *const *mut GameSession);
|
| 257 |
let gi = &mut *game_info;
|
| 258 |
-
let
|
| 259 |
|
| 260 |
// ββ Input controller (if param4 != 0) ββββββββββββββββββββββββββββββββββββ
|
| 261 |
if param4 == 0 {
|
|
@@ -266,13 +269,10 @@ unsafe extern "cdecl" fn impl_init_hardware(
|
|
| 266 |
(*ctrl).vtable = rb(va::INPUT_CTRL_VTABLE) as *mut u8;
|
| 267 |
(*session).input_ctrl = ctrl as *mut u8;
|
| 268 |
|
|
|
|
|
|
|
| 269 |
INPUT_CTRL_ESI = ctrl as u32;
|
| 270 |
-
let ok = call_input_ctrl_init(
|
| 271 |
-
(game_info as *mut u8).add(4),
|
| 272 |
-
param3,
|
| 273 |
-
param4,
|
| 274 |
-
d778_val,
|
| 275 |
-
);
|
| 276 |
if ok == 0 {
|
| 277 |
let vtbl = *(ctrl as *const *const usize);
|
| 278 |
let dtor: unsafe extern "thiscall" fn(*mut u8, u32) =
|
|
@@ -285,17 +285,17 @@ unsafe extern "cdecl" fn impl_init_hardware(
|
|
| 285 |
|
| 286 |
// ββ Timer object (ALWAYS) βββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 287 |
let timer = WABox::<GameTimer>::alloc(0x30, 0x30).leak();
|
| 288 |
-
call_timer_ctor(timer,
|
| 289 |
(*session).timer_obj = timer as *mut u8;
|
| 290 |
|
| 291 |
let headless = gi.headless_mode != 0;
|
| 292 |
|
| 293 |
if !headless {
|
| 294 |
// ββ DisplayGfx βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 295 |
-
let displaygfx_ctor: unsafe extern "stdcall" fn(*mut
|
| 296 |
core::mem::transmute(rb(va::DISPLAYGFX_CTOR) as usize);
|
| 297 |
-
let display_gfx = displaygfx_ctor(WABox::<
|
| 298 |
-
(*session).display_gfx = display_gfx;
|
| 299 |
|
| 300 |
// ββ DDDisplay::Init retry loop ββββββββββββββββββββββββββββββββββββββββ
|
| 301 |
let flags = gi.display_flags;
|
|
@@ -303,7 +303,7 @@ unsafe extern "cdecl" fn impl_init_hardware(
|
|
| 303 |
let h0 = gi.display_height;
|
| 304 |
|
| 305 |
DDISPLAY_INIT_ECX = h0;
|
| 306 |
-
let mut init_ok = call_ddisplay_init(display_gfx, hwnd, w0, flags) != 0;
|
| 307 |
|
| 308 |
if !init_ok {
|
| 309 |
let fallbacks: [(u32, u32); 3] = [
|
|
@@ -315,7 +315,7 @@ unsafe extern "cdecl" fn impl_init_hardware(
|
|
| 315 |
gi.display_width = w;
|
| 316 |
gi.display_height = h;
|
| 317 |
DDISPLAY_INIT_ECX = h;
|
| 318 |
-
if call_ddisplay_init(display_gfx, hwnd, w, flags) != 0 {
|
| 319 |
init_ok = true;
|
| 320 |
break;
|
| 321 |
}
|
|
@@ -363,12 +363,12 @@ unsafe extern "cdecl" fn impl_init_hardware(
|
|
| 363 |
}
|
| 364 |
|
| 365 |
// ββ DDKeyboard (inline construction) ββββββββββββββββββββββββββββββββββ
|
|
|
|
|
|
|
| 366 |
let kb = WABox::<DDKeyboard>::alloc(0x33C, 0x31C).leak();
|
| 367 |
(*kb).vtable = rb(va::DDKEYBOARD_VTABLE) as *mut u8;
|
| 368 |
(*kb).game_info_input_ptr = &raw mut gi.input_state_f918 as u32;
|
| 369 |
(*kb)._field_008 = 1;
|
| 370 |
-
core::ptr::write_bytes((*kb).key_state.as_mut_ptr(), 0, 0x100);
|
| 371 |
-
core::ptr::write_bytes((*kb).prev_state.as_mut_ptr(), 0, 0x100);
|
| 372 |
(*kb)._field_014 = 0;
|
| 373 |
(*kb)._field_018 = 0;
|
| 374 |
(*session).keyboard = kb;
|
|
@@ -376,7 +376,7 @@ unsafe extern "cdecl" fn impl_init_hardware(
|
|
| 376 |
// ββ Palette (inline construction) βββββββββββββββββββββββββββββββββββββ
|
| 377 |
let pal = WABox::<Palette>::alloc(0x28, 0).leak();
|
| 378 |
(*pal).vtable = rb(va::PALETTE_VTABLE_MAYBE) as *mut u8;
|
| 379 |
-
|
| 380 |
(*session).palette = pal;
|
| 381 |
|
| 382 |
// ββ DSSound βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -403,11 +403,12 @@ unsafe extern "cdecl" fn impl_init_hardware(
|
|
| 403 |
);
|
| 404 |
if hr2 == 0 {
|
| 405 |
// IDirectSoundBuffer::Play(this, 0, 0, DSBPLAY_LOOPING=1)
|
|
|
|
| 406 |
let pds = (*snd).primary_buffer as *const *const usize;
|
| 407 |
let vtbl = *pds;
|
| 408 |
let play: unsafe extern "stdcall" fn(
|
| 409 |
*const *const usize, u32, u32, u32,
|
| 410 |
-
) -> i32 = core::mem::transmute(*vtbl.add(
|
| 411 |
let hr3 = play(pds, 0, 0, 1);
|
| 412 |
if hr3 == 0 {
|
| 413 |
(*snd).init_success = 1;
|
|
@@ -418,25 +419,22 @@ unsafe extern "cdecl" fn impl_init_hardware(
|
|
| 418 |
|
| 419 |
// ββ Streaming audio βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 420 |
(*session).streaming_audio = core::ptr::null_mut();
|
| 421 |
-
if !(*session).sound.is_null() {
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
);
|
| 431 |
-
(*session).streaming_audio = stream_mem;
|
| 432 |
-
}
|
| 433 |
}
|
| 434 |
} else {
|
| 435 |
// ββ Headless / stats mode βββββββββββββββββββββββββββββββββββββββββββββ
|
| 436 |
let stats = WABox::<GameStats>::alloc(0x3560, 0x3560).leak();
|
| 437 |
-
let gamestats_ctor: unsafe extern "stdcall" fn(*mut
|
| 438 |
core::mem::transmute(rb(va::GAMESTATS_CTOR) as usize);
|
| 439 |
-
gamestats_ctor(stats
|
| 440 |
(*stats).vtable = rb(va::GAMESTATS_VTABLE) as *mut u8;
|
| 441 |
(*session).display_gfx = stats as *mut u8;
|
| 442 |
(*session).keyboard = core::ptr::null_mut();
|
|
@@ -445,10 +443,9 @@ unsafe extern "cdecl" fn impl_init_hardware(
|
|
| 445 |
(*session).streaming_audio = core::ptr::null_mut();
|
| 446 |
}
|
| 447 |
|
| 448 |
-
// ββ
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
*((session as *mut u8).add(0x24) as *mut u32) = 1;
|
| 452 |
|
| 453 |
// ββ DDGameWrapper (ALWAYS) ββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 454 |
let wrapper = game_session::construct_ddgame_wrapper(
|
|
@@ -488,9 +485,9 @@ unsafe extern "cdecl" fn impl_init_hardware(
|
|
| 488 |
}
|
| 489 |
|
| 490 |
// ββ DDNetGameWrapper (ALWAYS) βββββββββββββββββββββββββββββββββββββββββββββ
|
| 491 |
-
let net_ctor: unsafe extern "stdcall" fn(*mut
|
| 492 |
core::mem::transmute(rb(va::DDNETGAME_WRAPPER_CTOR) as usize);
|
| 493 |
-
(*session).net_game = net_ctor(WABox::<
|
| 494 |
|
| 495 |
let _ = log_line("[hardware_init] GameEngine::InitHardware done");
|
| 496 |
1
|
|
|
|
| 53 |
use openwa_core::input_ctrl::InputCtrl;
|
| 54 |
use openwa_core::game_timer::GameTimer;
|
| 55 |
use openwa_core::game_stats::GameStats;
|
| 56 |
+
use openwa_core::display_gfx::DisplayGfx;
|
| 57 |
+
use openwa_core::streaming_audio::StreamingAudio;
|
| 58 |
+
use openwa_core::ddnetgame_wrapper::DDNetGameWrapper;
|
| 59 |
use openwa_core::wa_alloc::WABox;
|
| 60 |
use crate::hook;
|
| 61 |
use crate::log_line;
|
|
|
|
| 93 |
// so the callee sees its actual stack args at [esp+4] / [esp+8] etc. (not
|
| 94 |
// displaced by an extra return address).
|
| 95 |
|
| 96 |
+
/// Timer constructor: `usercall(ESI=timer_ptr, EAX=crosshair_threshold)`, plain RET.
|
| 97 |
/// Returns whatever EAX holds after the call.
|
| 98 |
#[unsafe(naked)]
|
| 99 |
+
unsafe extern "cdecl" fn call_timer_ctor(_timer_ptr: *mut GameTimer, _crosshair_threshold: u32) -> u32 {
|
| 100 |
core::arch::naked_asm!(
|
| 101 |
+
// [esp+0]=bridge_ret, [esp+4]=timer_ptr, [esp+8]=crosshair_threshold
|
| 102 |
"pushl %esi",
|
| 103 |
// [esp+0]=old_esi, [esp+4]=bridge_ret, [esp+8]=timer_ptr, [esp+c]=d778_val
|
| 104 |
"movl 8(%esp), %esi", // ESI = timer_ptr
|
| 105 |
+
"movl 0xc(%esp), %eax", // EAX = crosshair_threshold
|
| 106 |
"calll *({fn})", // FUN_0053E950: plain RET (no stack args)
|
| 107 |
"popl %esi",
|
| 108 |
"retl", // cdecl; caller cleans 2 Γ u32
|
|
|
|
| 169 |
#[unsafe(naked)]
|
| 170 |
unsafe extern "stdcall" fn call_input_ctrl_init(
|
| 171 |
_game_info_p4: *mut u8,
|
|
|
|
| 172 |
_param3: u32,
|
| 173 |
+
_param4: u32,
|
| 174 |
+
_crosshair_threshold: u32,
|
| 175 |
) -> u32 {
|
| 176 |
core::arch::naked_asm!(
|
| 177 |
// [esp+0]=bridge_ret, [esp+4]=gip4, [esp+8]=hwnd, [esp+c]=p3, [esp+10]=d778
|
|
|
|
| 258 |
let _ = log_line("[hardware_init] GameEngine::InitHardware");
|
| 259 |
let session = *(rb(va::G_GAME_SESSION) as *const *mut GameSession);
|
| 260 |
let gi = &mut *game_info;
|
| 261 |
+
let crosshair_threshold = gi.crosshair_overflow_threshold as u32;
|
| 262 |
|
| 263 |
// ββ Input controller (if param4 != 0) ββββββββββββββββββββββββββββββββββββ
|
| 264 |
if param4 == 0 {
|
|
|
|
| 269 |
(*ctrl).vtable = rb(va::INPUT_CTRL_VTABLE) as *mut u8;
|
| 270 |
(*session).input_ctrl = ctrl as *mut u8;
|
| 271 |
|
| 272 |
+
// Original passes GameInfo+4 (skips first DWORD of unknown padding).
|
| 273 |
+
let game_info_plus_4 = (game_info as *mut u8).add(4);
|
| 274 |
INPUT_CTRL_ESI = ctrl as u32;
|
| 275 |
+
let ok = call_input_ctrl_init(game_info_plus_4, param3, param4, crosshair_threshold);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 276 |
if ok == 0 {
|
| 277 |
let vtbl = *(ctrl as *const *const usize);
|
| 278 |
let dtor: unsafe extern "thiscall" fn(*mut u8, u32) =
|
|
|
|
| 285 |
|
| 286 |
// ββ Timer object (ALWAYS) βββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 287 |
let timer = WABox::<GameTimer>::alloc(0x30, 0x30).leak();
|
| 288 |
+
call_timer_ctor(timer, crosshair_threshold);
|
| 289 |
(*session).timer_obj = timer as *mut u8;
|
| 290 |
|
| 291 |
let headless = gi.headless_mode != 0;
|
| 292 |
|
| 293 |
if !headless {
|
| 294 |
// ββ DisplayGfx βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 295 |
+
let displaygfx_ctor: unsafe extern "stdcall" fn(*mut DisplayGfx) -> *mut DisplayGfx =
|
| 296 |
core::mem::transmute(rb(va::DISPLAYGFX_CTOR) as usize);
|
| 297 |
+
let display_gfx = displaygfx_ctor(WABox::<DisplayGfx>::alloc(0x24E28, 0x24E08).leak());
|
| 298 |
+
(*session).display_gfx = display_gfx as *mut u8;
|
| 299 |
|
| 300 |
// ββ DDDisplay::Init retry loop ββββββββββββββββββββββββββββββββββββββββ
|
| 301 |
let flags = gi.display_flags;
|
|
|
|
| 303 |
let h0 = gi.display_height;
|
| 304 |
|
| 305 |
DDISPLAY_INIT_ECX = h0;
|
| 306 |
+
let mut init_ok = call_ddisplay_init(display_gfx as *mut u8, hwnd, w0, flags) != 0;
|
| 307 |
|
| 308 |
if !init_ok {
|
| 309 |
let fallbacks: [(u32, u32); 3] = [
|
|
|
|
| 315 |
gi.display_width = w;
|
| 316 |
gi.display_height = h;
|
| 317 |
DDISPLAY_INIT_ECX = h;
|
| 318 |
+
if call_ddisplay_init(display_gfx as *mut u8, hwnd, w, flags) != 0 {
|
| 319 |
init_ok = true;
|
| 320 |
break;
|
| 321 |
}
|
|
|
|
| 363 |
}
|
| 364 |
|
| 365 |
// ββ DDKeyboard (inline construction) ββββββββββββββββββββββββββββββββββ
|
| 366 |
+
// WABox zeroes the first 0x31C bytes, covering key_state (+0x11C) and
|
| 367 |
+
// prev_state (+0x21C) β no separate zeroing needed.
|
| 368 |
let kb = WABox::<DDKeyboard>::alloc(0x33C, 0x31C).leak();
|
| 369 |
(*kb).vtable = rb(va::DDKEYBOARD_VTABLE) as *mut u8;
|
| 370 |
(*kb).game_info_input_ptr = &raw mut gi.input_state_f918 as u32;
|
| 371 |
(*kb)._field_008 = 1;
|
|
|
|
|
|
|
| 372 |
(*kb)._field_014 = 0;
|
| 373 |
(*kb)._field_018 = 0;
|
| 374 |
(*session).keyboard = kb;
|
|
|
|
| 376 |
// ββ Palette (inline construction) βββββββββββββββββββββββββββββββββββββ
|
| 377 |
let pal = WABox::<Palette>::alloc(0x28, 0).leak();
|
| 378 |
(*pal).vtable = rb(va::PALETTE_VTABLE_MAYBE) as *mut u8;
|
| 379 |
+
(*pal)._field_004 = 0xFFFF_FFFF;
|
| 380 |
(*session).palette = pal;
|
| 381 |
|
| 382 |
// ββ DSSound βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 403 |
);
|
| 404 |
if hr2 == 0 {
|
| 405 |
// IDirectSoundBuffer::Play(this, 0, 0, DSBPLAY_LOOPING=1)
|
| 406 |
+
const PLAY_VSLOT: usize = 0x30 / 4; // IDirectSoundBuffer vtable slot 12
|
| 407 |
let pds = (*snd).primary_buffer as *const *const usize;
|
| 408 |
let vtbl = *pds;
|
| 409 |
let play: unsafe extern "stdcall" fn(
|
| 410 |
*const *const usize, u32, u32, u32,
|
| 411 |
+
) -> i32 = core::mem::transmute(*vtbl.add(PLAY_VSLOT));
|
| 412 |
let hr3 = play(pds, 0, 0, 1);
|
| 413 |
if hr3 == 0 {
|
| 414 |
(*snd).init_success = 1;
|
|
|
|
| 419 |
|
| 420 |
// ββ Streaming audio βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 421 |
(*session).streaming_audio = core::ptr::null_mut();
|
| 422 |
+
if !(*session).sound.is_null() && gi.speech_enabled != 0 {
|
| 423 |
+
let stream = WABox::<StreamingAudio>::alloc(0x354, 0x334).leak();
|
| 424 |
+
let ids = (*(*session).sound).direct_sound;
|
| 425 |
+
call_streaming_audio_ctor(
|
| 426 |
+
stream as *mut u8,
|
| 427 |
+
ids,
|
| 428 |
+
gi.streaming_audio_config.as_mut_ptr(),
|
| 429 |
+
);
|
| 430 |
+
(*session).streaming_audio = stream as *mut u8;
|
|
|
|
|
|
|
|
|
|
| 431 |
}
|
| 432 |
} else {
|
| 433 |
// ββ Headless / stats mode βββββββββββββββββββββββββββββββββββββββββββββ
|
| 434 |
let stats = WABox::<GameStats>::alloc(0x3560, 0x3560).leak();
|
| 435 |
+
let gamestats_ctor: unsafe extern "stdcall" fn(*mut GameStats) -> *mut GameStats =
|
| 436 |
core::mem::transmute(rb(va::GAMESTATS_CTOR) as usize);
|
| 437 |
+
gamestats_ctor(stats);
|
| 438 |
(*stats).vtable = rb(va::GAMESTATS_VTABLE) as *mut u8;
|
| 439 |
(*session).display_gfx = stats as *mut u8;
|
| 440 |
(*session).keyboard = core::ptr::null_mut();
|
|
|
|
| 443 |
(*session).streaming_audio = core::ptr::null_mut();
|
| 444 |
}
|
| 445 |
|
| 446 |
+
// ββ Session flags βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 447 |
+
(*session).init_flag = 1;
|
| 448 |
+
(*session).fullscreen_flag = (gi.home_lock != 0) as u32;
|
|
|
|
| 449 |
|
| 450 |
// ββ DDGameWrapper (ALWAYS) ββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 451 |
let wrapper = game_session::construct_ddgame_wrapper(
|
|
|
|
| 485 |
}
|
| 486 |
|
| 487 |
// ββ DDNetGameWrapper (ALWAYS) βββββββββββββββββββββββββββββββββββββββββββββ
|
| 488 |
+
let net_ctor: unsafe extern "stdcall" fn(*mut DDNetGameWrapper) -> *mut DDNetGameWrapper =
|
| 489 |
core::mem::transmute(rb(va::DDNETGAME_WRAPPER_CTOR) as usize);
|
| 490 |
+
(*session).net_game = net_ctor(WABox::<DDNetGameWrapper>::alloc(0x2C, 0).leak()) as *mut u8;
|
| 491 |
|
| 492 |
let _ = log_line("[hardware_init] GameEngine::InitHardware done");
|
| 493 |
1
|