feat: pure Rust DSSound::new() and typed COM init via windows crate
Browse filesReplace DSSound constructor FFI bridge (call_dssound_ctor) with pure
Rust DSSound::new() that initializes all fields: 8 channel descriptors,
500 channel slots, 64-entry buffer pool, volume, and status flags.
Replace DSSOUND_INIT_BUFFERS FFI bridge (call_dssound_init_buffers) with
typed windows crate COM calls: IDirectSound::SetCooperativeLevel,
CreateSoundBuffer(DSBCAPS_PRIMARYBUFFER), IDirectSoundBuffer::GetCaps.
Expand DSSound struct from opaque blob to named fields matching the
constructor analysis (channel_descs, channel_slots, buffer_pool, etc.).
Remove dead naked asm bridges and statics (DSSOUND_CTOR_ADDR,
DSSOUND_INIT_BUF_ADDR, DSSOUND_INIT_EAX, DSSOUND_INIT_SAVED_RET).
Trap DSSound__Constructor and DSSOUND_INIT_BUFFERS -- both had
GameEngine__InitHardware as their only caller (now Rust).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@@ -1,4 +1,5 @@
|
|
| 1 |
use crate::fixed::Fixed;
|
|
|
|
| 2 |
/// DSSound β DirectSound audio subsystem.
|
| 3 |
///
|
| 4 |
/// Created by DSSound__Constructor (0x573D50).
|
|
@@ -6,36 +7,114 @@ use crate::fixed::Fixed;
|
|
| 6 |
/// Size: 0xBE0 bytes (allocated by GameEngine__InitHardware).
|
| 7 |
///
|
| 8 |
/// Manages sound playback through DirectSound.
|
| 9 |
-
/// Has 500 sound channel slots
|
| 10 |
-
///
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
#[repr(C)]
|
| 13 |
pub struct DSSound {
|
| 14 |
/// 0x000: Vtable pointer (0x66AF20)
|
| 15 |
-
pub vtable:
|
| 16 |
-
/// 0x004: HWND (set
|
| 17 |
-
pub hwnd:
|
| 18 |
/// 0x008: IDirectSound* (from DirectSoundCreate)
|
| 19 |
-
pub direct_sound:
|
| 20 |
-
/// 0x00C:
|
| 21 |
pub primary_buffer_caps: u32,
|
| 22 |
-
/// 0x010: IDirectSoundBuffer* (primary buffer
|
| 23 |
-
pub primary_buffer:
|
| 24 |
-
/// 0x014-
|
| 25 |
-
pub
|
|
|
|
|
|
|
| 26 |
/// 0x8A4: Master volume (Fixed, init 0x10000 = 1.0)
|
| 27 |
pub volume: Fixed,
|
| 28 |
-
/// 0x8A8-
|
| 29 |
-
pub
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
/// 0xBB4: Status flag 1 (init 1)
|
| 31 |
pub status_1: u32,
|
| 32 |
/// 0xBB8: Status flag 2 (init 1)
|
| 33 |
pub status_2: u32,
|
| 34 |
/// 0xBBC: Init success flag β set to 1 when DirectSoundCreate +
|
| 35 |
-
///
|
| 36 |
pub init_success: u32,
|
| 37 |
/// 0xBC0-0xBDF: Unknown trailing fields
|
| 38 |
pub _unknown_bc0: [u8; 0x20],
|
| 39 |
}
|
| 40 |
|
| 41 |
const _: () = assert!(core::mem::size_of::<DSSound>() == 0xBE0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
use crate::fixed::Fixed;
|
| 2 |
+
|
| 3 |
/// DSSound β DirectSound audio subsystem.
|
| 4 |
///
|
| 5 |
/// Created by DSSound__Constructor (0x573D50).
|
|
|
|
| 7 |
/// Size: 0xBE0 bytes (allocated by GameEngine__InitHardware).
|
| 8 |
///
|
| 9 |
/// Manages sound playback through DirectSound.
|
| 10 |
+
/// Has 8 channel descriptors, 500 sound channel slots, a 64-entry
|
| 11 |
+
/// buffer pool, and a master volume control.
|
| 12 |
+
|
| 13 |
+
type Ptr32 = u32;
|
| 14 |
+
|
| 15 |
+
/// Channel descriptor (0x18 bytes). 8 entries at DSSound+0x14.
|
| 16 |
+
/// FUN_005742A0 initializes: field_00=-1, field_04=-1, field_10=0.
|
| 17 |
+
#[repr(C)]
|
| 18 |
+
#[derive(Clone, Copy)]
|
| 19 |
+
pub struct ChannelDescriptor {
|
| 20 |
+
pub field_00: u32, // init -1
|
| 21 |
+
pub field_04: u32, // init -1
|
| 22 |
+
pub _pad_08: [u32; 2],
|
| 23 |
+
pub field_10: u32, // init 0
|
| 24 |
+
pub _pad_14: u32,
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
const _: () = assert!(core::mem::size_of::<ChannelDescriptor>() == 0x18);
|
| 28 |
+
|
| 29 |
#[repr(C)]
|
| 30 |
pub struct DSSound {
|
| 31 |
/// 0x000: Vtable pointer (0x66AF20)
|
| 32 |
+
pub vtable: Ptr32,
|
| 33 |
+
/// 0x004: HWND (set after construction)
|
| 34 |
+
pub hwnd: Ptr32,
|
| 35 |
/// 0x008: IDirectSound* (from DirectSoundCreate)
|
| 36 |
+
pub direct_sound: Ptr32,
|
| 37 |
+
/// 0x00C: Primary buffer caps/format output from init_buffers
|
| 38 |
pub primary_buffer_caps: u32,
|
| 39 |
+
/// 0x010: IDirectSoundBuffer* (primary buffer)
|
| 40 |
+
pub primary_buffer: Ptr32,
|
| 41 |
+
/// 0x014-0xD3: 8 channel descriptors (0x18 bytes each = 0xC0 total)
|
| 42 |
+
pub channel_descs: [ChannelDescriptor; 8],
|
| 43 |
+
/// 0x0D4-0x8A3: 500 channel slot indices (zeroed by constructor)
|
| 44 |
+
pub channel_slots: [u32; 500],
|
| 45 |
/// 0x8A4: Master volume (Fixed, init 0x10000 = 1.0)
|
| 46 |
pub volume: Fixed,
|
| 47 |
+
/// 0x8A8-0x9A7: 64 buffer pool shadow entries (init all -1 by FUN_00574260)
|
| 48 |
+
pub buffer_pool_shadow: [u32; 64],
|
| 49 |
+
/// 0x9A8-0xAA7: 64 buffer pool indices (init 0..63 by FUN_00574260)
|
| 50 |
+
pub buffer_pool: [u32; 64],
|
| 51 |
+
/// 0xAA8: Buffer pool count (init 0x40 = 64)
|
| 52 |
+
pub buffer_pool_count: u32,
|
| 53 |
+
/// 0xAAC-0xBAB: Unknown (0x100 bytes)
|
| 54 |
+
pub _unknown_aac: [u8; 0x100],
|
| 55 |
+
/// 0xBAC: Buffer pool state (init 0 by FUN_00574260)
|
| 56 |
+
pub buffer_pool_state: u32,
|
| 57 |
+
/// 0xBB0: Unknown field (zeroed by constructor)
|
| 58 |
+
pub _field_bb0: u32,
|
| 59 |
/// 0xBB4: Status flag 1 (init 1)
|
| 60 |
pub status_1: u32,
|
| 61 |
/// 0xBB8: Status flag 2 (init 1)
|
| 62 |
pub status_2: u32,
|
| 63 |
/// 0xBBC: Init success flag β set to 1 when DirectSoundCreate +
|
| 64 |
+
/// init_buffers + IDirectSoundBuffer::Play all succeed.
|
| 65 |
pub init_success: u32,
|
| 66 |
/// 0xBC0-0xBDF: Unknown trailing fields
|
| 67 |
pub _unknown_bc0: [u8; 0x20],
|
| 68 |
}
|
| 69 |
|
| 70 |
const _: () = assert!(core::mem::size_of::<DSSound>() == 0xBE0);
|
| 71 |
+
|
| 72 |
+
impl DSSound {
|
| 73 |
+
/// Construct a DSSound with all fields initialized, matching the
|
| 74 |
+
/// original constructor (0x573D50) + helpers FUN_005742A0/FUN_00574260.
|
| 75 |
+
///
|
| 76 |
+
/// Uses WA's vtable pointer for identity (same pattern as DisplayBase).
|
| 77 |
+
///
|
| 78 |
+
/// # Safety
|
| 79 |
+
/// Must be called from within the WA.exe process (needs rebased vtable).
|
| 80 |
+
pub unsafe fn new(hwnd: u32) -> Self {
|
| 81 |
+
use crate::address::va;
|
| 82 |
+
use crate::rebase::rb;
|
| 83 |
+
|
| 84 |
+
let mut snd: Self = core::mem::zeroed();
|
| 85 |
+
|
| 86 |
+
// Vtable (WA's .rdata, identity-checked)
|
| 87 |
+
snd.vtable = rb(va::DS_SOUND_VTABLE);
|
| 88 |
+
|
| 89 |
+
// HWND
|
| 90 |
+
snd.hwnd = hwnd;
|
| 91 |
+
|
| 92 |
+
// Volume = 1.0 (16.16 fixed-point)
|
| 93 |
+
snd.volume = Fixed(0x10000);
|
| 94 |
+
|
| 95 |
+
// 8 channel descriptors: field_00=-1, field_04=-1, field_10=0
|
| 96 |
+
// (rest is zero from mem::zeroed)
|
| 97 |
+
for desc in &mut snd.channel_descs {
|
| 98 |
+
desc.field_00 = 0xFFFF_FFFF;
|
| 99 |
+
desc.field_04 = 0xFFFF_FFFF;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
// 500 channel slots: already zeroed
|
| 103 |
+
|
| 104 |
+
// Buffer pool: shadow entries all -1, indices 0..63
|
| 105 |
+
for (i, slot) in snd.buffer_pool.iter_mut().enumerate() {
|
| 106 |
+
*slot = i as u32;
|
| 107 |
+
}
|
| 108 |
+
for entry in &mut snd.buffer_pool_shadow {
|
| 109 |
+
*entry = 0xFFFF_FFFF;
|
| 110 |
+
}
|
| 111 |
+
snd.buffer_pool_count = 64;
|
| 112 |
+
|
| 113 |
+
// Status flags
|
| 114 |
+
snd.status_1 = 1;
|
| 115 |
+
snd.status_2 = 1;
|
| 116 |
+
// init_success stays 0 until COM init succeeds
|
| 117 |
+
|
| 118 |
+
snd
|
| 119 |
+
}
|
| 120 |
+
}
|
|
@@ -61,8 +61,6 @@ static mut SAVED_RET: u32 = 0;
|
|
| 61 |
|
| 62 |
/// Function addresses, set in `install()`.
|
| 63 |
static mut TIMER_CTOR_ADDR: u32 = 0;
|
| 64 |
-
static mut DSSOUND_CTOR_ADDR: u32 = 0;
|
| 65 |
-
static mut DSSOUND_INIT_BUF_ADDR: u32 = 0;
|
| 66 |
static mut INPUT_CTRL_INIT_ADDR: u32 = 0;
|
| 67 |
static mut STREAM_CTOR_ADDR: u32 = 0;
|
| 68 |
static mut DDISPLAY_INIT_ADDR: u32 = 0;
|
|
@@ -75,8 +73,6 @@ static mut INPUT_CTRL_ESI: u32 = 0;
|
|
| 75 |
/// Saved ESI across the `call_input_ctrl_init` call.
|
| 76 |
static mut INPUT_CTRL_SAVED_ESI: u32 = 0;
|
| 77 |
|
| 78 |
-
/// Implicit EAX for `call_dssound_init_buffers` (set by `impl_init_hardware`).
|
| 79 |
-
static mut DSSOUND_INIT_EAX: u32 = 0;
|
| 80 |
|
| 81 |
// βββ Bridges βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 82 |
//
|
|
@@ -102,58 +98,6 @@ unsafe extern "cdecl" fn call_timer_ctor(_timer_ptr: *mut GameTimer, _crosshair_
|
|
| 102 |
);
|
| 103 |
}
|
| 104 |
|
| 105 |
-
/// DSSound constructor: `usercall(EAX=this)`, plain RET. Void.
|
| 106 |
-
///
|
| 107 |
-
/// IMPORTANT: FUN_00573D50 clobbers ECX (MOV ECX, 0x1F4 for REP STOSD).
|
| 108 |
-
/// Do NOT use ECX to save bridge_ret β read it from the stack instead.
|
| 109 |
-
#[unsafe(naked)]
|
| 110 |
-
unsafe extern "cdecl" fn call_dssound_ctor(_dssound: *mut DSSound) {
|
| 111 |
-
core::arch::naked_asm!(
|
| 112 |
-
// [esp+0]=bridge_ret, [esp+4]=dssound
|
| 113 |
-
"movl 4(%esp), %eax", // EAX = dssound (bridge_ret stays on stack untouched)
|
| 114 |
-
"calll *({fn})", // FUN_00573D50: plain RET; calll pushes continuation here
|
| 115 |
-
// After FUN_00573D50 RET: stack = [bridge_ret, dssound]
|
| 116 |
-
"retl", // pop bridge_ret, return; caller cleans dssound
|
| 117 |
-
fn = sym DSSOUND_CTOR_ADDR,
|
| 118 |
-
options(att_syntax),
|
| 119 |
-
);
|
| 120 |
-
}
|
| 121 |
-
|
| 122 |
-
/// Saved bridge_ret for `call_dssound_init_buffers` (can't use ECX β callee clobbers it).
|
| 123 |
-
static mut DSSOUND_INIT_SAVED_RET: u32 = 0;
|
| 124 |
-
|
| 125 |
-
/// FUN_00573E50: `usercall(EAX=dssound)` + `__stdcall(out_0x10, out_0x0C)`, `RET 0x8`.
|
| 126 |
-
/// Declared as `extern "stdcall"` so Rust caller does NOT emit `add esp, 8` after the call
|
| 127 |
-
/// (callee already cleans via RET 0x8).
|
| 128 |
-
/// Caller sets `DSSOUND_INIT_EAX = dssound` before calling.
|
| 129 |
-
#[unsafe(naked)]
|
| 130 |
-
unsafe extern "stdcall" fn call_dssound_init_buffers(
|
| 131 |
-
_out_primary_buffer: *mut *mut u8,
|
| 132 |
-
_out_primary_caps: *mut u32,
|
| 133 |
-
) -> u32 {
|
| 134 |
-
core::arch::naked_asm!(
|
| 135 |
-
// Entry: ESP=E, [E+0]=bridge_ret, [E+4]=out_primary_buffer, [E+8]=out_primary_caps
|
| 136 |
-
"movl {eax_val}, %eax", // EAX = dssound
|
| 137 |
-
"movl (%esp), %ecx", // ECX = bridge_ret (temp)
|
| 138 |
-
"movl %ecx, {saved_ret}", // save to static (ECX clobbered by FUN_00573E50)
|
| 139 |
-
"movl 4(%esp), %ecx", // ECX = out_primary_buffer
|
| 140 |
-
"movl 8(%esp), %edx", // EDX = out_primary_caps
|
| 141 |
-
"addl $0xC, %esp", // ESP = E+12 (discard bridge_ret + 2 args)
|
| 142 |
-
"pushl %edx", // ESP = E+8, [E+8] = out_primary_caps
|
| 143 |
-
"pushl %ecx", // ESP = E+4, [E+4] = out_primary_buffer
|
| 144 |
-
"calll *({fn})", // ESP = E, [E] = cont
|
| 145 |
-
// FUN_00573E50 RET 0x8: pops cont β E+4, +8 β E+12
|
| 146 |
-
// At cont: ESP = E+12; EAX = return value
|
| 147 |
-
"pushl {saved_ret}", // ESP = E+8, [E+8] = bridge_ret
|
| 148 |
-
"retl", // pops bridge_ret β ESP = E+12
|
| 149 |
-
// stdcall: caller skips cleanup. ESP = E+12 = E+4+8 β
|
| 150 |
-
eax_val = sym DSSOUND_INIT_EAX,
|
| 151 |
-
fn = sym DSSOUND_INIT_BUF_ADDR,
|
| 152 |
-
saved_ret = sym DSSOUND_INIT_SAVED_RET,
|
| 153 |
-
options(att_syntax),
|
| 154 |
-
);
|
| 155 |
-
}
|
| 156 |
-
|
| 157 |
/// FUN_0058C0D0: `usercall(ESI=input_ctrl)` + stdcall(4 params), RET 0x10.
|
| 158 |
/// Caller sets `INPUT_CTRL_ESI = input_ctrl` before calling.
|
| 159 |
/// FUN_0058C0D0's `RET 0x10` cleans all 4 args, so `retl` here needs no cleanup.
|
|
@@ -248,34 +192,50 @@ unsafe extern "stdcall" fn call_ddisplay_init(
|
|
| 248 |
/// if COM steps fail, matching original WA behavior).
|
| 249 |
unsafe fn create_dssound(hwnd: u32) -> *mut DSSound {
|
| 250 |
use windows::Win32::Media::Audio::DirectSound::{
|
| 251 |
-
DirectSoundCreate, IDirectSound, IDirectSoundBuffer,
|
|
|
|
| 252 |
};
|
|
|
|
| 253 |
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
(*snd).hwnd = hwnd;
|
| 257 |
|
| 258 |
-
//
|
| 259 |
let mut ds: Option<IDirectSound> = None;
|
| 260 |
if DirectSoundCreate(None, &mut ds, None).is_ok() {
|
| 261 |
let ds = ds.unwrap();
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
let
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 275 |
if primary.Play(0, 0, DSBPLAY_LOOPING).is_ok() {
|
| 276 |
(*snd).init_success = 1;
|
| 277 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
}
|
|
|
|
|
|
|
|
|
|
| 279 |
}
|
| 280 |
|
| 281 |
snd
|
|
@@ -409,7 +369,7 @@ unsafe extern "cdecl" fn impl_init_hardware(
|
|
| 409 |
(*session).streaming_audio = core::ptr::null_mut();
|
| 410 |
if !(*session).sound.is_null() && gi.speech_enabled != 0 {
|
| 411 |
let stream = WABox::<StreamingAudio>::alloc(0x354, 0x334).leak();
|
| 412 |
-
let ids = (*(*session).sound).direct_sound;
|
| 413 |
call_streaming_audio_ctor(
|
| 414 |
stream as *mut u8,
|
| 415 |
ids,
|
|
@@ -500,8 +460,6 @@ unsafe extern "C" fn hook_init_hardware() {
|
|
| 500 |
pub fn install() -> Result<(), String> {
|
| 501 |
unsafe {
|
| 502 |
TIMER_CTOR_ADDR = rb(va::GAME_ENGINE_TIMER_CTOR);
|
| 503 |
-
DSSOUND_CTOR_ADDR = rb(va::CONSTRUCT_DS_SOUND);
|
| 504 |
-
DSSOUND_INIT_BUF_ADDR = rb(va::DSSOUND_INIT_BUFFERS);
|
| 505 |
INPUT_CTRL_INIT_ADDR = rb(va::INPUT_CTRL_INIT);
|
| 506 |
STREAM_CTOR_ADDR = rb(va::STREAMING_AUDIO_CTOR);
|
| 507 |
DDISPLAY_INIT_ADDR = rb(va::DDISPLAY_INIT);
|
|
@@ -512,6 +470,10 @@ pub fn install() -> Result<(), String> {
|
|
| 512 |
va::GAME_ENGINE_INIT_HARDWARE,
|
| 513 |
hook_init_hardware as *const (),
|
| 514 |
)?;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 515 |
}
|
| 516 |
Ok(())
|
| 517 |
}
|
|
|
|
| 61 |
|
| 62 |
/// Function addresses, set in `install()`.
|
| 63 |
static mut TIMER_CTOR_ADDR: u32 = 0;
|
|
|
|
|
|
|
| 64 |
static mut INPUT_CTRL_INIT_ADDR: u32 = 0;
|
| 65 |
static mut STREAM_CTOR_ADDR: u32 = 0;
|
| 66 |
static mut DDISPLAY_INIT_ADDR: u32 = 0;
|
|
|
|
| 73 |
/// Saved ESI across the `call_input_ctrl_init` call.
|
| 74 |
static mut INPUT_CTRL_SAVED_ESI: u32 = 0;
|
| 75 |
|
|
|
|
|
|
|
| 76 |
|
| 77 |
// βββ Bridges βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 78 |
//
|
|
|
|
| 98 |
);
|
| 99 |
}
|
| 100 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
/// FUN_0058C0D0: `usercall(ESI=input_ctrl)` + stdcall(4 params), RET 0x10.
|
| 102 |
/// Caller sets `INPUT_CTRL_ESI = input_ctrl` before calling.
|
| 103 |
/// FUN_0058C0D0's `RET 0x10` cleans all 4 args, so `retl` here needs no cleanup.
|
|
|
|
| 192 |
/// if COM steps fail, matching original WA behavior).
|
| 193 |
unsafe fn create_dssound(hwnd: u32) -> *mut DSSound {
|
| 194 |
use windows::Win32::Media::Audio::DirectSound::{
|
| 195 |
+
DirectSoundCreate, IDirectSound, IDirectSoundBuffer,
|
| 196 |
+
DSBUFFERDESC, DSBCAPS_PRIMARYBUFFER, DSBPLAY_LOOPING, DSSCL_PRIORITY,
|
| 197 |
};
|
| 198 |
+
use windows::Win32::Foundation::HWND;
|
| 199 |
|
| 200 |
+
// Pure Rust construction β replaces call_dssound_ctor bridge.
|
| 201 |
+
let snd = WABox::<DSSound>::from_value(DSSound::new(hwnd)).leak();
|
|
|
|
| 202 |
|
| 203 |
+
// DirectSound COM initialization β replaces call_dssound_init_buffers bridge.
|
| 204 |
let mut ds: Option<IDirectSound> = None;
|
| 205 |
if DirectSoundCreate(None, &mut ds, None).is_ok() {
|
| 206 |
let ds = ds.unwrap();
|
| 207 |
+
|
| 208 |
+
// SetCooperativeLevel(hwnd, DSSCL_PRIORITY)
|
| 209 |
+
let _ = ds.SetCooperativeLevel(HWND(hwnd as _), DSSCL_PRIORITY);
|
| 210 |
+
|
| 211 |
+
// CreateSoundBuffer with DSBCAPS_PRIMARYBUFFER (no format β primary buffer)
|
| 212 |
+
let desc = DSBUFFERDESC {
|
| 213 |
+
dwSize: core::mem::size_of::<DSBUFFERDESC>() as u32,
|
| 214 |
+
dwFlags: DSBCAPS_PRIMARYBUFFER,
|
| 215 |
+
..core::mem::zeroed()
|
| 216 |
+
};
|
| 217 |
+
let mut primary: Option<IDirectSoundBuffer> = None;
|
| 218 |
+
if ds.CreateSoundBuffer(&desc, &mut primary, None).is_ok() {
|
| 219 |
+
let primary = primary.unwrap();
|
| 220 |
+
|
| 221 |
+
// GetCaps to populate primary_buffer_caps
|
| 222 |
+
let mut caps = core::mem::zeroed::<windows::Win32::Media::Audio::DirectSound::DSBCAPS>();
|
| 223 |
+
caps.dwSize = core::mem::size_of_val(&caps) as u32;
|
| 224 |
+
let _ = primary.GetCaps(&mut caps);
|
| 225 |
+
(*snd).primary_buffer_caps = caps.dwBufferBytes;
|
| 226 |
+
|
| 227 |
+
// Start primary buffer looping
|
| 228 |
if primary.Play(0, 0, DSBPLAY_LOOPING).is_ok() {
|
| 229 |
(*snd).init_success = 1;
|
| 230 |
}
|
| 231 |
+
|
| 232 |
+
// Store COM pointers as raw u32 (WA owns the references)
|
| 233 |
+
(*snd).primary_buffer = core::mem::transmute_copy(&primary);
|
| 234 |
+
core::mem::forget(primary);
|
| 235 |
}
|
| 236 |
+
|
| 237 |
+
(*snd).direct_sound = core::mem::transmute_copy(&ds);
|
| 238 |
+
core::mem::forget(ds);
|
| 239 |
}
|
| 240 |
|
| 241 |
snd
|
|
|
|
| 369 |
(*session).streaming_audio = core::ptr::null_mut();
|
| 370 |
if !(*session).sound.is_null() && gi.speech_enabled != 0 {
|
| 371 |
let stream = WABox::<StreamingAudio>::alloc(0x354, 0x334).leak();
|
| 372 |
+
let ids = (*(*session).sound).direct_sound as *mut u8;
|
| 373 |
call_streaming_audio_ctor(
|
| 374 |
stream as *mut u8,
|
| 375 |
ids,
|
|
|
|
| 460 |
pub fn install() -> Result<(), String> {
|
| 461 |
unsafe {
|
| 462 |
TIMER_CTOR_ADDR = rb(va::GAME_ENGINE_TIMER_CTOR);
|
|
|
|
|
|
|
| 463 |
INPUT_CTRL_INIT_ADDR = rb(va::INPUT_CTRL_INIT);
|
| 464 |
STREAM_CTOR_ADDR = rb(va::STREAMING_AUDIO_CTOR);
|
| 465 |
DDISPLAY_INIT_ADDR = rb(va::DDISPLAY_INIT);
|
|
|
|
| 470 |
va::GAME_ENGINE_INIT_HARDWARE,
|
| 471 |
hook_init_hardware as *const (),
|
| 472 |
)?;
|
| 473 |
+
|
| 474 |
+
// Trap functions whose only caller was GameEngine__InitHardware (now Rust).
|
| 475 |
+
hook::install_trap!("DSSound__Constructor", va::CONSTRUCT_DS_SOUND);
|
| 476 |
+
hook::install_trap!("DSSOUND_INIT_BUFFERS", va::DSSOUND_INIT_BUFFERS);
|
| 477 |
}
|
| 478 |
Ok(())
|
| 479 |
}
|