paavohtl Claude Opus 4.6 (1M context) commited on
Commit
886847c
Β·
1 Parent(s): 5ce5ac6

fix: correct slot 9 return value, enable slots 1, 9, 10, 11

Browse files

Slot 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): is_channel_playing β€” checks buffer status
88
- pub is_channel_playing: 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,19 +241,35 @@ pub unsafe extern "thiscall" fn sub_destructor(
241
  this
242
  }
243
 
244
- /// Slot 9: is_channel_playing β€” checks if a buffer pool entry's GetStatus succeeds.
245
- /// `pool_id` is 1-based (1..64). Returns 0 only for out-of-range pool_id.
246
- /// Original always returns 1 unless pool_id is invalid β€” it checks whether
247
- /// GetStatus succeeds (S_OK), not the actual playing status bits.
248
- pub unsafe extern "thiscall" fn is_channel_playing(
 
 
 
 
 
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
- // Original returns 1 in all other cases (found or not, playing or not).
256
- 1
 
 
 
 
 
 
 
 
 
 
 
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, is_channel_playing, 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
  };
 
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, is_channel_playing, stop_channel, dssound_sub_destructor,
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
- // Slots 1, 11 disabled β€” update_channels/release_finished crash
183
- // when iterating descriptors. Layout is confirmed correct via
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
- // Slots 9, 10 disabled β€” slot 9's return value semantics are unclear
189
- // from decompile; wrong values break sound scheduling entirely.
190
- // *vt.add(9) = is_channel_playing as *const () as u32;
191
 
192
- // Slot 10: disabled pending slot 9 fix
193
- // *vt.add(10) = stop_channel as *const () as u32;
 
 
 
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 13/24 slots with Rust");
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
  }