[Scummvm-git-logs] scummvm master -> 98627e8de0cdd6dde66052a9b0c0159bcb2a8b78

dreammaster noreply at scummvm.org
Tue Jul 28 21:00:07 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:
98627e8de0 MADS: Added sendSysExSequence to RSound initialization


Commit: 98627e8de0cdd6dde66052a9b0c0159bcb2a8b78
    https://github.com/scummvm/scummvm/commit/98627e8de0cdd6dde66052a9b0c0159bcb2a8b78
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-07-29T06:59:51+10:00

Commit Message:
MADS: Added sendSysExSequence to RSound initialization

Assisted-by: Claude Code:claude-opus-4.8

Changed paths:
    engines/mads/dragonsphere/sound/rsound.cpp
    engines/mads/dragonsphere/sound/rsound.h
    engines/mads/nebular/sound/rsound.cpp
    engines/mads/nebular/sound/rsound.h
    engines/mads/phantom/sound/rsound.cpp
    engines/mads/phantom/sound/rsound.h


diff --git a/engines/mads/dragonsphere/sound/rsound.cpp b/engines/mads/dragonsphere/sound/rsound.cpp
index a861e5e9357..a9190e019f2 100644
--- a/engines/mads/dragonsphere/sound/rsound.cpp
+++ b/engines/mads/dragonsphere/sound/rsound.cpp
@@ -121,7 +121,12 @@ RSound::RSound(Audio::Mixer *mixer, const Common::Path &filename,
 	for (int i = 0; i < ARRAYSIZE(_scriptVariables); ++i)
 		_scriptVariables[i] = 0;
 
+	// Matches initDeviceOnce: command0() then sendSysExSequence(). The
+	// disassembly's _deviceInitialized guard flag is omitted - this
+	// constructor only ever runs once per driver instance, so there's
+	// nothing to guard against.
 	command0();
+	sendSysExSequence();
 }
 
 void RSound::validate() {
@@ -349,23 +354,36 @@ void RSound::sendGmResetRange(int high, int low) {
 	}
 }
 
