[Scummvm-git-logs] scummvm master -> 24813e60febcab5da73f870f035b7675e88b1d39

dreammaster noreply at scummvm.org
Sun Aug 2 06:39:01 UTC 2026


This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
7d7c2deb07 MADS: NEBULAR: Implement demo Adlib sound drivers
bec794b52c MADS: NEBULAR: Implement Rex Demo DCL decoder
24813e60fe MADS: NEBULAR: Demo specific restrictions


Commit: 7d7c2deb070ba800336c4eb834805cbb2b649a67
    https://github.com/scummvm/scummvm/commit/7d7c2deb070ba800336c4eb834805cbb2b649a67
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-08-02T16:38:49+10:00

Commit Message:
MADS: NEBULAR: Implement demo Adlib sound drivers

Changed paths:
    engines/mads/animview/functions.cpp
    engines/mads/nebular/nebular.cpp
    engines/mads/nebular/sound/asound.cpp
    engines/mads/nebular/sound/asound.h
    engines/mads/nebular/sound/asound_nebular.cpp
    engines/mads/nebular/sound/asound_nebular.h
    engines/mads/nebular/sound/sound.cpp
    engines/mads/nebular/sound/sound.h


diff --git a/engines/mads/animview/functions.cpp b/engines/mads/animview/functions.cpp
index 86d9bfcd7e5..e4f5fe50190 100644
--- a/engines/mads/animview/functions.cpp
+++ b/engines/mads/animview/functions.cpp
@@ -140,6 +140,11 @@ void flag_parse(const char *param) {
 		do_not_clear_screen = true;
 		break;
 
+	case 'z':
+		// Show stats
+		// Not implemented in ScummVM
+		break;
+
 	default:
 		error("Unsupported animview flag - %c", c);
 		break;
diff --git a/engines/mads/nebular/nebular.cpp b/engines/mads/nebular/nebular.cpp
index b23a4595cd9..807e64f8338 100644
--- a/engines/mads/nebular/nebular.cpp
+++ b/engines/mads/nebular/nebular.cpp
@@ -79,7 +79,7 @@ Common::Error RexNebularEngine::run() {
 	}
 
 	// Set up sound manager
-	_soundManager = new Sound::RexSoundManager(_mixer, _soundFlag);
+	_soundManager = new Sound::RexSoundManager(_mixer, _soundFlag, isDemo());
 	_soundManager->validate();
 
 	// Run the game
diff --git a/engines/mads/nebular/sound/asound.cpp b/engines/mads/nebular/sound/asound.cpp
index db19c02ec0c..a55577700a9 100644
--- a/engines/mads/nebular/sound/asound.cpp
+++ b/engines/mads/nebular/sound/asound.cpp
@@ -177,7 +177,7 @@ ASound::~ASound() {
 	delete _opl;
 }
 
-void ASound::validate() {
+void ASound::validate(bool isDemo) {
 	Common::File f;
 	static const char *const MD5[] = {
 		"205398468de2c8873b7d4d73d5be8ddc",
@@ -191,14 +191,23 @@ void ASound::validate() {
 		"a31e4783e098f633cbb6689adb41dd4f"
 	};
 
+	static const char *const MD5_DEMO[] = {
+		"11aa27a511abdfa905fd290b4d96e9d3",
+		"057c16af3b84b019bf088cb0ef8e6c4f"
+	};
+
 	for (int i = 1; i <= 9; ++i) {
+		if (isDemo && i != 1 && i != 9)
+			continue;
+		const char *const EXPECTED = isDemo ? MD5_DEMO[i == 1 ? 0 : 1] : MD5[i - 1];
+
 		Common::Path filename(Common::String::format("ASOUND.00%d", i));
 		if (!f.open(filename))
 			error("Could not process - %s", filename.toString().c_str());
 		Common::String md5str = Common::computeStreamMD5AsString(f, 8192);
 		f.close();
 
-		if (md5str != MD5[i - 1])
+		if (md5str != EXPECTED)
 			error("Invalid sound file - %s", filename.toString().c_str());
 	}
 }
diff --git a/engines/mads/nebular/sound/asound.h b/engines/mads/nebular/sound/asound.h
index 3af918b13b9..ffaffd1171b 100644
--- a/engines/mads/nebular/sound/asound.h
+++ b/engines/mads/nebular/sound/asound.h
@@ -328,7 +328,7 @@ public:
 	/**
 	 * Validates the Adlib sound files
 	 */
-	static void validate();
+	static void validate(bool isDemo);
 
 public:
 	/**
diff --git a/engines/mads/nebular/sound/asound_nebular.cpp b/engines/mads/nebular/sound/asound_nebular.cpp
index e107f4ccc4c..c2378aeab4d 100644
--- a/engines/mads/nebular/sound/asound_nebular.cpp
+++ b/engines/mads/nebular/sound/asound_nebular.cpp
@@ -326,6 +326,270 @@ int ASound1::command2627293032() {
 }
 
 
+/*-----------------------------------------------------------------------*/
+
+const ASoundDemo1::CommandPtr ASoundDemo1::_commandList[41] = {
+	&ASoundDemo1::command0, &ASoundDemo1::command1, &ASoundDemo1::command2, &ASoundDemo1::command3,
+	&ASoundDemo1::command4, &ASoundDemo1::command5, &ASoundDemo1::command6, &ASoundDemo1::command7,
+	&ASoundDemo1::command8, &ASoundDemo1::command9, &ASoundDemo1::command10, &ASoundDemo1::command11,
+	&ASoundDemo1::command12, &ASoundDemo1::command13, &ASoundDemo1::command14, &ASoundDemo1::command15,
+	&ASoundDemo1::command16, &ASoundDemo1::command17, &ASoundDemo1::command18, &ASoundDemo1::command19,
+	&ASoundDemo1::command20, &ASoundDemo1::command21, &ASoundDemo1::command22, &ASoundDemo1::command23,
+	&ASoundDemo1::command24, &ASoundDemo1::command25, &ASoundDemo1::command26, &ASoundDemo1::command27,
+	&ASoundDemo1::command28, &ASoundDemo1::command29, &ASoundDemo1::command30, &ASoundDemo1::command31,
+	&ASoundDemo1::command32, &ASoundDemo1::command33, &ASoundDemo1::command34, &ASoundDemo1::command35,
+	&ASoundDemo1::command36, &ASoundDemo1::command37, &ASoundDemo1::command38, &ASoundDemo1::command39,
+	&ASoundDemo1::command40
+};
+
+ASoundDemo1::ASoundDemo1(Audio::Mixer *mixer) : ASound(mixer, "asound.001", 0x1540, 0x24b0) {
+	_cmd23Toggle = false;
+
+	// Load sound samples
+	auto samplesStream = getDataStream(0x50);
+	for (int i = 0; i < 96; ++i)
+		_samples.push_back(AdlibSample(samplesStream));
+}
+
+int ASoundDemo1::command(int commandId, int param) {
+	if (commandId > 40)
+		return 0;
+
+	_commandParam = param;
+	_frameCounter = 0;
+	return (this->*_commandList[commandId])();
+}
+
+int ASoundDemo1::command9() {
+	playSoundAny(0x2220);
+	return 0;
+}
+
+int ASoundDemo1::command10() {
+	byte *pData1 = loadData(0x1C8C);
+	isSoundActive(pData1); // result unused, matches disassembly quirk
+	command1();
+	_channels[4].load(pData1);
+	_channels[5].load(loadData(0x1CBC));
+	_channels[6].load(loadData(0x1E3E));
+	_channels[7].load(loadData(0x1E6C));
+	return 0;
+}
+
+int ASoundDemo1::command11() {
+	command111213();
+	_channels[0]._volumeOffset = 0;
+	_channels[1]._volumeOffset = 0;
+	return 0;
+}
+
+int ASoundDemo1::command12() {
+	command111213();
+	_channels[0]._volumeOffset = 40;
+	_channels[1]._volumeOffset = 0;
+	return 0;
+}
+
+int ASoundDemo1::command13() {
+	command111213();
+	_channels[0]._volumeOffset = 40;
+	_channels[1]._volumeOffset = 50;
+	return 0;
+}
+
+int ASoundDemo1::command14() {
+	playSoundAny(0x1C08);
+	return 0;
+}
+
+int ASoundDemo1::command15() {
+	byte *pData1 = loadData(0x1E9C);
+	isSoundActive(pData1); // result unused, matches disassembly quirk
+	command1();
+	_channels[4].load(pData1);
+	_channels[5].load(loadData(0x1F3A));
+	_channels[6].load(loadData(0x1F92));
+	_channels[7].load(loadData(0x1FEA));
+	_channels[8].load(loadData(0x2014));
+	return 0;
+}
+
+int ASoundDemo1::command16() {
+	playSoundAny(0x222A);
+	return 0;
+}
+
+int ASoundDemo1::command17() {
+	playSoundAny(0x244C);
+	return 0;
+}
+
+int ASoundDemo1::command18() {
+	command1();
+	playSoundAny(0x225A);
+	return 0;
+}
+
+int ASoundDemo1::command19() {
+	command1();
+	playSoundAny(0x226A);
+	return 0;
+}
+
+int ASoundDemo1::command20() {
+	playSoundAny(0x22C6);
+	return 0;
+}
+
+int ASoundDemo1::command21() {
+	playSoundAny(0x22B6);
+	return 0;
+}
+
+int ASoundDemo1::command22() {
+	byte *pData = loadData(0x22E2);
+	pData[6] = (byte)((getRandomNumber() & 7) + 85);
+	playSoundAny(0x22E2);
+	return 0;
+}
+
+int ASoundDemo1::command23() {
+	_cmd23Toggle = !_cmd23Toggle;
+	playSoundAny(_cmd23Toggle ? 0x22EC : 0x22F4);
+	return 0;
+}
+
+int ASoundDemo1::command24() {
+	playSoundAny(0x22FC);
+	playSoundAny(0x230C);
+	playSoundAny(0x231E);
+	return 0;
+}
+
+int ASoundDemo1::command25() {
+	playSoundAny(0x232A);
+	return 0;
+}
+
+int ASoundDemo1::command26() {
+	byte *pData = loadData(0x249E);
+	pData[5] = (byte)((command2627293032() >> 1) + 60);
+	_channels[8].load(pData);
+	return 0;
+}
+
+int ASoundDemo1::command27() {
+	byte *pData = loadData(0x2494);
+	pData[5] = (byte)((command2627293032() >> 1) + 78);
+	_channels[7].load(pData);
+	return 0;
+}
+
+int ASoundDemo1::command28() {
+	playSoundAny(0x233A);
+	return 0;
+}
+
+int ASoundDemo1::command29() {
+	byte *pData = loadData(0x2236);
+	byte v = (byte)((command2627293032() >> 1) + 76);
+	pData[7] = pData[13] = pData[21] = pData[27] = v;
+	isSoundActive(pData); // result unused, matches disassembly quirk
+	playSoundAny(0x2236);
+	return 0;
+}
+
+int ASoundDemo1::command30() {
+	byte *pData = loadData(0x2456);
+	pData[7] = (byte)((command2627293032() >> 1) + 72);
+	isSoundActive(pData); // result unused, matches disassembly quirk
+	playSoundAny(0x2456);
+	return 0;
+}
+
+int ASoundDemo1::command31() {
+	_channels[6].load(loadData(0x23B8));
+	_channels[7].load(loadData(0x2354));
+	return 0;
+}
+
+int ASoundDemo1::command32() {
+	byte *pData = loadData(0x2466);
+	byte base = (byte)command2627293032() >> 1;
+	byte v1 = base + 20;
+	byte v2 = base + 40;
+	pData[9] = pData[17] = pData[25] = pData[33] = v1;
+	pData[11] = pData[19] = pData[27] = pData[35] = v2;
+	isSoundActive(pData); // result unused, matches disassembly quirk
+	playSoundAny(0x2466);
+	return 0;
+}
+
+int ASoundDemo1::command33() {
+	playSoundAny(0x2362);
+	playSoundAny(0x236C);
+	return 0;
+}
+
+int ASoundDemo1::command34() {
+	byte *pData = loadData(0x2376);
+	byte v = (byte)((getRandomNumber() & 12) + 45);
+	pData[9] = v;
+	pData[16] = v + 36;
+	playSoundAny(0x2376);
+	return 0;
+}
+
+int ASoundDemo1::command35() {
+	playSoundAny(0x238A);
+	return 0;
+}
+
+int ASoundDemo1::command36() {
+	playSoundAny(0x23AE);
+	return 0;
+}
+
+int ASoundDemo1::command37() {
+	playSoundAny(0x23C6);
+	return 0;
+}
+
+int ASoundDemo1::command38() {
+	playSoundAny(0x23DA);
+	return 0;
+}
+
+int ASoundDemo1::command39() {
+	byte *pData1 = loadData(0x203E);
+	isSoundActive(pData1); // result unused, matches disassembly quirk
+	_channels[5].load(pData1);
+	_channels[6].load(loadData(0x20C0));
+	_channels[7].load(loadData(0x20DE));
+	_channels[8].load(loadData(0x2106));
+	return 0;
+}
+
+int ASoundDemo1::command40() {
+	playSoundAny(0x2396);
+	return 0;
+}
+
+void ASoundDemo1::command111213() {
+	byte *pData1 = loadData(0x18E8);
+	isSoundActive(pData1); // result unused, matches disassembly quirk
+	command1();
+	_channels[0].load(pData1);
+	_channels[1].load(loadData(0x1A80));
+	_channels[2].load(loadData(0x1B8A));
+	_channels[3].load(loadData(0x1BCC));
+}
+
+int ASoundDemo1::command2627293032() {
+	return (_commandParam > 0x40) ? _commandParam - 0x40 : _commandParam & 0xff00;
+}
+
+
 /*-----------------------------------------------------------------------*/
 
 const ASound2::CommandPtr ASound2::_commandList[44] = {
@@ -2217,6 +2481,147 @@ int ASound8::command37() {
 
 /*-----------------------------------------------------------------------*/
 
+const ASoundDemo9::CommandPtr ASoundDemo9::_commandList[39] = {
+	&ASoundDemo9::command0, &ASoundDemo9::command1, &ASoundDemo9::command2, &ASoundDemo9::command3,
+	&ASoundDemo9::command4, &ASoundDemo9::command5, &ASoundDemo9::command6, &ASoundDemo9::command7,
+	&ASoundDemo9::command8, &ASoundDemo9::nullCommand, &ASoundDemo9::nullCommand, &ASoundDemo9::command11,
+	&ASoundDemo9::nullCommand, &ASoundDemo9::nullCommand, &ASoundDemo9::command14, &ASoundDemo9::nullCommand,
+	&ASoundDemo9::nullCommand, &ASoundDemo9::command17, &ASoundDemo9::nullCommand, &ASoundDemo9::nullCommand,
+	&ASoundDemo9::command20, &ASoundDemo9::command21, &ASoundDemo9::command22, &ASoundDemo9::command23,
+	&ASoundDemo9::nullCommand, &ASoundDemo9::nullCommand, &ASoundDemo9::command26, &ASoundDemo9::command27,
+	&ASoundDemo9::command28, &ASoundDemo9::command29, &ASoundDemo9::command30, &ASoundDemo9::command31,
+	&ASoundDemo9::nullCommand, &ASoundDemo9::nullCommand, &ASoundDemo9::command34, &ASoundDemo9::command35,
+	&ASoundDemo9::command36, &ASoundDemo9::nullCommand, &ASoundDemo9::command38
+};
+
+ASoundDemo9::ASoundDemo9(Audio::Mixer *mixer) : ASound(mixer, "asound.009", 0x13f0, 0x3610) {
+	// Load sound samples
+	auto samplesStream = getDataStream(0x50);
+	for (int i = 0; i < 94; ++i)
+		_samples.push_back(AdlibSample(samplesStream));
+}
+
+int ASoundDemo9::command(int commandId, int param) {
+	if (commandId > 38)
+		return 0;
+
+	_commandParam = param;
+	_frameCounter = 0;
+	return (this->*_commandList[commandId])();
+}
+
+int ASoundDemo9::command11() {
+	playSoundAny(0x320E);
+	playSoundAny(0x32A2);
+	return 0;
+}
+
+int ASoundDemo9::command14() {
+	playSoundAny(0x31D2);
+	return 0;
+}
+
+int ASoundDemo9::command17() {
+	command29();
+	playSoundAny(0x3540);
+	return 0;
+}
+
+int ASoundDemo9::command20() {
+	byte *pData = loadData(0x315E);
+	int v = getRandomNumber();
+	v = (v & 0x10) | 0x4D;
+	pData[4] = (byte)(v & 0x7F);
+	playSoundAny(0x315E);
+	return 0;
+}
+
+int ASoundDemo9::command21() {
+	playSoundAny(0x3176);
+	return 0;
+}
+
+int ASoundDemo9::command22() {
+	playSoundAny(0x3186);
+	return 0;
+}
+
+int ASoundDemo9::command23() {
+	playSoundAny(0x3166);
+	return 0;
+}
+
+int ASoundDemo9::command26() {
+	_channels[6].load(loadData(0x3338));
+	_channels[7].load(loadData(0x33D4));
+	return 0;
+}
+
+int ASoundDemo9::command27() {
+	playSoundAny(0x3474);
+	return 0;
+}
+
+int ASoundDemo9::command28() {
+	_channels[8].load(loadData(0x31A6));
+	return 0;
+}
+
+int ASoundDemo9::command29() {
+	_channels[8].load(loadData(0x31B0));
+	return 0;
+}
+
+int ASoundDemo9::command30() {
+	playSoundAny(0x3196);
+	return 0;
+}
+
+int ASoundDemo9::command31() {
+	playSoundAny(0x31E8);
+	playSoundAny(0x31F6);
+	return 0;
+}
+
+int ASoundDemo9::command34() {
+	byte *pData1 = loadData(0x18FE);
+	isSoundActive(pData1); // result unused, matches disassembly quirk
+	command1();
+	_channels[0].load(pData1);
+	_channels[1].load(loadData(0x1B10));
+	_channels[2].load(loadData(0x1E4E));
+	_channels[3].load(loadData(0x21EE));
+	_channels[4].load(loadData(0x2804));
+	_channels[5].load(loadData(0x2C72));
+	return 0;
+}
+
+int ASoundDemo9::command35() {
+	playSoundAny(0x3500);
+	return 0;
+}
+
+int ASoundDemo9::command36() {
+	playSoundAny(0x31BA);
+	playSoundAny(0x31C4);
+	return 0;
+}
+
+int ASoundDemo9::command38() {
+	byte *pData1 = loadData(0x307A);
+	isSoundActive(pData1); // result unused, matches disassembly quirk
+	command1();
+	_channels[0].load(pData1);
+	_channels[1].load(loadData(0x3090));
+	_channels[2].load(loadData(0x30C8));
+	_channels[3].load(loadData(0x3102));
+	_channels[4].load(loadData(0x313A));
+	_channels[5].load(loadData(0x314A));
+	return 0;
+}
+
+/*-----------------------------------------------------------------------*/
+
 const ASound9::CommandPtr ASound9::_commandList[52] = {
 	&ASound9::command0, &ASound9::command1, &ASound9::command2, &ASound9::command3,
 	&ASound9::command4, &ASound9::command5, &ASound9::command6, &ASound9::command7,
diff --git a/engines/mads/nebular/sound/asound_nebular.h b/engines/mads/nebular/sound/asound_nebular.h
index 99d54d09ace..f6d94217c2f 100644
--- a/engines/mads/nebular/sound/asound_nebular.h
+++ b/engines/mads/nebular/sound/asound_nebular.h
@@ -76,6 +76,53 @@ public:
 	int command(int commandId, int param) override;
 };
 
+class ASoundDemo1 : public ASound {
+private:
+	typedef int (ASoundDemo1:: *CommandPtr)();
+	static const CommandPtr _commandList[41];
+	bool _cmd23Toggle;
+
+	int command9();
+	int command10();
+	int command11();
+	int command12();
+	int command13();
+	int command14();
+	int command15();
+	int command16();
+	int command17();
+	int command18();
+	int command19();
+	int command20();
+	int command21();
+	int command22();
+	int command23();
+	int command24();
+	int command25();
+	int command26();
+	int command27();
+	int command28();
+	int command29();
+	int command30();
+	int command31();
+	int command32();
+	int command33();
+	int command34();
+	int command35();
+	int command36();
+	int command37();
+	int command38();
+	int command39();
+	int command40();
+
+	void command111213();
+	int command2627293032();
+public:
+	ASoundDemo1(Audio::Mixer *mixer);
+
+	int command(int commandId, int param) override;
+};
+
 class ASound2 : public ASound {
 private:
 	byte _command12Param;
@@ -376,6 +423,34 @@ public:
 	int command(int commandId, int param) override;
 };
 
+class ASoundDemo9 : public ASound {
+private:
+	typedef int (ASoundDemo9:: *CommandPtr)();
+	static const CommandPtr _commandList[39];
+
+	int command11();
+	int command14();
+	int command17();
+	int command20();
+	int command21();
+	int command22();
+	int command23();
+	int command26();
+	int command27();
+	int command28();
+	int command29();
+	int command30();
+	int command31();
+	int command34();
+	int command35();
+	int command36();
+	int command38();
+public:
+	ASoundDemo9(Audio::Mixer *mixer);
+
+	int command(int commandId, int param) override;
+};
+
 class ASound9 : public ASound {
 private:
 	/**
diff --git a/engines/mads/nebular/sound/sound.cpp b/engines/mads/nebular/sound/sound.cpp
index d30cad11815..7d1f9fee054 100644
--- a/engines/mads/nebular/sound/sound.cpp
+++ b/engines/mads/nebular/sound/sound.cpp
@@ -29,6 +29,9 @@ namespace RexNebular {
 namespace Sound {
 
 void RexSoundManager::validate() {
+	if (_isDemo)
+		_driverType = SOUND_ADLIB;
+
 	switch (_driverType) {
 	case SOUND_MT32:
 		RSound::validate();
@@ -39,7 +42,7 @@ void RexSoundManager::validate() {
 		break;
 
 	default:
-		ASound::validate();
+		ASound::validate(_isDemo);
 		break;
 	}
 }
@@ -47,6 +50,15 @@ void RexSoundManager::validate() {
 void RexSoundManager::loadDriver(int sectionNumber) {
 	removeDriver();
 
+	if (_isDemo) {
+		assert(sectionNumber == 1 || sectionNumber == 9);
+		if (sectionNumber == 1)
+			_driver = new ASoundDemo1(_mixer);
+		else
+			_driver = new ASoundDemo9(_mixer);
+		return;
+	}
+
 	switch (_driverType) {
 	case SOUND_MT32:
 		// Roland MT32 drivers
diff --git a/engines/mads/nebular/sound/sound.h b/engines/mads/nebular/sound/sound.h
index 5678bab403d..2854e29b7cf 100644
--- a/engines/mads/nebular/sound/sound.h
+++ b/engines/mads/nebular/sound/sound.h
@@ -29,6 +29,9 @@ namespace RexNebular {
 namespace Sound {
 
 class RexSoundManager : public SoundManager {
+private:
+	bool _isDemo;
+
 protected:
 	/**
 	 * Load the particular section sound handler
@@ -37,7 +40,8 @@ protected:
 	void loadDriver(int sectionNum) override;
 
 public:
-	RexSoundManager(Audio::Mixer *mixer, bool &soundFlag) : SoundManager(mixer, soundFlag) {
+	RexSoundManager(Audio::Mixer *mixer, bool &soundFlag, bool isDemo) :
+		SoundManager(mixer, soundFlag), _isDemo(isDemo) {
 	}
 	~RexSoundManager() override {
 	}


Commit: bec794b52cbf9f5f1303e77f476f756f8106632c
    https://github.com/scummvm/scummvm/commit/bec794b52cbf9f5f1303e77f476f756f8106632c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-08-02T16:38:50+10:00

Commit Message:
MADS: NEBULAR: Implement Rex Demo DCL decoder

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

Changed paths:
  A engines/mads/core/pack_dcl.cpp
  A engines/mads/core/pack_dcl.h
    engines/mads/core/text.cpp
    engines/mads/module.mk


diff --git a/engines/mads/core/pack_dcl.cpp b/engines/mads/core/pack_dcl.cpp
new file mode 100644
index 00000000000..9e4a22c2f0e
--- /dev/null
+++ b/engines/mads/core/pack_dcl.cpp
@@ -0,0 +1,286 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/textconsole.h"
+#include "mads/core/pack_dcl.h"
+
+namespace MADS {
+
+/*
+ * Format notes (from blast.c):
+ *
+ * - First byte is 0 if literals are uncoded or 1 if they are coded. Second
+ *   byte is 4, 5, or 6 for the number of extra bits in the distance code.
+ *   This is the base-2 logarithm of the dictionary size minus six.
+ *
+ * - Compressed data is a combination of literals and length/distance pairs
+ *   terminated by an end code. Literals are either Huffman coded or
+ *   uncoded bytes. A length/distance pair is a coded length followed by a
+ *   coded distance to represent a string that occurs earlier in the
+ *   uncompressed data that occurs again at the current location.
+ *
+ * - A bit preceding a literal or length/distance pair indicates which comes
+ *   next: 0 for literals, 1 for length/distance.
+ *
+ * - If literals are uncoded, the next eight bits are the literal, in normal
+ *   bit order (no bit-reversal). Length and distance extra bits are also
+ *   read in normal bit order.
+ *
+ * - Bits are packed from the least significant bit to the most significant
+ *   bit of each byte.
+ *
+ * - Huffman codes are bit-reversed relative to a simple integer ordering of
+ *   codes of the same length, so decode() inverts each bit as it reads it
+ *   to recover a naturally-ordered code value for comparison.
+ */
+
+#define DCL_MAXBITS 13
+#define DCL_MAXWIN  4096
+
+struct DclHuffman {
+	short count[DCL_MAXBITS + 1]; /* number of symbols of each length */
+	short *symbol;                 /* canonically ordered symbols */
+};
+
+// Bit lengths of literal codes 0..255, compactly encoded: each byte's low
+// nibble is a code length and high nibble is (repeat count - 1).
+static const byte kLitLenCompact[] = {
+	11, 124, 8, 7, 28, 7, 188, 13, 76, 4, 10, 8, 12, 10, 12, 10, 8, 23, 8,
+	9, 7, 6, 7, 8, 7, 6, 55, 8, 23, 24, 12, 11, 7, 9, 11, 12, 6, 7, 22, 5,
+	7, 24, 6, 11, 9, 6, 7, 22, 7, 11, 38, 7, 9, 8, 25, 11, 8, 11, 9, 12,
+	8, 12, 5, 38, 5, 38, 5, 11, 7, 5, 6, 21, 6, 10, 53, 8, 7, 24, 10, 27,
+	44, 253, 253, 253, 252, 252, 252, 13, 12, 45, 12, 45, 12, 61, 12, 45,
+	44, 173
+};
+
+// Bit lengths of length codes 0..15 (same compact encoding).
+static const byte kLenLenCompact[] = {2, 35, 36, 53, 38, 23};
+
+// Bit lengths of distance codes 0..63 (same compact encoding).
+static const byte kDistLenCompact[] = {2, 20, 53, 230, 247, 151, 248};
+
+// Base value and extra-bit count for each of the 16 length codes.
+static const short kLenBase[16] = {
+	3, 2, 4, 5, 6, 7, 8, 9, 10, 12, 16, 24, 40, 72, 136, 264
+};
+static const byte kLenExtra[16] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8
+};
+
+struct DclState {
+	Common::SeekableReadStream *src;
+
+	uint32 bitbuf;
+	int bitcnt;
+	bool inputError;
+
+	// Sliding window: doubles as LZ77 history and the last DCL_MAXWIN
+	// bytes of output. windowPos wraps modulo DCL_MAXWIN, which is
+	// equivalent to blast.c's own block-recycling scheme since distances
+	// never exceed the dictionary size (max 4096).
+	byte window[DCL_MAXWIN];
+	int windowPos;
+	bool first; // true until a full window's worth of output has been produced
+
+	byte *dest;
+	long destSize;
+	long destPos;
+};
+
+/**
+ * Reads need bits from the input stream, least-significant-bit first.
+ * Sets s.inputError and returns 0 if the stream runs out of data.
+ */
+static int dclBits(DclState &s, int need) {
+	uint32 val = s.bitbuf;
+	while (s.bitcnt < need) {
+		byte b;
+		if (s.src->eos() || s.src->read(&b, 1) != 1) {
+			s.inputError = true;
+			return 0;
+		}
+		val |= (uint32)b << s.bitcnt;
+		s.bitcnt += 8;
+	}
+	s.bitbuf = val >> need;
+	s.bitcnt -= need;
+	return (int)(val & ((1u << need) - 1));
+}
+
+/**
+ * Bit-reversed canonical Huffman decode; see the format notes above.
+ */
+static int dclDecode(DclState &s, const DclHuffman &h) {
+	int code = 0, first = 0, index = 0, len = 1;
+	const short *next = h.count + 1;
+
+	for (;;) {
+		int bit = dclBits(s, 1);
+		if (s.inputError)
+			return 0;
+
+		code |= bit ^ 1; // invert to recover natural ordering
+
+		int count = *next++;
+		if (code < first + count)
+			return h.symbol[index + (code - first)];
+
+		index += count;
+		first += count;
+		first <<= 1;
+		code <<= 1;
+		len++;
+
+		if (len > DCL_MAXBITS) {
+			s.inputError = true;
+			return 0;
+		}
+	}
+}
+
+/**
+ * Expands a compact repeat-count/length byte list into a canonical Huffman
+ * decode table (count[] and symbol[]).
+ *
+ * @param h         Huffman table to fill in (h.symbol must already point at
+ *                  storage large enough for the expanded symbol count).
+ * @param rep       Compact repeat-count/length bytes.
+ * @param repLen    Number of bytes in rep.
+ * @param scratch   Scratch buffer, must hold at least as many entries as the
+ *                  expanded symbol count (256 is always sufficient).
+ */
+static void dclConstruct(DclHuffman &h, const byte *rep, int repLen, short *scratch) {
+	int symbol = 0;
+	for (int i = 0; i < repLen; ++i) {
+		int len = rep[i] & 15;
+		int left = (rep[i] >> 4) + 1;
+		while (left-- > 0)
+			scratch[symbol++] = len;
+	}
+	int n = symbol;
+
+	for (int len = 0; len <= DCL_MAXBITS; ++len)
+		h.count[len] = 0;
+	for (int i = 0; i < n; ++i)
+		h.count[scratch[i]]++;
+
+	if (h.count[0] == n)
+		return; // empty code; never referenced by a well-formed stream
+
+	short offs[DCL_MAXBITS + 2];
+	offs[1] = 0;
+	for (int len = 1; len < DCL_MAXBITS; ++len)
+		offs[len + 1] = offs[len] + h.count[len];
+
+	for (int i = 0; i < n; ++i) {
+		if (scratch[i] != 0)
+			h.symbol[offs[scratch[i]]++] = i;
+	}
+}
+
+bool pack_dcl_explode(Common::SeekableReadStream *src, byte *dest, long destSize) {
+	DclState s;
+	s.src = src;
+	s.bitbuf = 0;
+	s.bitcnt = 0;
+	s.inputError = false;
+	memset(s.window, 0, sizeof(s.window));
+	s.windowPos = 0;
+	s.first = true;
+	s.dest = dest;
+	s.destSize = destSize;
+	s.destPos = 0;
+
+	short litSym[256], lenSym[16], distSym[64];
+	DclHuffman litCode, lenCode, distCode;
+	litCode.symbol = litSym;
+	lenCode.symbol = lenSym;
+	distCode.symbol = distSym;
+
+	short scratch[256];
+	dclConstruct(litCode, kLitLenCompact, sizeof(kLitLenCompact), scratch);
+	dclConstruct(lenCode, kLenLenCompact, sizeof(kLenLenCompact), scratch);
+	dclConstruct(distCode, kDistLenCompact, sizeof(kDistLenCompact), scratch);
+
+	int lit = dclBits(s, 8);
+	if (s.inputError || lit > 1)
+		return false;
+	int dictBits = dclBits(s, 8);
+	if (s.inputError || dictBits < 4 || dictBits > 6)
+		return false;
+
+	while (s.destPos < s.destSize) {
+		int flag = dclBits(s, 1);
+		if (s.inputError)
+			return false;
+
+		if (flag) {
+			// Length/distance pair.
+			int symbol = dclDecode(s, lenCode);
+			if (s.inputError)
+				return false;
+			int len = kLenBase[symbol] + dclBits(s, kLenExtra[symbol]);
+			if (s.inputError)
+				return false;
+			if (len == 519)
+				break; // end code
+
+			int distBits = (len == 2) ? 2 : dictBits;
+			int distSymbol = dclDecode(s, distCode);
+			if (s.inputError)
+				return false;
+			uint32 dist = ((uint32)distSymbol << distBits) + dclBits(s, distBits);
+			if (s.inputError)
+				return false;
+			dist++;
+
+			if (s.first && (long)dist > s.windowPos)
+				return false; // distance points before the start of output
+
+			while (len-- > 0) {
+				byte b = s.window[(s.windowPos - (int)dist) & (DCL_MAXWIN - 1)];
+				s.window[s.windowPos] = b;
+				s.windowPos = (s.windowPos + 1) & (DCL_MAXWIN - 1);
+				if (s.windowPos == 0)
+					s.first = false;
+				if (s.destPos < s.destSize)
+					s.dest[s.destPos++] = b;
+			}
+		} else {
+			// Literal.
+			int symbol = lit ? dclDecode(s, litCode) : dclBits(s, 8);
+			if (s.inputError)
+				return false;
+
+			byte b = (byte)symbol;
+			s.window[s.windowPos] = b;
+			s.windowPos = (s.windowPos + 1) & (DCL_MAXWIN - 1);
+			if (s.windowPos == 0)
+				s.first = false;
+			if (s.destPos < s.destSize)
+				s.dest[s.destPos++] = b;
+		}
+	}
+
+	return s.destPos == s.destSize;
+}
+
+} // namespace MADS
diff --git a/engines/mads/core/pack_dcl.h b/engines/mads/core/pack_dcl.h
new file mode 100644
index 00000000000..f60619a0108
--- /dev/null
+++ b/engines/mads/core/pack_dcl.h
@@ -0,0 +1,61 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_CORE_PACK_DCL_H
+#define MADS_CORE_PACK_DCL_H
+
+#include "common/stream.h"
+#include "mads/core/general.h"
+
+namespace MADS {
+
+/*
+ * pack_dcl.cpp -- genuine PKWare Data Compression Library ("explode")
+ * decompressor.
+ *
+ * MADSPACK 1.0 (used by early builds such as the Rex Nebular demo) used
+ * this real PKWare algorithm; MADSPACK 2.0 replaced it with MicroProse's
+ * own "pFAB" compressor (see pfab.cpp). The two formats are unrelated
+ * despite the shared "MADSPACK" branding, and this decoder is deliberately
+ * kept independent of the shared pack_data()/PackList machinery in
+ * pack.cpp (which every other MADS game relies on), so that adding support
+ * for this older format carries no risk of regressing anything else.
+ *
+ * Ported from the public-domain reference implementation "blast.c" by
+ * Mark Adler (zlib contrib/blast), which documents the format originally
+ * described by Ben Rudiak-Gould.
+ */
+
+/**
+ * Decompresses a PKWare DCL "explode" stream.
+ *
+ * @param src       Compressed data stream, positioned at the start of the block.
+ *                  Only as many bytes as the format actually needs are consumed;
+ *                  the stream is not required to end where the block does.
+ * @param dest      Destination buffer; must be at least destSize bytes.
+ * @param destSize  Expected number of decompressed bytes.
+ * @return true if exactly destSize bytes were decompressed successfully.
+ */
+extern bool pack_dcl_explode(Common::SeekableReadStream *src, byte *dest, long destSize);
+
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/core/text.cpp b/engines/mads/core/text.cpp
index a348d8043ef..e9419a74da3 100644
--- a/engines/mads/core/text.cpp
+++ b/engines/mads/core/text.cpp
@@ -38,6 +38,7 @@
 #include "mads/core/object.h"
 #include "mads/core/text.h"
 #include "mads/core/global.h"
+#include "mads/core/pack_dcl.h"
 #include "mads/mads.h"
 
 namespace MADS {
@@ -134,12 +135,23 @@ TextPtr text_load(long id) {
 
 	fileio_setpos(handle, target_offset);
 
-	pack_strategy = PACK_PFAB;
-	if (pack_data(PACK_EXPLODE, dir.length,
-		FROM_DISK, handle,
-		TO_MEMORY, &result->text[0]) != (int)dir.length) {
-		mem_free(result);
-		result = NULL;
+	// The Rex Nebular demo's messages.dat predates MADSPACK 2.0's switch to
+	// the pFAB compressor and still uses genuine PKWare DCL compression
+	// (MADSPACK 1.0). Route it to a dedicated decoder rather than risking
+	// the shared pack_data() path used by every other game.
+	if (g_engine->getGameID() == GType_RexNebular && g_engine->isDemo()) {
+		if (!pack_dcl_explode(handle, (byte *)&result->text[0], dir.length)) {
+			mem_free(result);
+			result = NULL;
+		}
+	} else {
+		pack_strategy = PACK_PFAB;
+		if (pack_data(PACK_EXPLODE, dir.length,
+			FROM_DISK, handle,
+			TO_MEMORY, &result->text[0]) != (int)dir.length) {
+			mem_free(result);
+			result = NULL;
+		}
 	}
 
 done:
diff --git a/engines/mads/module.mk b/engines/mads/module.mk
index 9872184e5b3..b5c1f97d97a 100644
--- a/engines/mads/module.mk
+++ b/engines/mads/module.mk
@@ -45,6 +45,7 @@ MODULE_OBJS := \
 	core/mps_installer.o \
 	core/object.o \
 	core/pack.o \
+	core/pack_dcl.o \
 	core/pal.o \
 	core/pfab.o \
 	core/player.o \


Commit: 24813e60febcab5da73f870f035b7675e88b1d39
    https://github.com/scummvm/scummvm/commit/24813e60febcab5da73f870f035b7675e88b1d39
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-08-02T16:38:50+10:00

Commit Message:
MADS: NEBULAR: Demo specific restrictions

Changed paths:
    engines/mads/nebular/rooms/room102.cpp
    engines/mads/nebular/rooms/room106.cpp
    engines/mads/nebular/rooms/room107.cpp


diff --git a/engines/mads/nebular/rooms/room102.cpp b/engines/mads/nebular/rooms/room102.cpp
index 3e6f5ae6356..9173d61e384 100644
--- a/engines/mads/nebular/rooms/room102.cpp
+++ b/engines/mads/nebular/rooms/room102.cpp
@@ -86,7 +86,12 @@ static void room_102_init() {
 		kernel_seq_range(g_sequence_ids[8], -2, -2);
 	}
 
-	if (previous_room == 101) {
+	if (g_engine->isDemo() && previous_room == KERNEL_STARTING_GAME) {
+		player.x = 169;
+		player.y = 115;
+		player.facing = FACING_SOUTH;
+		inter_move_object(OBJ_REBREATHER, PLAYER);
+	} else if (previous_room == 101) {
 		player.x = 229;
 		player.y = 109;
 		player.commands_allowed = false;
@@ -217,13 +222,13 @@ static void room_102_pre_parser() {
 }
 
 static void room_102_parser() {
+	bool justOpenedFl = false;
+
 	if (player.look_around) {
 		text_show(10234);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
-	bool justOpenedFl = false;
 	if (player_said_1(refrigerator) && !local._fridgeOpenedFl) {
 		switch (kernel.trigger) {
 		case 0:
@@ -236,8 +241,7 @@ static void room_102_parser() {
 			}
 			player.commands_allowed = false;
 			g_engine->_soundManager->command(20, 0);
-			player.command_ready = false;
-			return;
+			goto handled;
 
 		case 1:
 			g_sequence_ids[7] = kernel_seq_forward(g_sprite_ids[7], false, 6, 0, 0, 0);
@@ -249,8 +253,7 @@ static void room_102_parser() {
 			else
 				delay = 48;
 			kernel_timing_trigger(delay, 2);
-			player.command_ready = false;
-			return;
+			goto handled;
 
 		case 2:
 			local._fridgeOpenedFl = true;
@@ -274,8 +277,7 @@ static void room_102_parser() {
 			text_show(10229);
 
 		local._fridgeFirstOpenFl = false;
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(walkto, refrigerator) && justOpenedFl) {
@@ -288,23 +290,26 @@ static void room_102_parser() {
 		kernel_message_add(curQuote, 210, 60, 0x1110, 120, 73, 0);
 		kernel_message_add(quote_string(kernel.quotes, 64), 214 + width, 60, 0x1110, 120, 73, 0);
 		local._activeMsgFl = true;
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(close, refrigerator)) {
 		text_show(10213);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(take, refrigerator)) {
 		text_show(8);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(walk_through, door)) {
+		if (g_engine->isDemo()) {
+			// Not available in demo
+			text_show(99);
+			goto handled;
+		}
+
 		switch (kernel.trigger) {
 		case 0:
 			g_sequence_ids[6] = kernel_seq_forward(g_sprite_ids[6], false, 6, 0, 0, 1);
@@ -320,99 +325,87 @@ static void room_102_parser() {
 		default:
 			break;
 		}
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(walkto, engineering_section)) {
-		new_room = 103;
-		player.command_ready = false;
-		return;
+		if (g_engine->isDemo())
+			// Not available in demo
+			text_show(99);
+		else
+			new_room = 103;
+		goto handled;
 	}
 
 	if (player_said_2(walkto, poster) || player_said_2(look, poster) || player_said_2(walkto, binoculars)) {
 		addRandomMessage();
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(look, weight_machine)) {
 		text_show(10212);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(look, engineering_section)) {
 		text_show(10205);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(look, door)) {
 		text_show(10204);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(stare_at, ceiling) || player_said_2(look, ceiling)) {
 		text_show(10203);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(stare_at, overhead_lamp) || player_said_2(look, overhead_lamp)) {
 		text_show(10202);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(look, robo_kitchen)) {
 		text_show(10215);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_3(put, burger, robo_kitchen) && player_has(OBJ_BURGER)) {
 		text_show(10216);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(put, refrigerator) && player_has(object_named(player2.words[1]))) {
 		text_show(10217);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_3(put, dead_fish, robo_kitchen) || player_said_3(put, stuffed_fish, robo_kitchen)) {
 		text_show(10230);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(open, robo_kitchen)) {
 		text_show(10218);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(look, closet)) {
 		text_show(10219);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if ((player_said_1(ladder) || player_said_1(hatchway)) && (player_said_1(look) || player_said_1(climb_up) || player_said_1(climb_through))) {
 		if (player_has(OBJ_REBREATHER)) {
 			if (!player_said_1(climb_up) && !player_said_1(climb_through)) {
 				text_show(10231);
-				player.command_ready = false;
-				return;
+				goto handled;
 			}
 		} else if (player_said_1(look) || (game.difficulty != DIFFICULTY_HARD)) {
 			text_show(10222);
-			player.command_ready = false;
-			return;
+			goto handled;
 		}
 	}
 
@@ -442,7 +435,7 @@ static void room_102_parser() {
 			break;
 
 		case 6:
-			if (player_has(OBJ_REBREATHER) && !player_has_been_in_room(106))
+			if (player_has(OBJ_REBREATHER) && !player_has_been_in_room(106) && !g_engine->isDemo())
 				text_show(10237);
 			new_room = 106;
 			break;
@@ -450,58 +443,49 @@ static void room_102_parser() {
 		default:
 			break;
 		}
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(look, power_status_panel)) {
 		text_show(10226);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(look, window) || player_said_2(look_through, window)) {
 		text_show(10227);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(look, doorway) || player_said_2(walkto, doorway)) {
 		text_show(10228);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(look, drawer) || ((player_said_2(close, drawer) || player_said_2(push, drawer)) && !local._drawerDescrFl)) {
 		text_show(10220);
 		local._drawerDescrFl = true;
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(close, drawer) || player_said_2(push, drawer)) {
 		text_show(10221);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(open, drawer)) {
 		text_show(10236);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(look, chair) || (player_said_2(sit_in, chair) && !local._chairDescrFl)) {
 		local._chairDescrFl = true;
 		text_show(10210);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(sit_in, chair)) {
 		text_show(10211);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(look, medicine_cabinet)) {
@@ -510,8 +494,7 @@ static void room_102_parser() {
 		else
 			text_show(10206);
 
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(close, medicine_cabinet) && global[kMedicineCabinetOpen]) {
@@ -537,8 +520,7 @@ static void room_102_parser() {
 		default:
 			break;
 		}
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(open, medicine_cabinet) && !global[kMedicineCabinetOpen]) {
@@ -570,8 +552,7 @@ static void room_102_parser() {
 		default:
 			break;
 		}
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(take, binoculars) && object_is_here(OBJ_BINOCULARS)) {
@@ -597,8 +578,7 @@ static void room_102_parser() {
 		default:
 			break;
 		}
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(take, burger) && object_is_here(OBJ_BURGER)) {
@@ -611,38 +591,40 @@ static void room_102_parser() {
 			player.walker_visible = true;
 			player.commands_allowed = true;
 		}
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(take, poster)) {
 		text_show(10224);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if ((player_said_1(push) || player_said_1(pull)) && player_said_1(weight_machine)) {
 		text_show(10225);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(look, floor)) {
 		text_show(10232);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(look, binoculars) && !player_has(OBJ_BINOCULARS)) {
 		text_show(10233);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_said_2(look, burger) && (player.main_object_source == STROKE_INTERFACE)) {
 		text_show(801);
-		player.command_ready = false;
+		goto handled;
 	}
+
+	goto done;
+
+handled:
+	player.command_ready = false;
+done:
+	;
 }
 
 static void room_102_error() {
diff --git a/engines/mads/nebular/rooms/room106.cpp b/engines/mads/nebular/rooms/room106.cpp
index c519b29faeb..59bb1034a76 100644
--- a/engines/mads/nebular/rooms/room106.cpp
+++ b/engines/mads/nebular/rooms/room106.cpp
@@ -95,6 +95,7 @@ static void room_106_init() {
 }
 
 static void room_106_daemon() {
+	if (kernel.trigger != 0) warning("%d", kernel.trigger);
 	if (kernel.trigger == 70) {
 		g_sequence_ids[0] = kernel_seq_forward(g_sprite_ids[0], false, 6, 0, 0, 0);
 		kernel_seq_range(g_sequence_ids[0], -2, -2);
@@ -122,6 +123,10 @@ static void room_106_daemon() {
 			player.prepare_walk_facing = FACING_SOUTHWEST;
 			local._firstEmergingFl = true;
 			kernel_run_animation(kernel_full_name(106, 'B', -1, "", KERNEL_AA), 80);
+
+			// FIXME: trigger 80 isn't happening for demo
+			if (g_engine->isDemo())
+				kernel_timing_trigger(100, 87);
 		}
 	}
 
@@ -189,9 +194,14 @@ static void room_106_daemon() {
 
 static void room_106_pre_parser() {
 	if (player_said_2(swim_towards, sea_cliff) || player_said_2(swim_towards, seaweed_bank)) {
-		player.commands_allowed = false;
-		series_list[player.series_base + 1]->walker->velocity = 24;
-		player.walk_off_edge_to_room = 104;
+		if (g_engine->isDemo()) {
+			text_show(99);
+			player.command_ready = false;
+		} else {
+			player.commands_allowed = false;
+			series_list[player.series_base + 1]->walker->velocity = 24;
+			player.walk_off_edge_to_room = 104;
+		}
 	}
 
 	if (player_said_2(swim_towards, open_area_to_east))
diff --git a/engines/mads/nebular/rooms/room107.cpp b/engines/mads/nebular/rooms/room107.cpp
index c67333a2f59..91a2b449d8b 100644
--- a/engines/mads/nebular/rooms/room107.cpp
+++ b/engines/mads/nebular/rooms/room107.cpp
@@ -102,8 +102,14 @@ static void room_107_pre_parser() {
 	if (player_said_2(swim_towards, open_area_to_west))
 		player.walk_off_edge_to_room = 106;
 
-	if (player_said_2(swim_towards, open_area_to_south))
-		player.walk_off_edge_to_room = 108;
+	if (player_said_2(swim_towards, open_area_to_south)) {
+		if (g_engine->isDemo()) {
+			text_show(99);
+			player.command_ready = false;
+		} else {
+			player.walk_off_edge_to_room = 108;
+		}
+	}
 }
 
 static void room_107_parser() {
@@ -121,7 +127,10 @@ static void room_107_parser() {
 			object_examine(OBJ_DEAD_FISH, 802, 0);
 		}
 	} else if (player_said_2(swim_towards, northern_sea_cliff))
-		new_room = 105;
+		if (g_engine->isDemo())
+			text_show(99);
+		else
+			new_room = 105;
 	else if (player_said_2(look, northern_sea_cliff))
 		text_show(10701);
 	else if (player_said_2(look, dead_fish) && (player.main_object_source == STROKE_INTERFACE))




More information about the Scummvm-git-logs mailing list