[Scummvm-git-logs] scummvm master -> b9c5a9c67dfec3eb7f627f82a03d0ba49f9ed804
dreammaster
noreply at scummvm.org
Tue Jul 28 10:45:06 UTC 2026
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
b9c5a9c67d MADS: Fix checkFade data references to a static 0,0 array
Commit: b9c5a9c67dfec3eb7f627f82a03d0ba49f9ed804
https://github.com/scummvm/scummvm/commit/b9c5a9c67dfec3eb7f627f82a03d0ba49f9ed804
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-07-28T20:45:00+10:00
Commit Message:
MADS: Fix checkFade data references to a static 0,0 array
Changed paths:
engines/mads/dragonsphere/sound/rsound.cpp
engines/mads/dragonsphere/sound/rsound.h
engines/mads/dragonsphere/sound/rsound_dragonsphere.cpp
engines/mads/dragonsphere/sound/rsound_dragonsphere.h
engines/mads/nebular/sound/rsound.cpp
diff --git a/engines/mads/dragonsphere/sound/rsound.cpp b/engines/mads/dragonsphere/sound/rsound.cpp
index 8c693cb90aa..746935fc5e8 100644
--- a/engines/mads/dragonsphere/sound/rsound.cpp
+++ b/engines/mads/dragonsphere/sound/rsound.cpp
@@ -66,7 +66,7 @@ void Channel::reset(byte *startPtr) {
void Channel::enable(int flag) {
if (_activeCount) {
_pendingStop = flag;
- _soundData = nullptr;
+ _soundData = RSound::_silenceStream;
}
}
@@ -78,6 +78,8 @@ void Channel::load(byte *pData) {
/*-----------------------------------------------------------------------*/
+byte RSound::_silenceStream[2] = { 0, 0 };
+
RSound::RSound(Audio::Mixer *mixer, const Common::Path &filename,
int dataOffset, int dataSize, int sysExOffset) : SoundDriver(mixer, filename, dataOffset, dataSize) {
_commandParam = 0;
@@ -432,11 +434,9 @@ void RSound::Channel_checkFade(Channel *channel, int midiChannel) {
return;
if (channel->_volume == 0) {
- // enable_channel_data: 2 zero bytes - a generic silence/no-op
- // stream, not driver-specific sound data (2 bytes here, unlike
- // Phantom's 3 - confirmed directly from this disassembly).
- static byte silenceStream[2] = { 0, 0 };
- channel->_pSrc = silenceStream;
+ // null_sound_data: a fixed 2-byte (0,0) silence marker - the
+ // same block Channel::enable() redirects _soundData to.
+ channel->_pSrc = _silenceStream;
channel->_pendingStop = 0;
return;
}
@@ -467,14 +467,14 @@ void RSound::resetChannelRange(int first, int last) {
}
}
-void RSound::disableChannelTo(int channelIndex, byte flag, int offset) {
+void RSound::disableChannelTo(int channelIndex, byte flag) {
Channel &ch = _channels[channelIndex];
if (!ch._activeCount)
return;
if (ch._activeCount == 1)
- ch._pSrc = loadData(offset);
+ ch._pSrc = _silenceStream;
ch._pendingStop = flag;
- ch._soundData = loadData(offset);
+ ch._soundData = _silenceStream;
}
void RSound::resetHeldNotes() {
diff --git a/engines/mads/dragonsphere/sound/rsound.h b/engines/mads/dragonsphere/sound/rsound.h
index 92203fd9b03..82aff15ddbb 100644
--- a/engines/mads/dragonsphere/sound/rsound.h
+++ b/engines/mads/dragonsphere/sound/rsound.h
@@ -157,8 +157,11 @@ public:
* masking, same "mutate 3 bytes then sendSysEx" shape found at
* sub_108A1) - NOT independently confirmed by inspecting the literal
* bytes at rsound.dr1's offset 0x67 sysex template.
- * - The silence/no-op stream used by Channel_checkFade is 2 zero bytes
- * here (enable_channel_data), not Phantom's 3.
+ * - null_sound_data (see _silenceStream) is a fixed 2-byte (0, 0)
+ * silence marker referenced by BOTH Channel::enable() and
+ * Channel_checkFade() - same intent as Phantom's fixed 3-byte
+ * silence stream (2 bytes here, unlike Phantom's 3 - confirmed
+ * directly from this disassembly).
*
* NOTE: The actual MIDI transmission (sendMidiByte()) currently just logs
* via warning() - it isn't hooked up to a real ScummVM MIDI/MT-32 output
@@ -206,6 +209,24 @@ private:
*/
int _sysExOffset;
+ /**
+ * "null_sound_data" - a fixed 2-byte (0,0) silence marker,
+ * referenced by BOTH Channel::enable() (Channel_enable, written to
+ * _soundData) and Channel_checkFade() (written to _pSrc once a
+ * pending-stop channel's volume has fully decayed) - the same single
+ * symbol in the disassembly, confirmed at a different address in
+ * each driver's own resource file, but always the same (0,0)
+ * content, same intent as Phantom's fixed 3-byte silence stream.
+ * pollActiveChannel() processing a (note=0, duration=0) pair sets
+ * _activeCount to 0, and its own top-of-function guard (checked
+ * before any decrement) then short-circuits every later call before
+ * _pSrc is ever read again - so only these 2 bytes are ever actually
+ * consumed. A shared static array (rather than reading via
+ * loadData()) is therefore sufficient and avoids needing a
+ * per-driver offset for content that never varies.
+ */
+ static byte _silenceStream[2];
+
/**
* General-purpose script variable table (matches the equivalent
* Phantom mechanism, 32 bytes).
@@ -286,14 +307,11 @@ protected:
/**
* A driver-specific variant of Channel::enable() confirmed across
- * multiple drivers so far (RSound4's sub_1092A targeting 0x1F8B,
- * RSound5's sub_10854 targeting 0x20C9) - redirects _soundData (and,
- * if the channel is about to expire this tick, _pSrc too) to
- * loadData(offset) instead of nullptr. The specific offset is always
- * driver-specific "silence"/placeholder sound data, passed explicitly
- * rather than hardcoded here.
- */
- void disableChannelTo(int channelIndex, byte flag, int offset);
+ * multiple drivers so far (RSound4's sub_1092A, RSound5's sub_10854)
+ * - redirects _soundData (and, if the channel is about to expire this
+ * tick, _pSrc too) to _silenceStream instead of nullptr.
+ */
+ void disableChannelTo(int channelIndex, byte flag);
/**
* Resets the _heldNotes table (see its field comment). Protected so
diff --git a/engines/mads/dragonsphere/sound/rsound_dragonsphere.cpp b/engines/mads/dragonsphere/sound/rsound_dragonsphere.cpp
index c82a5b2ecbb..281512285bb 100644
--- a/engines/mads/dragonsphere/sound/rsound_dragonsphere.cpp
+++ b/engines/mads/dragonsphere/sound/rsound_dragonsphere.cpp
@@ -1264,9 +1264,9 @@ int RSound4::command1() {
if (isSoundActive(loadData(0x1F4F)))
return 0;
_fadeCheckPeriod = 1; // armFadeCheck
- disableChannelTo(5, 0xFF, 0x1F8B); // channel 6 (redundant re-disable if command3's own gate already did it - harmless)
- disableChannelTo(6, 0xFF, 0x1F8B); // channel 7
- disableChannelTo(7, 0xFF, 0x1F8B); // channel 8
+ disableChannelTo(5, 0xFF); // channel 6 (redundant re-disable if command3's own gate already did it - harmless)
+ disableChannelTo(6, 0xFF); // channel 7
+ disableChannelTo(7, 0xFF); // channel 8
return 0;
}
@@ -1281,15 +1281,15 @@ int RSound4::command3() {
// familiar "lower" group), then conditionally disables channel 6 too -
// only if loadData(0x1F4F) isn't already playing.
_fadeCheckPeriod = 1; // armFadeCheck
- disableChannelTo(0, 0xFF, 0x1F8B); // ch1
- disableChannelTo(1, 0xFF, 0x1F8B); // ch2
- disableChannelTo(2, 0xFF, 0x1F8B); // ch3
- disableChannelTo(3, 0xFF, 0x1F8B); // ch4
- disableChannelTo(4, 0xFF, 0x1F8B); // ch5
- disableChannelTo(8, 0xFF, 0x1F8B); // ch9
+ disableChannelTo(0, 0xFF); // ch1
+ disableChannelTo(1, 0xFF); // ch2
+ disableChannelTo(2, 0xFF); // ch3
+ disableChannelTo(3, 0xFF); // ch4
+ disableChannelTo(4, 0xFF); // ch5
+ disableChannelTo(8, 0xFF); // ch9
if (isSoundActive(loadData(0x1F4F)))
return 0;
- disableChannelTo(5, 0xFF, 0x1F8B); // ch6 (conditional)
+ disableChannelTo(5, 0xFF); // ch6 (conditional)
return 0;
}
@@ -1710,11 +1710,11 @@ int RSound5::command1() {
int RSound5::command5() {
// Matches sub_10854: same disableChannelTo mechanic as RSound4's
- // sub_1092A, but targeting loadData(0x20C9) - for channels 6,7,8.
+ // sub_1092A, for channels 6,7,8.
_fadeCheckPeriod = 1; // armFadeCheck
- disableChannelTo(5, 0xFF, 0x20C9);
- disableChannelTo(6, 0xFF, 0x20C9);
- disableChannelTo(7, 0xFF, 0x20C9);
+ disableChannelTo(5, 0xFF);
+ disableChannelTo(6, 0xFF);
+ disableChannelTo(7, 0xFF);
return 0;
}
diff --git a/engines/mads/dragonsphere/sound/rsound_dragonsphere.h b/engines/mads/dragonsphere/sound/rsound_dragonsphere.h
index e234c1a2e08..3b60a09e13b 100644
--- a/engines/mads/dragonsphere/sound/rsound_dragonsphere.h
+++ b/engines/mads/dragonsphere/sound/rsound_dragonsphere.h
@@ -329,7 +329,7 @@ public:
* command1/command2/command3/command4 are all genuinely different from
* the base class, in two ways:
* - They use the base class's disableChannelTo() (matching sub_1092A)
- * instead of Channel::enable(), targeting loadData(0x1F8B).
+ * instead of Channel::enable().
* - Channel 6 (the boundary between the "lower" 1-5,9 group and
* "upper" 6,7,8 group) is conditionally included/excluded based on
* isSoundActive(loadData(0x1F4F)) in command1/command3, and based on
@@ -439,9 +439,9 @@ public:
* enable exactly).
*
* command5 uses the base class's disableChannelTo() (matching
- * sub_10854) instead of Channel::enable(), targeting loadData(0x20C9) -
- * for channels 6,7,8 (three channels, matching the base's default upper
- * group range, just via a different mechanic).
+ * sub_10854) instead of Channel::enable(), for channels 6,7,8 (three
+ * channels, matching the base's default upper group range, just via a
+ * different mechanic).
*
* command1/2/3 are not virtual in the base class, so this driver's own
* command1() must be overridden too (calling THIS class's command5()) -
diff --git a/engines/mads/nebular/sound/rsound.cpp b/engines/mads/nebular/sound/rsound.cpp
index edc226a6eae..25779cc3537 100644
--- a/engines/mads/nebular/sound/rsound.cpp
+++ b/engines/mads/nebular/sound/rsound.cpp
@@ -343,11 +343,14 @@ void RSound::Channel_checkFade(Channel *channel) {
--channel->_volume;
sendVolume(channel->_midiChannel, channel->_volume);
} else {
- // Fully silent - recycle the channel to the fixed "silence" stream
- // (unk_14566 in the disassembly, at offset 0x3246 relative to
- // seg001's load address - matches sub_1029F exactly)
- // FIXME This reads out of bounds
- //channel->_pSrc = loadData(0x3246);
+ // Fully silent - recycle the channel to a fixed 2-byte (0,0)
+ // silence stream. pollActiveChannel() processing a (note=0,
+ // duration=0) pair sets _activeCount to 0, and its own
+ // top-of-function guard (checked before any decrement) then
+ // short-circuits every later call before _pSrc is ever read
+ // again - so only these first 2 bytes are ever consumed.
+ static byte silenceStream[2] = { 0, 0 };
+ channel->_pSrc = silenceStream;
channel->_pendingStop = 0;
}
}
More information about the Scummvm-git-logs
mailing list