-void RSound::sendSysExData(const byte *pData) {
+const byte *RSound::sendSysExData(const byte *pData) {
 	static const byte header[] = { 0xF0, 0x41, 0x10, 0x16, 0x12 };
 	for (int i = 0; i < ARRAYSIZE(header); ++i)
 		sendMidiByte(header[i]);
 
 	_sysexChecksum = 0;
-	for (int i = 0; pData[i] != 0xFF; ++i) {
+	int i = 0;
+	for (; pData[i] != 0xFF; ++i) {
 		sendMidiByte(pData[i]);
 		_sysexChecksum += pData[i];
 	}
 
 	sendMidiByte((~_sysexChecksum + 1) & 0x7F);
 	sendMidiByte(0xF7);
+
+	return &pData[i];
+}
+
+const byte *RSound::sendSysEx(int offset) {
+	return sendSysExData(loadData(offset));
 }
 
-void RSound::sendSysEx(int offset) {
-	sendSysExData(loadData(offset));
+void RSound::sendSysExSequence() {
+	const byte *pData = loadData(_sysExOffset);
+	for (;;) {
+		pData = sendSysExData(pData);
+		++pData;
+		if (*pData == 0xFF)
+			break;
+	}
 }
 
 void RSound::sendPatchInitSequence() {
diff --git a/engines/mads/dragonsphere/sound/rsound.h b/engines/mads/dragonsphere/sound/rsound.h
index 82aff15ddbb..7ffa2098d43 100644
--- a/engines/mads/dragonsphere/sound/rsound.h
+++ b/engines/mads/dragonsphere/sound/rsound.h
@@ -483,12 +483,26 @@ protected:
 	 * Sends a single Roland DT1-style SysEx message from a raw buffer:
 	 * the fixed header, then bytes from pData up to (not including) a
 	 * 0xFF terminator - each byte sent and folded into a running
-	 * checksum - then the checksum byte and a closing F7.
+	 * checksum - then the checksum byte and a closing F7. Returns a
+	 * pointer to the terminating 0xFF byte (matching the disassembly's
+	 * own si register value on return), so callers walking a sequence
+	 * of consecutive messages can advance past it to find the next one.
 	 */
-	void sendSysExData(const byte *pData);
+	const byte *sendSysExData(const byte *pData);
 
 	/** sendSysExData() for a block already in this driver's own loaded sound data. */
-	void sendSysEx(int offset);
+	const byte *sendSysEx(int offset);
+
+	/**
+	 * Matches sendSysExSequence: repeatedly calls sendSysEx(), starting
+	 * from this driver's own command0_array (_sysExOffset) and advancing
+	 * past each message's terminating 0xFF to the start of the next one,
+	 * until an empty message (two consecutive 0xFF bytes) marks the end
+	 * of the table. Called once from the constructor (matching
+	 * initDeviceOnce) - the disassembly's _deviceInitialized guard flag
+	 * isn't needed since nothing else ever calls this again.
+	 */
+	void sendSysExSequence();
 
 	void sendPatchInitSequence();
 
diff --git a/engines/mads/nebular/sound/rsound.cpp b/engines/mads/nebular/sound/rsound.cpp
index 25779cc3537..473cb5037ba 100644
--- a/engines/mads/nebular/sound/rsound.cpp
+++ b/engines/mads/nebular/sound/rsound.cpp
@@ -100,7 +100,12 @@ RSound::RSound(Audio::Mixer *mixer, const Common::Path &filename,
 	// MIDI channel reset messages to the device. Since we don't do real hardware
 	// detection here, just go straight to command0() - matches ASound's
 	// constructor calling command0() directly.
+	// Matches initDeviceOnce: command0() then sendSysExSequence(). The
+	// disassembly's _deviceInitialized guard flag is omitted - this
+	// constructor only ever runs once per driver instance, so there's
+	// nothing to guard against.
 	command0();
+	sendSysExSequence();
 
 	_midiDriver->setTimerCallback(this, &timerCallback);
 }
@@ -284,38 +289,44 @@ void RSound::restoreChannelVolume(int midiChannel, int volume) {
 	sendVolume(midiChannel, volume);
 }
 
-void RSound::sendSysEx(int offset) {
+byte *RSound::sendSysExData(byte *pData) {
+	// FIXME If the data is malformed, this will read out of bounds. Not sure
+	// how the original code handles this.
+	uint16 length = 0;
+	for (int i = 0; pData[i] != 0xFF; ++i) {
+		length++;
+	}
+
+	// FIXME This call adds the necessary delay for the MT-32 to process the
+	// SysEx message, which will make the engine unresponsive.
+	// This can be fixed using the SysEx queue (specify true as 3rd param).
+	// Driver status can then be checked from the main event loop using
+	// _midiDriver->isReady().
+	_midiDriver->sysExMT32(pData, length);
+
+	return &pData[length];
+}
+
+byte *RSound::sendSysEx(int offset) {
 	if (offset < 0) {
 		// _sysExOffset wasn't given a confirmed value for this driver yet
 		// (see the constructor) - deliberately not scanning for a 0xFF
 		// terminator from an unconfirmed/arbitrary offset, since that
 		// could read well past the actual command0_array table.
 		warning("RSound::sendSysEx: command0_array offset not yet known for this driver");
-		return;
+		return nullptr;
 	}
 
-	// There is a whole block of SysEx data at this offset that has to be
-	// sent to the MT-32. Each entry is terminated by 0xFF; the block seems to
-	// be terminated by a second 0xFF following the last entry.
-	// FIXME If the data is malformed, this will read out of bounds. Not sure
-	// how the original code handles this.
-	byte *pData = loadData(offset);
-	while (true) {
-		uint16 length = 0;
-		for (int i = 0; pData[i] != 0xFF; ++i) {
-			length++;
-		}
-		if (length == 0) {
-			// Two subsequent 0xFF bytes - end of SysEx data block.
+	return sendSysExData(loadData(offset));
+}
+
+void RSound::sendSysExSequence() {
+	byte *pData = loadData(_sysExOffset);
+	for (;;) {
+		pData = sendSysExData(pData);
+		++pData;
+		if (*pData == 0xFF)
 			break;
-		}
-		// FIXME This call adds the necessary delay for the MT-32 to process the
-		// SysEx message, which will make the engine unresponsive.
-		// This can be fixed using the SysEx queue (specify true as 3rd param).
-		// Driver status can then be checked from the main event loop using
-		// _midiDriver->isReady().
-		_midiDriver->sysExMT32(pData, length);
-		pData += length + 1;
 	}
 }
 
diff --git a/engines/mads/nebular/sound/rsound.h b/engines/mads/nebular/sound/rsound.h
index dfda2782a6e..1b17a0f7062 100644
--- a/engines/mads/nebular/sound/rsound.h
+++ b/engines/mads/nebular/sound/rsound.h
@@ -261,19 +261,30 @@ protected:
 	void sendMidiChannelReset(int first, int last);
 
 	/**
-	 * Sends a single Roland DT1-style SysEx message: the fixed
-	 * _sysExHeader (F0 41 10 16 12), then bytes from loadData(offset)
-	 * up to (but not including) a 0xFF terminator - each byte sent and
-	 * folded into a running checksum - then the two's-complement/7-bit
-	 * checksum byte and a closing F7. Matches sub_1041E exactly.
-	 *
-	 * The original follows this with a busy-wait delay loop
-	 * (_sysexDelayCount iterations) to give the MT-32 time to process
-	 * the message before the next one arrives; irrelevant while
-	 * sendMidiByte() is just a warning() stub, so not ported - will need
-	 * a real (non-blocking) delay once actual MIDI output exists.
+	 * Sends a single SysEx message: bytes from pData up to (but not
+	 * including) a 0xFF terminator, via the real MT32GM MIDI driver
+	 * (_midiDriver->sysExMT32()) - this part is a work in progress and
+	 * intentionally NOT shared with the Dragonsphere/Phantom RSound
+	 * families, which still route through the sendMidiByte() warning()
+	 * stub. Returns a pointer to the terminating 0xFF byte, so callers
+	 * walking a sequence of consecutive messages can advance past it to
+	 * find the next one.
 	 */
-	void sendSysEx(int offset);
+	byte *sendSysExData(byte *pData);
+
+	/** sendSysExData() for a block already in this driver's own loaded sound data. */
+	byte *sendSysEx(int offset);
+
+	/**
+	 * Matches sendSysExSequence: repeatedly calls sendSysEx(), starting
+	 * from this driver's own command0_array (_sysExOffset) and advancing
+	 * past each message's terminating 0xFF to the start of the next one,
+	 * until an empty message (two consecutive 0xFF bytes) marks the end
+	 * of the table. Called once from the constructor (matching
+	 * initDeviceOnce) - the disassembly's _deviceInitialized guard flag
+	 * isn't needed since nothing else ever calls this again.
+	 */
+	void sendSysExSequence();
 
 	virtual int command0();
 	int command1();
diff --git a/engines/mads/phantom/sound/rsound.cpp b/engines/mads/phantom/sound/rsound.cpp
index 8bfcf22f065..73ce32466b9 100644
--- a/engines/mads/phantom/sound/rsound.cpp
+++ b/engines/mads/phantom/sound/rsound.cpp
@@ -120,7 +120,12 @@ RSound::RSound(Audio::Mixer *mixer, const Common::Path &filename,
 	for (int i = 0; i < ARRAYSIZE(_scriptVariables); ++i)
 		_scriptVariables[i] = 0;
 
+	// Matches initDeviceOnce: command0() then sendSysExSequence(). The
+	// disassembly's _deviceInitialized guard flag is omitted - this
+	// constructor only ever runs once per driver instance, so there's
+	// nothing to guard against.
 	command0();
+	sendSysExSequence();
 }
 
 void RSound::validate() {
@@ -331,23 +336,36 @@ void RSound::sendGmReset(int count) {
 	}
 }
 
-void RSound::sendSysExData(const byte *pData) {
+const byte *RSound::sendSysExData(const byte *pData) {
 	static const byte header[] = { 0xF0, 0x41, 0x10, 0x16, 0x12 };
 	for (int i = 0; i < ARRAYSIZE(header); ++i)
 		sendMidiByte(header[i]);
 
 	_sysexChecksum = 0;
-	for (int i = 0; pData[i] != 0xFF; ++i) {
+	int i = 0;
+	for (; pData[i] != 0xFF; ++i) {
 		sendMidiByte(pData[i]);
 		_sysexChecksum += pData[i];
 	}
 
 	sendMidiByte((~_sysexChecksum + 1) & 0x7F);
 	sendMidiByte(0xF7);
+
+	return &pData[i];
+}
+
+const byte *RSound::sendSysEx(int offset) {
+	return sendSysExData(loadData(offset));
 }
 
-void RSound::sendSysEx(int offset) {
-	sendSysExData(loadData(offset));
+void RSound::sendSysExSequence() {
+	const byte *pData = loadData(_sysExOffset);
+	for (;;) {
+		pData = sendSysExData(pData);
+		++pData;
+		if (*pData == 0xFF)
+			break;
+	}
 }
 
 void RSound::sendPatchInitSequence() {
diff --git a/engines/mads/phantom/sound/rsound.h b/engines/mads/phantom/sound/rsound.h
index a781927492e..971262d6492 100644
--- a/engines/mads/phantom/sound/rsound.h
+++ b/engines/mads/phantom/sound/rsound.h
@@ -380,16 +380,26 @@ protected:
 	 * checksum - then the checksum byte and a closing F7. The shared
 	 * core of sendSysEx()/sendReverbSysEx() below - split out so
 	 * hardcoded protocol buffers (not driver-specific loaded sound data)
-	 * can be sent without going through loadData().
+	 * can be sent without going through loadData(). Returns a pointer to
+	 * the terminating 0xFF byte (matching the disassembly's own si
+	 * register value on return), so callers walking a sequence of
+	 * consecutive messages can advance past it to find the next one.
 	 */
-	void sendSysExData(const byte *pData);
+	const byte *sendSysExData(const byte *pData);
+
+	/** sendSysExData() for a block already in this driver's own loaded sound data. */
+	const byte *sendSysEx(int offset);
 
 	/**
-	 * sendSysExData() for a block already in this driver's own loaded
-	 * sound data. Matches sendSysEx exactly (same algorithm as the
-	 * confirmed Rex Nebular RSound::sendSysEx()).
+	 * Matches sendSysExSequence: repeatedly calls sendSysEx(), starting
+	 * from this driver's own command0_array (_sysExOffset) and advancing
+	 * past each message's terminating 0xFF to the start of the next one,
+	 * until an empty message (two consecutive 0xFF bytes) marks the end
+	 * of the table. Called once from the constructor (matching
+	 * initDeviceOnce) - the disassembly's _deviceInitialized guard flag
+	 * isn't needed since nothing else ever calls this again.
 	 */
-	void sendSysEx(int offset);
+	void sendSysExSequence();
 
 	/**
 	 * TENTATIVE: matches sub_102BE - a nested loop (4 outer x 32 inner




More information about the Scummvm-git-logs mailing list