fix: correct slot 9 return value, enable slots 1, 9, 10, 11
Browse filesSlot 9 was named is_channel_playing but actually implements
is_channel_finished: the original inverts DSBSTATUS_PLAYING via
NOT+AND (playingβ0, stoppedβ1). Our code returned 1 for playing,
causing callers to think channels were free and immediately reuse
them β killing all sound playback.
Rename to is_channel_finished with correct semantics. Re-enable
slots 1 (update_channels), 10 (stop_channel), 11 (release_finished)
which all work correctly with the fixed descriptor layout.
17/24 DSSound vtable slots now Rust.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/audio/dssound.rs
CHANGED
|
@@ -84,8 +84,8 @@ pub struct DSSoundVtable {
|
|
| 84 |
pub set_master_volume: unsafe extern "thiscall" fn(*mut DSSound, i32) -> u32,
|
| 85 |
/// Slot 8 (0x574980): set_channel_volume β volume on specific channel
|
| 86 |
pub set_channel_volume: unsafe extern "thiscall" fn(*mut DSSound, i32, i32) -> u32,
|
| 87 |
-
/// Slot 9 (0x5747F0):
|
| 88 |
-
pub
|
| 89 |
/// Slot 10 (0x574840): stop_channel β stops + releases buffer, returns to pool
|
| 90 |
pub stop_channel: unsafe extern "thiscall" fn(*mut DSSound, i32) -> u32,
|
| 91 |
/// Slot 11 (0x574AB0): release_finished β releases finished buffers, returns count
|
|
@@ -241,19 +241,35 @@ pub unsafe extern "thiscall" fn sub_destructor(
|
|
| 241 |
this
|
| 242 |
}
|
| 243 |
|
| 244 |
-
/// Slot 9:
|
| 245 |
-
/// `pool_id` is 1-based (1..64).
|
| 246 |
-
///
|
| 247 |
-
///
|
| 248 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
this: *mut DSSound, pool_id: i32,
|
| 250 |
) -> u8 {
|
| 251 |
let idx = (pool_id - 1) as usize;
|
| 252 |
if idx >= 64 {
|
| 253 |
return 0;
|
| 254 |
}
|
| 255 |
-
|
| 256 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
}
|
| 258 |
|
| 259 |
/// Slot 10: stop_channel β stops a buffer, releases it, and returns the pool entry.
|
|
|
|
| 84 |
pub set_master_volume: unsafe extern "thiscall" fn(*mut DSSound, i32) -> u32,
|
| 85 |
/// Slot 8 (0x574980): set_channel_volume β volume on specific channel
|
| 86 |
pub set_channel_volume: unsafe extern "thiscall" fn(*mut DSSound, i32, i32) -> u32,
|
| 87 |
+
/// Slot 9 (0x5747F0): is_channel_finished β returns 1 if stopped, 0 if playing
|
| 88 |
+
pub is_channel_finished: unsafe extern "thiscall" fn(*mut DSSound, i32) -> u8,
|
| 89 |
/// Slot 10 (0x574840): stop_channel β stops + releases buffer, returns to pool
|
| 90 |
pub stop_channel: unsafe extern "thiscall" fn(*mut DSSound, i32) -> u32,
|
| 91 |
/// Slot 11 (0x574AB0): release_finished β releases finished buffers, returns count
|
|
|
|
| 241 |
this
|
| 242 |
}
|
| 243 |
|
| 244 |
+
/// Slot 9: is_channel_finished β checks if a buffer pool entry has stopped playing.
|
| 245 |
+
/// `pool_id` is 1-based (1..64).
|
| 246 |
+
///
|
| 247 |
+
/// Returns:
|
| 248 |
+
/// - **0** if the buffer IS playing (channel busy, don't reuse)
|
| 249 |
+
/// - **1** if the buffer is NOT playing, or error, or no desc (channel free)
|
| 250 |
+
/// - **0** if pool_id is out of range
|
| 251 |
+
///
|
| 252 |
+
/// The original inverts DSBSTATUS_PLAYING (bit 0): `NOT status; AND 1`.
|
| 253 |
+
pub unsafe extern "thiscall" fn is_channel_finished(
|
| 254 |
this: *mut DSSound, pool_id: i32,
|
| 255 |
) -> u8 {
|
| 256 |
let idx = (pool_id - 1) as usize;
|
| 257 |
if idx >= 64 {
|
| 258 |
return 0;
|
| 259 |
}
|
| 260 |
+
let snd = &*this;
|
| 261 |
+
let desc_idx = snd.buffer_pool_shadow[idx];
|
| 262 |
+
if desc_idx < 0 || desc_idx as usize >= 8 {
|
| 263 |
+
return 1; // no desc β finished/free
|
| 264 |
+
}
|
| 265 |
+
let desc = &snd.channel_descs[desc_idx as usize];
|
| 266 |
+
let Some(buf) = desc.buffer() else { return 1 };
|
| 267 |
+
match buf.GetStatus() {
|
| 268 |
+
Ok(status) => {
|
| 269 |
+
if status & 1 != 0 { 0 } else { 1 } // playing β 0, stopped β 1
|
| 270 |
+
}
|
| 271 |
+
Err(_) => 1, // error β treat as finished
|
| 272 |
+
}
|
| 273 |
}
|
| 274 |
|
| 275 |
/// Slot 10: stop_channel β stops a buffer, releases it, and returns the pool entry.
|
crates/openwa-core/src/audio/mod.rs
CHANGED
|
@@ -9,7 +9,7 @@ pub use active_sound::{ActiveSoundEntry, ActiveSoundTable};
|
|
| 9 |
pub use dssound::{DSSound, DSSoundVtable, ChannelDescriptor};
|
| 10 |
pub use dssound::{
|
| 11 |
update_channels, release_finished,
|
| 12 |
-
is_slot_loaded,
|
| 13 |
sub_destructor as dssound_sub_destructor, load_wav,
|
| 14 |
noop as dssound_noop, returns_0 as dssound_returns_0, returns_1 as dssound_returns_1,
|
| 15 |
};
|
|
|
|
| 9 |
pub use dssound::{DSSound, DSSoundVtable, ChannelDescriptor};
|
| 10 |
pub use dssound::{
|
| 11 |
update_channels, release_finished,
|
| 12 |
+
is_slot_loaded, is_channel_finished, stop_channel,
|
| 13 |
sub_destructor as dssound_sub_destructor, load_wav,
|
| 14 |
noop as dssound_noop, returns_0 as dssound_returns_0, returns_1 as dssound_returns_1,
|
| 15 |
};
|
crates/openwa-wormkit/src/replacements/sound.rs
CHANGED
|
@@ -169,7 +169,7 @@ unsafe fn patch_dssound_vtable() -> Result<(), String> {
|
|
| 169 |
use openwa_core::vtable::patch_vtable;
|
| 170 |
use openwa_core::audio::{
|
| 171 |
update_channels, release_finished,
|
| 172 |
-
is_slot_loaded,
|
| 173 |
load_wav, dssound_noop, dssound_returns_0, dssound_returns_1,
|
| 174 |
};
|
| 175 |
|
|
@@ -179,18 +179,17 @@ unsafe fn patch_dssound_vtable() -> Result<(), String> {
|
|
| 179 |
// Slot 12: load_wav β WAV file β DirectSound secondary buffer (hound + windows crate)
|
| 180 |
*vt.add(12) = load_wav as *const () as u32;
|
| 181 |
|
| 182 |
-
//
|
| 183 |
-
|
| 184 |
-
// runtime dumps; issue is likely COM refcount handling in buffer().
|
| 185 |
-
// *vt.add(1) = update_channels as *const () as u32;
|
| 186 |
-
// *vt.add(11) = release_finished as *const () as u32;
|
| 187 |
|
| 188 |
-
//
|
| 189 |
-
|
| 190 |
-
// *vt.add(9) = is_channel_playing as *const () as u32;
|
| 191 |
|
| 192 |
-
// Slot
|
| 193 |
-
|
|
|
|
|
|
|
|
|
|
| 194 |
|
| 195 |
// Slot 13: is_slot_loaded β channel_slots check
|
| 196 |
*vt.add(13) = is_slot_loaded as *const () as u32;
|
|
@@ -210,6 +209,6 @@ unsafe fn patch_dssound_vtable() -> Result<(), String> {
|
|
| 210 |
// Trivial returns-1 (slot 23)
|
| 211 |
*vt.add(23) = dssound_returns_1 as *const () as u32;
|
| 212 |
|
| 213 |
-
let _ = log_line("[Sound] DSSound vtable: patched
|
| 214 |
}).map_err(|e| e.to_string())
|
| 215 |
}
|
|
|
|
| 169 |
use openwa_core::vtable::patch_vtable;
|
| 170 |
use openwa_core::audio::{
|
| 171 |
update_channels, release_finished,
|
| 172 |
+
is_slot_loaded, is_channel_finished, stop_channel, dssound_sub_destructor,
|
| 173 |
load_wav, dssound_noop, dssound_returns_0, dssound_returns_1,
|
| 174 |
};
|
| 175 |
|
|
|
|
| 179 |
// Slot 12: load_wav β WAV file β DirectSound secondary buffer (hound + windows crate)
|
| 180 |
*vt.add(12) = load_wav as *const () as u32;
|
| 181 |
|
| 182 |
+
// Slot 1: update_channels β release finished buffers each frame
|
| 183 |
+
*vt.add(1) = update_channels as *const () as u32;
|
|
|
|
|
|
|
|
|
|
| 184 |
|
| 185 |
+
// Slot 11: release_finished β like update_channels, returns count
|
| 186 |
+
*vt.add(11) = release_finished as *const () as u32;
|
|
|
|
| 187 |
|
| 188 |
+
// Slot 9: is_channel_finished β returns 0 if playing, 1 if stopped/free
|
| 189 |
+
*vt.add(9) = is_channel_finished as *const () as u32;
|
| 190 |
+
|
| 191 |
+
// Slot 10: stop_channel β stops buffer, releases, returns to pool
|
| 192 |
+
*vt.add(10) = stop_channel as *const () as u32;
|
| 193 |
|
| 194 |
// Slot 13: is_slot_loaded β channel_slots check
|
| 195 |
*vt.add(13) = is_slot_loaded as *const () as u32;
|
|
|
|
| 209 |
// Trivial returns-1 (slot 23)
|
| 210 |
*vt.add(23) = dssound_returns_1 as *const () as u32;
|
| 211 |
|
| 212 |
+
let _ = log_line("[Sound] DSSound vtable: patched 17/24 slots with Rust");
|
| 213 |
}).map_err(|e| e.to_string())
|
| 214 |
}
|