paavohtl Claude Opus 4.6 (1M context) commited on
Commit
0976585
Β·
1 Parent(s): 085ae61

feat: replace DSSound vtable slots 9 (is_channel_playing) and 10 (stop_channel)

Browse files

Add typed ChannelDescriptor::buffer() and take_buffer() helpers for
safe IDirectSoundBuffer access. Remove cfg(target_os) guards β€” this
project is 32-bit Windows only.

Slot 9: checks IDirectSoundBuffer::GetStatus via typed helper.
Slot 10: stops buffer via take_buffer() + Drop, resets descriptor,
returns pool index to free list, removes from used list.

14/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
@@ -1,5 +1,7 @@
1
  use crate::fixed::Fixed;
2
 
 
 
3
  /// DSSound β€” DirectSound audio subsystem.
4
  ///
5
  /// Created by DSSound__Constructor (0x573D50).
@@ -27,7 +29,8 @@ pub struct ChannelDescriptor {
27
  pub volume_num: i32,
28
  /// Volume value (scaled, used by set_channel_volume)
29
  pub volume_val: i32,
30
- /// IDirectSoundBuffer* for the active buffer on this channel (0 = empty)
 
31
  pub ds_buffer: Ptr32,
32
  /// Pool index tracking which buffer pool entry this channel uses
33
  pub pool_index: u32,
@@ -35,6 +38,28 @@ pub struct ChannelDescriptor {
35
 
36
  const _: () = assert!(core::mem::size_of::<ChannelDescriptor>() == 0x18);
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  /// DSSound vtable layout (24 slots at 0x66AF20).
39
  ///
40
  /// Methods operate on the 8 channel descriptors and 64-entry buffer pool.
@@ -160,6 +185,85 @@ pub unsafe extern "thiscall" fn returns_0(_this: *mut DSSound) -> u32 { 0 }
160
  /// Trivial stub β€” returns 1. Used for slot 23.
161
  pub unsafe extern "thiscall" fn returns_1(_this: *mut DSSound) -> u32 { 1 }
162
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  /// Slot 12: load_wav β€” load a WAV file into a DirectSound secondary buffer
164
  /// and store the buffer pointer at `channel_slots[slot_idx]`.
165
  ///
@@ -170,7 +274,6 @@ pub unsafe extern "thiscall" fn returns_1(_this: *mut DSSound) -> u32 { 1 }
170
  /// for DirectSound COM calls.
171
  ///
172
  /// Returns 1 on success, 0 on failure (matching original).
173
- #[cfg(target_os = "windows")]
174
  pub unsafe extern "thiscall" fn load_wav(
175
  this: *mut DSSound,
176
  slot_idx: i32,
 
1
  use crate::fixed::Fixed;
2
 
3
+ use windows::Win32::Media::Audio::DirectSound::IDirectSoundBuffer;
4
+
5
  /// DSSound β€” DirectSound audio subsystem.
6
  ///
7
  /// Created by DSSound__Constructor (0x573D50).
 
29
  pub volume_num: i32,
30
  /// Volume value (scaled, used by set_channel_volume)
31
  pub volume_val: i32,
32
+ /// IDirectSoundBuffer* for the active buffer on this channel (0 = empty).
33
+ /// Use `ds_buffer()` to get a typed reference.
34
  pub ds_buffer: Ptr32,
35
  /// Pool index tracking which buffer pool entry this channel uses
36
  pub pool_index: u32,
 
38
 
39
  const _: () = assert!(core::mem::size_of::<ChannelDescriptor>() == 0x18);
40
 
41
+ impl ChannelDescriptor {
42
+ /// Get the DirectSound buffer as a typed reference, if present.
43
+ pub unsafe fn buffer(&self) -> Option<&IDirectSoundBuffer> {
44
+ if self.ds_buffer != 0 {
45
+ Some(&*(&self.ds_buffer as *const Ptr32 as *const IDirectSoundBuffer))
46
+ } else {
47
+ None
48
+ }
49
+ }
50
+
51
+ /// Take ownership of the buffer (for releasing). Zeroes the field.
52
+ pub unsafe fn take_buffer(&mut self) -> Option<IDirectSoundBuffer> {
53
+ if self.ds_buffer != 0 {
54
+ let buf: IDirectSoundBuffer = core::mem::transmute_copy(&self.ds_buffer);
55
+ self.ds_buffer = 0;
56
+ Some(buf)
57
+ } else {
58
+ None
59
+ }
60
+ }
61
+ }
62
+
63
  /// DSSound vtable layout (24 slots at 0x66AF20).
64
  ///
65
  /// Methods operate on the 8 channel descriptors and 64-entry buffer pool.
 
185
  /// Trivial stub β€” returns 1. Used for slot 23.
186
  pub unsafe extern "thiscall" fn returns_1(_this: *mut DSSound) -> u32 { 1 }
187
 
188
+ /// Slot 9: is_channel_playing β€” checks if a buffer pool entry is still playing.
189
+ /// `pool_id` is 1-based (1..64). Returns 1 if playing or invalid, 0 if stopped.
190
+ pub unsafe extern "thiscall" fn is_channel_playing(
191
+ this: *mut DSSound, pool_id: i32,
192
+ ) -> u8 {
193
+ let idx = (pool_id - 1) as usize;
194
+ if idx >= 64 {
195
+ return 0;
196
+ }
197
+ let snd = &*this;
198
+ let desc_idx = snd.buffer_pool_shadow[idx];
199
+ if desc_idx < 0 {
200
+ return 0;
201
+ }
202
+ let desc = &snd.channel_descs[desc_idx as usize];
203
+ let Some(buf) = desc.buffer() else { return 0 };
204
+ let status = buf.GetStatus();
205
+ if let Ok(s) = status {
206
+ if (s & 1) != 0 {
207
+ // DSBSTATUS_PLAYING = 1
208
+ return 1;
209
+ }
210
+ }
211
+ 1 // original returns 1 even on error
212
+ }
213
+
214
+ /// Slot 10: stop_channel β€” stops a buffer, releases it, and returns the pool entry.
215
+ /// `pool_id` is 1-based (1..64). Returns 1 on success, 0 on invalid.
216
+ pub unsafe extern "thiscall" fn stop_channel(
217
+ this: *mut DSSound, pool_id: i32,
218
+ ) -> u32 {
219
+ let idx = (pool_id - 1) as usize;
220
+ if idx >= 64 {
221
+ return 0;
222
+ }
223
+ let snd = &mut *this;
224
+ let desc_idx = snd.buffer_pool_shadow[idx];
225
+ if desc_idx < 0 {
226
+ return 0;
227
+ }
228
+ let di = desc_idx as usize;
229
+ let desc = &mut snd.channel_descs[di];
230
+
231
+ // Stop and release the buffer.
232
+ if let Some(buf) = desc.take_buffer() {
233
+ let _ = buf.Stop();
234
+ // Release happens when buf drops
235
+ }
236
+
237
+ // Reset descriptor to free state.
238
+ desc.volume_num = 0;
239
+ desc.volume_val = 0;
240
+ desc.flags_b = -1;
241
+ desc.flags_a = -1;
242
+
243
+ // Mark pool shadow as free.
244
+ snd.buffer_pool_shadow[idx] = -1;
245
+
246
+ // Return pool index to free list.
247
+ let free_slot = snd.buffer_pool_free_count as usize;
248
+ snd.buffer_pool_free[free_slot] = idx as u32;
249
+ snd.buffer_pool_free_count += 1;
250
+
251
+ // Remove from used list.
252
+ let used_count = snd.buffer_pool_used_count as usize;
253
+ for i in 0..used_count {
254
+ if snd.buffer_pool_used[i] == idx as u32 {
255
+ // Shift remaining entries down.
256
+ for j in i..used_count - 1 {
257
+ snd.buffer_pool_used[j] = snd.buffer_pool_used[j + 1];
258
+ }
259
+ snd.buffer_pool_used_count -= 1;
260
+ break;
261
+ }
262
+ }
263
+
264
+ 1
265
+ }
266
+
267
  /// Slot 12: load_wav β€” load a WAV file into a DirectSound secondary buffer
268
  /// and store the buffer pointer at `channel_slots[slot_idx]`.
269
  ///
 
274
  /// for DirectSound COM calls.
275
  ///
276
  /// Returns 1 on success, 0 on failure (matching original).
 
277
  pub unsafe extern "thiscall" fn load_wav(
278
  this: *mut DSSound,
279
  slot_idx: i32,
crates/openwa-core/src/audio/mod.rs CHANGED
@@ -7,7 +7,7 @@ pub mod streaming;
7
 
8
  pub use active_sound::{ActiveSoundEntry, ActiveSoundTable};
9
  pub use dssound::{DSSound, DSSoundVtable, ChannelDescriptor};
10
- pub use dssound::{is_slot_loaded, load_wav, noop as dssound_noop, returns_0 as dssound_returns_0, returns_1 as dssound_returns_1};
11
  pub use music::Music;
12
  pub use sound::SoundId;
13
  pub use speech::{SpeechLineId, SpeechLineTableEntry, SpeechSlotTable, SPEECH_LINE_TABLE_COUNT};
 
7
 
8
  pub use active_sound::{ActiveSoundEntry, ActiveSoundTable};
9
  pub use dssound::{DSSound, DSSoundVtable, ChannelDescriptor};
10
+ pub use dssound::{is_slot_loaded, is_channel_playing, stop_channel, load_wav, noop as dssound_noop, returns_0 as dssound_returns_0, returns_1 as dssound_returns_1};
11
  pub use music::Music;
12
  pub use sound::SoundId;
13
  pub use speech::{SpeechLineId, SpeechLineTableEntry, SpeechSlotTable, SPEECH_LINE_TABLE_COUNT};
crates/openwa-wormkit/src/replacements/sound.rs CHANGED
@@ -168,7 +168,8 @@ unsafe fn patch_dssound_vtable() -> Result<(), String> {
168
  use openwa_core::rebase::rb;
169
  use openwa_core::vtable::patch_vtable;
170
  use openwa_core::audio::{
171
- is_slot_loaded, load_wav, dssound_noop, dssound_returns_0, dssound_returns_1,
 
172
  };
173
 
174
  let vtable = rb(va::DS_SOUND_VTABLE) as *mut u32;
@@ -177,6 +178,12 @@ unsafe fn patch_dssound_vtable() -> Result<(), String> {
177
  // Slot 12: load_wav β€” WAV file β†’ DirectSound secondary buffer (hound + windows crate)
178
  *vt.add(12) = load_wav as *const () as u32;
179
 
 
 
 
 
 
 
180
  // Slot 13: is_slot_loaded β€” channel_slots check
181
  *vt.add(13) = is_slot_loaded as *const () as u32;
182
 
@@ -192,6 +199,6 @@ unsafe fn patch_dssound_vtable() -> Result<(), String> {
192
  // Trivial returns-1 (slot 23)
193
  *vt.add(23) = dssound_returns_1 as *const () as u32;
194
 
195
- let _ = log_line("[Sound] DSSound vtable: patched 12/24 slots with Rust");
196
  }).map_err(|e| e.to_string())
197
  }
 
168
  use openwa_core::rebase::rb;
169
  use openwa_core::vtable::patch_vtable;
170
  use openwa_core::audio::{
171
+ is_slot_loaded, is_channel_playing, stop_channel, load_wav,
172
+ dssound_noop, dssound_returns_0, dssound_returns_1,
173
  };
174
 
175
  let vtable = rb(va::DS_SOUND_VTABLE) as *mut u32;
 
178
  // Slot 12: load_wav β€” WAV file β†’ DirectSound secondary buffer (hound + windows crate)
179
  *vt.add(12) = load_wav as *const () as u32;
180
 
181
+ // Slot 9: is_channel_playing β€” GetStatus check
182
+ *vt.add(9) = is_channel_playing as *const () as u32;
183
+
184
+ // Slot 10: stop_channel β€” Stop, Release, return to pool
185
+ *vt.add(10) = stop_channel as *const () as u32;
186
+
187
  // Slot 13: is_slot_loaded β€” channel_slots check
188
  *vt.add(13) = is_slot_loaded as *const () as u32;
189
 
 
199
  // Trivial returns-1 (slot 23)
200
  *vt.add(23) = dssound_returns_1 as *const () as u32;
201
 
202
+ let _ = log_line("[Sound] DSSound vtable: patched 14/24 slots with Rust");
203
  }).map_err(|e| e.to_string())
204
  }