[Scummvm-git-logs] scummvm master -> df3c32da7b437b8074362fe3d10c88671e0e86f8

sev- noreply at scummvm.org
Mon Jul 21 10:43:32 UTC 2025


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

Summary:
47dc844da8 DIRECTOR: XOBJ: Add SmallUtil
759e9016c7 DIRECTOR: Silence noisy warning message
762f60e565 DIRECTOR: Update Flipper detection entry
1af84d2738 DIRECTOR: Make DirectorSound use int for soundChannel everywhere
8bbfda114d DIRECTOR: Mark channel dirty if the blendAmount changes
3b1b9a5e0a DIRECTOR: XOBJ: Add basic VoyagerXSound playback
3ec095a40b DIRECTOR: XOBJ: Add file read/write commands to Ednox
ef27bd06bb DIRECTOR: XOBJ: Implement more of VoyagerXSound
b0af6d4218 DIRECTOR: XOBJ: Add VMPresent stub
df3c32da7b DIRECTOR: Update detection entry for Puppet Motel


Commit: 47dc844da8fa611eb195250258b43920b0b3145a
    https://github.com/scummvm/scummvm/commit/47dc844da8fa611eb195250258b43920b0b3145a
Author: Scott Percival (code at moral.net.au)
Date: 2025-07-21T12:43:25+02:00

Commit Message:
DIRECTOR: XOBJ: Add SmallUtil

Changed paths:
  A engines/director/lingo/xlibs/smallutil.cpp
  A engines/director/lingo/xlibs/smallutil.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 1e43eb9fdea..20c6accfd4d 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -116,6 +116,7 @@
 #include "director/lingo/xlibs/registercomponent.h"
 #include "director/lingo/xlibs/remixxcmd.h"
 #include "director/lingo/xlibs/serialportxobj.h"
+#include "director/lingo/xlibs/smallutil.h"
 #include "director/lingo/xlibs/soundjam.h"
 #include "director/lingo/xlibs/spacemgr.h"
 #include "director/lingo/xlibs/stagetc.h"
@@ -316,6 +317,7 @@ static const struct XLibProto {
 	XLIBDEF(RolloverToolkitXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(ScrnUtilXtra,		kXtraObj,		500),	// D5
 	XLIBDEF(SerialPortXObj,		kXObj,			200),	// D2
+	XLIBDEF(SmallUtilXObj,			kXObj,					400),	// D4
 	XLIBDEF(SoundJam,			kXObj,			400),	// D4
 	XLIBDEF(SpaceMgr,			kXObj,			400),	// D4
 	XLIBDEF(StageTCXObj,		kXObj,			400),	// D4
diff --git a/engines/director/lingo/xlibs/smallutil.cpp b/engines/director/lingo/xlibs/smallutil.cpp
new file mode 100644
index 00000000000..f10b0727f96
--- /dev/null
+++ b/engines/director/lingo/xlibs/smallutil.cpp
@@ -0,0 +1,112 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xlibs/smallutil.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * flipper
+ *
+ **************************************************/
+
+/*
+-- SmallUtil, xobject  08Aug95 PF
+-- ©1995 - Interactive Multimedia Unit
+-- Central Queensland University
+-- CodeWarrior version, 16May95 EA
+-- xobject framework, 29nov94 JT
+--
+I    mNew                           -- Create an instance of SmallUtil
+X    mDispose                       -- Destroy the instance of SmallUtil
+I    mQTVersion                     -- Return QT Version
+S    mQTVersionasText               -- Return QT Version as Text "."delimited
+I    mSMVersion                     -- Return Sound Manager Version
+S    mSMVersionasText               -- Return Sound Manager Version "." delimited
+S    mFontList                      -- Return List of FOND's
+S    mVolumeList                    -- Return List of Mounted Volumes
+ */
+
+namespace Director {
+
+const char *SmallUtilXObj::xlibName = "SmallUtil";
+const XlibFileDesc SmallUtilXObj::fileNames[] = {
+	{ "SmallUtil",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+	{ "new",				SmallUtilXObj::m_new,		 0, 0,	400 },
+	{ "dispose",				SmallUtilXObj::m_dispose,		 0, 0,	400 },
+	{ "qTVersion",				SmallUtilXObj::m_qTVersion,		 0, 0,	400 },
+	{ "qTVersionasText",				SmallUtilXObj::m_qTVersionasText,		 0, 0,	400 },
+	{ "sMVersion",				SmallUtilXObj::m_sMVersion,		 0, 0,	400 },
+	{ "sMVersionasText",				SmallUtilXObj::m_sMVersionasText,		 0, 0,	400 },
+	{ "fontList",				SmallUtilXObj::m_fontList,		 0, 0,	400 },
+	{ "volumeList",				SmallUtilXObj::m_volumeList,		 0, 0,	400 },
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+SmallUtilXObject::SmallUtilXObject(ObjectType ObjectType) :Object<SmallUtilXObject>("SmallUtil") {
+	_objType = ObjectType;
+}
+
+void SmallUtilXObj::open(ObjectType type, const Common::Path &path) {
+    SmallUtilXObject::initMethods(xlibMethods);
+    SmallUtilXObject *xobj = new SmallUtilXObject(type);
+    if (type == kXtraObj)
+        g_lingo->_openXtras.push_back(xlibName);
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void SmallUtilXObj::close(ObjectType type) {
+    SmallUtilXObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void SmallUtilXObj::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("SmallUtilXObj::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+XOBJSTUBNR(SmallUtilXObj::m_dispose)
+XOBJSTUB(SmallUtilXObj::m_qTVersion, 0)
+XOBJSTUB(SmallUtilXObj::m_qTVersionasText, "2.5")
+XOBJSTUB(SmallUtilXObj::m_sMVersion, 0)
+XOBJSTUB(SmallUtilXObj::m_sMVersionasText, "3.2")
+XOBJSTUB(SmallUtilXObj::m_fontList, "")
+XOBJSTUB(SmallUtilXObj::m_volumeList, "")
+
+}
diff --git a/engines/director/lingo/xlibs/smallutil.h b/engines/director/lingo/xlibs/smallutil.h
new file mode 100644
index 00000000000..55ff9ef1001
--- /dev/null
+++ b/engines/director/lingo/xlibs/smallutil.h
@@ -0,0 +1,53 @@
+/* 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 DIRECTOR_LINGO_XLIBS_SMALLUTIL_H
+#define DIRECTOR_LINGO_XLIBS_SMALLUTIL_H
+
+namespace Director {
+
+class SmallUtilXObject : public Object<SmallUtilXObject> {
+public:
+	SmallUtilXObject(ObjectType objType);
+};
+
+namespace SmallUtilXObj {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_dispose(int nargs);
+void m_qTVersion(int nargs);
+void m_qTVersionasText(int nargs);
+void m_sMVersion(int nargs);
+void m_sMVersionasText(int nargs);
+void m_fontList(int nargs);
+void m_volumeList(int nargs);
+
+} // End of namespace SmallUtilXObj
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index 34e7b767ad6..87e969250b7 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -146,6 +146,7 @@ MODULE_OBJS = \
 	lingo/xlibs/registercomponent.o \
 	lingo/xlibs/remixxcmd.o \
 	lingo/xlibs/serialportxobj.o \
+	lingo/xlibs/smallutil.o \
 	lingo/xlibs/soundjam.o \
 	lingo/xlibs/spacemgr.o \
 	lingo/xlibs/stagetc.o \


Commit: 759e9016c7284dfce04663e09d3ac469dd91d09e
    https://github.com/scummvm/scummvm/commit/759e9016c7284dfce04663e09d3ac469dd91d09e
Author: Scott Percival (code at moral.net.au)
Date: 2025-07-21T12:43:25+02:00

Commit Message:
DIRECTOR: Silence noisy warning message

Changed paths:
    engines/director/cursor.cpp


diff --git a/engines/director/cursor.cpp b/engines/director/cursor.cpp
index b0fb0e6ec1e..666beeb7e98 100644
--- a/engines/director/cursor.cpp
+++ b/engines/director/cursor.cpp
@@ -159,7 +159,7 @@ void Cursor::readFromCast(Datum cursorCasts) {
 }
 
 void Cursor::readBuiltinType(Datum resourceId) {
-	if (resourceId.equalTo(_cursorResId))
+	if (resourceId.type == _cursorResId.type && resourceId.equalTo(_cursorResId))
 		return;
 
 	if (resourceId.type != INT) {
@@ -194,7 +194,7 @@ void Cursor::readBuiltinType(Datum resourceId) {
 }
 
 void Cursor::readFromResource(Datum resourceId) {
-	if (resourceId == _cursorResId)
+	if (resourceId.type == _cursorResId.type && resourceId.equalTo(_cursorResId))
 		return;
 
 	if (resourceId.type != INT) {


Commit: 762f60e565ac8ead39dd6c16756e5840a2291a0a
    https://github.com/scummvm/scummvm/commit/762f60e565ac8ead39dd6c16756e5840a2291a0a
Author: Scott Percival (code at moral.net.au)
Date: 2025-07-21T12:43:25+02:00

Commit Message:
DIRECTOR: Update Flipper detection entry

Changed paths:
    engines/director/detection_tables.h
    engines/director/game-quirks.cpp


diff --git a/engines/director/detection_tables.h b/engines/director/detection_tables.h
index 988fe93ccc0..e3a0be8c65b 100644
--- a/engines/director/detection_tables.h
+++ b/engines/director/detection_tables.h
@@ -6918,9 +6918,9 @@ static const DirectorGameDescription gameDescriptions[] = {
 
 	// Developed by Brilliant Interactive Ideas, published by Brainstorm
 	// Win version needs extraction/installation (DATA.Z)
-	MACGAME1("flipper", "", "Flipper/Flipper!",  "rt:7130f0fd98e0b169a2f3bfeb6fbded9a", 718125, 501),
-	WINGAME2("flipper", "", "FLIP32.EXE",		 "t:f5f0b26a2506fadac19f66cc2ae235f9", 1398201,
-							"FLIPPER/CPA24.DXR", "d:fcfd7c09d92797fa42f4a38a301b8c13", 2780110, 500),
+	MACGAME1f("flipper", "", "Flipper/Flipper!",  "rt:7130f0fd98e0b169a2f3bfeb6fbded9a", 718125, 501, GF_32BPP),
+	WINGAME2f("flipper", "", "FLIP32.EXE",		 "t:f5f0b26a2506fadac19f66cc2ae235f9", 1398201,
+							"FLIPPER/CPA24.DXR", "d:fcfd7c09d92797fa42f4a38a301b8c13", 2780110, 500, GF_32BPP),
 
 	MACGAME1("fplit", "", "Toyland",			  "r:7fc35c7129cb027987528b027c6cc27c", 705445, 500),
 	WINGAME2("fplit", "", "PIR32BIT/START32.EXE", "t:564db7e9ffacb6c0fecbf83c1f988069", 1394437,
diff --git a/engines/director/game-quirks.cpp b/engines/director/game-quirks.cpp
index 234d5365765..0a1cee574af 100644
--- a/engines/director/game-quirks.cpp
+++ b/engines/director/game-quirks.cpp
@@ -256,6 +256,8 @@ const struct Quirk {
 	{ "vnc", Common::kPlatformMacintosh, &quirkPretend16Bit },
 	{ "finkletimes", Common::kPlatformWindows, &quirkPretend16Bit },
 	{ "finkletimes", Common::kPlatformMacintosh, &quirkPretend16Bit },
+	{ "flipper", Common::kPlatformMacintosh, &quirkPretend16Bit },
+	{ "flipper", Common::kPlatformWindows, &quirkPretend16Bit },
 
 	{ nullptr, Common::kPlatformUnknown, nullptr }
 };


Commit: 1af84d273895900d1f61cb49b2b580189864873a
    https://github.com/scummvm/scummvm/commit/1af84d273895900d1f61cb49b2b580189864873a
Author: Scott Percival (code at moral.net.au)
Date: 2025-07-21T12:43:25+02:00

Commit Message:
DIRECTOR: Make DirectorSound use int for soundChannel everywhere

Changed paths:
    engines/director/sound.cpp
    engines/director/sound.h


diff --git a/engines/director/sound.cpp b/engines/director/sound.cpp
index 65f01a82a41..c7e2f4f0e84 100644
--- a/engines/director/sound.cpp
+++ b/engines/director/sound.cpp
@@ -68,13 +68,13 @@ DirectorSound::~DirectorSound() {
 		delete it._value;
 }
 
-SoundChannel *DirectorSound::getChannel(uint8 soundChannel) {
+SoundChannel *DirectorSound::getChannel(int soundChannel) {
 	if (!assertChannel(soundChannel))
 		return nullptr;
 	return _channels[soundChannel];
 }
 
-void DirectorSound::playFile(Common::String filename, uint8 soundChannel) {
+void DirectorSound::playFile(Common::String filename, int soundChannel) {
 	if (!assertChannel(soundChannel))
 		return;
 
@@ -103,7 +103,7 @@ void DirectorSound::playMCI(Audio::AudioStream &stream, uint32 from, uint32 to)
 	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_scriptSound, subSeekStream);
 }
 
-uint8 DirectorSound::getChannelVolume(uint8 soundChannel) {
+uint8 DirectorSound::getChannelVolume(int soundChannel) {
 	if (!assertChannel(soundChannel))
 		return 0;
 
@@ -116,7 +116,7 @@ void DirectorSound::setChannelDefaultVolume(int soundChannel) {
 	_channels[soundChannel]->volume = vol;
 }
 
-void DirectorSound::playStream(Audio::AudioStream &stream, uint8 soundChannel) {
+void DirectorSound::playStream(Audio::AudioStream &stream, int soundChannel) {
 	if (!assertChannel(soundChannel))
 		return;
 
@@ -129,7 +129,7 @@ void DirectorSound::playStream(Audio::AudioStream &stream, uint8 soundChannel) {
 	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_channels[soundChannel]->handle, &stream, -1, getChannelVolume(soundChannel));
 }
 
-void DirectorSound::playSound(SoundID soundID, uint8 soundChannel, bool forPuppet) {
+void DirectorSound::playSound(SoundID soundID, int soundChannel, bool forPuppet) {
 	switch (soundID.type) {
 	case kSoundCast:
 		playCastMember(CastMemberID(soundID.u.cast.member, soundID.u.cast.castLib), soundChannel, forPuppet);
@@ -140,7 +140,7 @@ void DirectorSound::playSound(SoundID soundID, uint8 soundChannel, bool forPuppe
 	}
 }
 
-void DirectorSound::playCastMember(CastMemberID memberID, uint8 soundChannel, bool forPuppet) {
+void DirectorSound::playCastMember(CastMemberID memberID, int soundChannel, bool forPuppet) {
 	if (!assertChannel(soundChannel))
 		return;
 
@@ -257,7 +257,7 @@ void SNDDecoder::loadExternalSoundStream(Common::SeekableReadStreamEndian &strea
 	_channels = 1;
 }
 
-void DirectorSound::registerFade(uint8 soundChannel, bool fadeIn, int ticks) {
+void DirectorSound::registerFade(int soundChannel, bool fadeIn, int ticks) {
 	if (!assertChannel(soundChannel))
 		return;
 
@@ -307,7 +307,7 @@ bool DirectorSound::fadeChannels() {
 	return ongoing;
 }
 
-void DirectorSound::cancelFade(uint8 soundChannel) {
+void DirectorSound::cancelFade(int soundChannel) {
 	if (!assertChannel(soundChannel))
 		return;
 	// NOTE: It is assumed that soundChannel has already been validated, which is
@@ -323,7 +323,7 @@ void DirectorSound::cancelFade(uint8 soundChannel) {
 	}
 }
 
-bool DirectorSound::isChannelActive(uint8 soundChannel) {
+bool DirectorSound::isChannelActive(int soundChannel) {
 	if (!assertChannel(soundChannel))
 		return false;
 
@@ -427,7 +427,7 @@ void DirectorSound::unloadSampleSounds() {
 	}
 }
 
-void DirectorSound::playExternalSound(uint16 menu, uint16 submenu, uint8 soundChannel) {
+void DirectorSound::playExternalSound(uint16 menu, uint16 submenu, int soundChannel) {
 	if (!assertChannel(soundChannel))
 		return;
 
@@ -479,21 +479,21 @@ void DirectorSound::changingMovie() {
 	unloadSampleSounds(); // TODO: we can possibly keep this between movies
 }
 
-void DirectorSound::setLastPlayedSound(uint8 soundChannel, SoundID soundId, bool stopOnZero) {
+void DirectorSound::setLastPlayedSound(int soundChannel, SoundID soundId, bool stopOnZero) {
 	_channels[soundChannel]->lastPlayedSound = soundId;
 	_channels[soundChannel]->stopOnZero = stopOnZero;
 	_channels[soundChannel]->movieChanged = false;
 }
 
-bool DirectorSound::isLastPlayedSound(uint8 soundChannel, const SoundID &soundId) {
+bool DirectorSound::isLastPlayedSound(int soundChannel, const SoundID &soundId) {
 	return !_channels[soundChannel]->movieChanged && _channels[soundChannel]->lastPlayedSound == soundId;
 }
 
-bool DirectorSound::shouldStopOnZero(uint8 soundChannel) {
+bool DirectorSound::shouldStopOnZero(int soundChannel) {
 	return _channels[soundChannel]->stopOnZero;
 }
 
-void DirectorSound::stopSound(uint8 soundChannel) {
+void DirectorSound::stopSound(int soundChannel) {
 	if (!assertChannel(soundChannel))
 		return;
 
@@ -529,7 +529,7 @@ void DirectorSound::systemBeep() {
 	_speaker->play(Audio::PCSpeaker::kWaveFormSquare, 500, 150);
 }
 
-bool DirectorSound::isChannelPuppet(uint8 soundChannel) {
+bool DirectorSound::isChannelPuppet(int soundChannel) {
 	if (!assertChannel(soundChannel))
 		return false;
 
@@ -540,7 +540,7 @@ bool DirectorSound::isChannelPuppet(uint8 soundChannel) {
 	return true;
 }
 
-void DirectorSound::setPuppetSound(SoundID soundId, uint8 soundChannel) {
+void DirectorSound::setPuppetSound(SoundID soundId, int soundChannel) {
 	if (!assertChannel(soundChannel))
 		return;
 
@@ -549,7 +549,7 @@ void DirectorSound::setPuppetSound(SoundID soundId, uint8 soundChannel) {
 	_channels[soundChannel]->stopOnZero = true;
 }
 
-void DirectorSound::playPuppetSound(uint8 soundChannel) {
+void DirectorSound::playPuppetSound(int soundChannel) {
 	if (!assertChannel(soundChannel))
 		return;
 
@@ -643,7 +643,7 @@ void DirectorSound::playFPlaySound(const Common::Array<Common::String> &fplayLis
 	playFPlaySound();
 }
 
-void DirectorSound::setChannelVolumeInternal(uint8 soundChannel, uint8 volume) {
+void DirectorSound::setChannelVolumeInternal(int soundChannel, uint8 volume) {
 	if (!(_channels[soundChannel]) || volume == _channels[soundChannel]->volume)
 		return;
 
diff --git a/engines/director/sound.h b/engines/director/sound.h
index b1d4aae19cd..8c53c8fcca0 100644
--- a/engines/director/sound.h
+++ b/engines/director/sound.h
@@ -176,13 +176,13 @@ public:
 	DirectorSound(Window *window);
 	~DirectorSound();
 
-	SoundChannel *getChannel(uint8 soundChannel);
-	void playFile(Common::String filename, uint8 soundChannel);
+	SoundChannel *getChannel(int soundChannel);
+	void playFile(Common::String filename, int soundChannel);
 	void playMCI(Audio::AudioStream &stream, uint32 from, uint32 to);
-	void playStream(Audio::AudioStream &stream, uint8 soundChannel);
-	void playSound(SoundID soundId, uint8 soundChannel, bool forPuppet = false);
-	void playCastMember(CastMemberID memberID, uint8 soundChannel, bool forPuppet = false);
-	void playExternalSound(uint16 menu, uint16 submenu, uint8 soundChannel);
+	void playStream(Audio::AudioStream &stream, int soundChannel);
+	void playSound(SoundID soundId, int soundChannel, bool forPuppet = false);
+	void playCastMember(CastMemberID memberID, int soundChannel, bool forPuppet = false);
+	void playExternalSound(uint16 menu, uint16 submenu, int soundChannel);
 	void playFPlaySound(const Common::Array<Common::String> &fplayList);
 	void playFPlaySound();
 	void setSoundEnabled(bool enabled);
@@ -192,32 +192,32 @@ public:
 	void loadSampleSounds(uint type);
 	void unloadSampleSounds();
 
-	bool isChannelPuppet(uint8 soundChannel);
-	void setPuppetSound(SoundID soundId, uint8 soundChannel);
-	void playPuppetSound(uint8 soundChannel);
+	bool isChannelPuppet(int soundChannel);
+	void setPuppetSound(SoundID soundId, int soundChannel);
+	void playPuppetSound(int soundChannel);
 
 	bool getSoundEnabled() { return _enable; }
 
 	Common::String getCurrentSound() { return _currentSoundName; }
 
-	void registerFade(uint8 soundChannel, bool fadeIn, int ticks);
+	void registerFade(int soundChannel, bool fadeIn, int ticks);
 	bool fadeChannels();
 
-	bool isChannelActive(uint8 soundChannel);
-	uint8 getChannelVolume(uint8 soundChannel);
+	bool isChannelActive(int soundChannel);
+	uint8 getChannelVolume(int soundChannel);
 	void setChannelVolume(int channel, uint8 volume);
-	void stopSound(uint8 soundChannel);
+	void stopSound(int soundChannel);
 	void stopSound();
 	void setChannelDefaultVolume(int soundChannel);
 
 private:
-	void setLastPlayedSound(uint8 soundChannel, SoundID soundId, bool stopOnZero = true);
-	bool isLastPlayedSound(uint8 soundChannel, const SoundID &soundId);
-	bool shouldStopOnZero(uint8 soundChannel);
+	void setLastPlayedSound(int soundChannel, SoundID soundId, bool stopOnZero = true);
+	bool isLastPlayedSound(int soundChannel, const SoundID &soundId);
+	bool shouldStopOnZero(int soundChannel);
 
-	void setChannelVolumeInternal(uint8 soundChannel, uint8 volume);
+	void setChannelVolumeInternal(int soundChannel, uint8 volume);
 	bool assertChannel(int soundChannel);
-	void cancelFade(uint8 soundChannel);
+	void cancelFade(int soundChannel);
 };
 
 class AudioDecoder {


Commit: 8bbfda114ddc40dfe9f3a4d897f92a3537064d47
    https://github.com/scummvm/scummvm/commit/8bbfda114ddc40dfe9f3a4d897f92a3537064d47
Author: Scott Percival (code at moral.net.au)
Date: 2025-07-21T12:43:25+02:00

Commit Message:
DIRECTOR: Mark channel dirty if the blendAmount changes

Fixes fade of sprites in the introduction of Puppet Motel.

Changed paths:
    engines/director/channel.cpp


diff --git a/engines/director/channel.cpp b/engines/director/channel.cpp
index db14e8d2631..540d1930346 100644
--- a/engines/director/channel.cpp
+++ b/engines/director/channel.cpp
@@ -255,7 +255,8 @@ bool Channel::isDirty(Sprite *nextSprite) {
 		// modified.
 		isDirtyFlag |= _sprite->_castId != nextSprite->_castId ||
 			_sprite->_ink != nextSprite->_ink || _sprite->_backColor != nextSprite->_backColor ||
-			_sprite->_foreColor != nextSprite->_foreColor;
+			_sprite->_foreColor != nextSprite->_foreColor || _sprite->_blend != nextSprite->_blend ||
+			_sprite->_blendAmount != nextSprite->_blendAmount || _sprite->_thickness != nextSprite->_thickness;
 		if (!_sprite->_moveable)
 			isDirtyFlag |= _sprite->getPosition() != nextSprite->getPosition();
 		if (isStretched() && !hasTextCastMember(_sprite))


Commit: 3b1b9a5e0a401b11665da666da2712e2044973e9
    https://github.com/scummvm/scummvm/commit/3b1b9a5e0a401b11665da666da2712e2044973e9
Author: Scott Percival (code at moral.net.au)
Date: 2025-07-21T12:43:25+02:00

Commit Message:
DIRECTOR: XOBJ: Add basic VoyagerXSound playback

Fixes scripting and sound playback of Puppet Motel.

Changed paths:
    engines/director/lingo/xlibs/voyagerxsound.cpp
    engines/director/lingo/xlibs/voyagerxsound.h


diff --git a/engines/director/lingo/xlibs/voyagerxsound.cpp b/engines/director/lingo/xlibs/voyagerxsound.cpp
index 51e524ddbec..c888a923c9e 100644
--- a/engines/director/lingo/xlibs/voyagerxsound.cpp
+++ b/engines/director/lingo/xlibs/voyagerxsound.cpp
@@ -22,6 +22,7 @@
 #include "common/system.h"
 
 #include "director/director.h"
+#include "director/window.h"
 #include "director/lingo/lingo.h"
 #include "director/lingo/lingo-object.h"
 #include "director/lingo/lingo-utils.h"
@@ -109,6 +110,41 @@ static BuiltinProto xlibBuiltins[] = {
 
 VoyagerXSoundXObject::VoyagerXSoundXObject(ObjectType ObjectType) :Object<VoyagerXSoundXObject>("XSound") {
 	_objType = ObjectType;
+	_soundManager = g_director->getCurrentWindow()->getSoundManager();
+
+}
+
+VoyagerXSoundXObject::~VoyagerXSoundXObject() {
+	close();
+}
+
+int VoyagerXSoundXObject::open(int numChan, int monoStereo) {
+	if (!_channels.contains(numChan)) {
+		_channels[numChan] = new VoyagerChannel();
+		_channels[numChan]->channelID = numChan + 1000;
+	}
+	return 1;
+}
+
+void VoyagerXSoundXObject::close() {
+	for (auto &it : _channels)
+		delete it._value;
+	_channels.clear();
+}
+
+int VoyagerXSoundXObject::status(int chan) {
+	if (_channels.contains(chan)) {
+		return _soundManager->isChannelActive(_channels[chan]->channelID) ? 1 : 0;
+	}
+	return 0;
+}
+
+int VoyagerXSoundXObject::playfile(int chan, Common::String &path, int tstart, int tend) {
+	if (!_channels.contains(chan)) {
+		open(chan, 2);
+	}
+	_soundManager->playFile(path, _channels[chan]->channelID);
+	return 1;
 }
 
 void VoyagerXSoundXObj::open(ObjectType type, const Common::Path &path) {
@@ -123,7 +159,6 @@ void VoyagerXSoundXObj::open(ObjectType type, const Common::Path &path) {
 void VoyagerXSoundXObj::close(ObjectType type) {
     VoyagerXSoundXObject::cleanupMethods();
     g_lingo->_globalvars[xlibName] = Datum();
-
 }
 
 void VoyagerXSoundXObj::m_new(int nargs) {
@@ -132,18 +167,72 @@ void VoyagerXSoundXObj::m_new(int nargs) {
 	g_lingo->push(g_lingo->_state->me);
 }
 
+
 // For some reason the game code calls all of these with ARGC, so always return something
 XOBJSTUBNR(VoyagerXSoundXObj::m_dispose)
 XOBJSTUBNR(VoyagerXSoundXObj::m_init)
-XOBJSTUB(VoyagerXSoundXObj::m_open, 0)
-XOBJSTUB(VoyagerXSoundXObj::m_close, 0)
+
+void VoyagerXSoundXObj::m_open(int nargs) {
+	g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_open", nargs);
+	ARGNUMCHECK(2);
+	VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
+	Datum monoStereo = g_lingo->pop();
+	Datum numChan = g_lingo->pop();
+	int result = me->open(numChan.asInt(), monoStereo.asInt());
+	g_lingo->push(result);
+}
+
+void VoyagerXSoundXObj::m_close(int nargs) {
+	g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_close", nargs);
+	ARGNUMCHECK(0);
+	VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
+	me->close();
+	g_lingo->push(0);
+}
+
 XOBJSTUB(VoyagerXSoundXObj::m_bufsize, 0)
 XOBJSTUB(VoyagerXSoundXObj::m_exists, 0)
-XOBJSTUB(VoyagerXSoundXObj::m_status, 0)
+
+void VoyagerXSoundXObj::m_status(int nargs) {
+	g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_status", nargs);
+	VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
+	ARGNUMCHECK(1);
+	Datum chan = g_lingo->pop();
+	g_lingo->push(me->status(chan.asInt()));
+}
+
 XOBJSTUB(VoyagerXSoundXObj::m_path, 0)
 XOBJSTUB(VoyagerXSoundXObj::m_duration, "")
 XOBJSTUB(VoyagerXSoundXObj::m_curtime, "")
-XOBJSTUB(VoyagerXSoundXObj::m_playfile, 0)
+
+void VoyagerXSoundXObj::m_playfile(int nargs) {
+	g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_playfile", nargs);
+	VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
+	if (nargs < 2) {
+		warning("VoyagerXSoundXObj::m_playfile: expected at least 2 args");
+		g_lingo->dropStack(nargs);
+		g_lingo->push(0);
+		return;
+	} else if (nargs > 4) {
+		g_lingo->dropStack(nargs - 4);
+		nargs = 4;
+	}
+	Datum tend(-1);
+	if (nargs == 4) {
+		tend = g_lingo->pop();
+		nargs--;
+	}
+	Datum tstart(-1);
+	if (nargs == 3) {
+		tstart = g_lingo->pop();
+		nargs--;
+	}
+	Common::String path = g_lingo->pop().asString();
+	Datum chan = g_lingo->pop();
+	int result = me->playfile(chan.asInt(), path, tstart.asInt(), tend.asInt());
+	g_lingo->push(result);
+}
+
 XOBJSTUB(VoyagerXSoundXObj::m_loadfile, 0)
 XOBJSTUB(VoyagerXSoundXObj::m_unloadfile, 0)
 XOBJSTUB(VoyagerXSoundXObj::m_playsnd, 0)
diff --git a/engines/director/lingo/xlibs/voyagerxsound.h b/engines/director/lingo/xlibs/voyagerxsound.h
index da9d7620224..472ec4f21da 100644
--- a/engines/director/lingo/xlibs/voyagerxsound.h
+++ b/engines/director/lingo/xlibs/voyagerxsound.h
@@ -22,11 +22,27 @@
 #ifndef DIRECTOR_LINGO_XLIBS_VOYAGERXSOUND_H
 #define DIRECTOR_LINGO_XLIBS_VOYAGERXSOUND_H
 
+#include "director/sound.h"
+
 namespace Director {
 
+struct VoyagerChannel {
+	int channelID;
+};
+
 class VoyagerXSoundXObject : public Object<VoyagerXSoundXObject> {
 public:
 	VoyagerXSoundXObject(ObjectType objType);
+	~VoyagerXSoundXObject();
+
+	int open(int monoStereo, int numChan);
+	void close();
+	int status(int chan);
+	int playfile(int chan, Common::String &path, int tstart, int tend);
+
+	DirectorSound *_soundManager;
+
+	Common::HashMap<int, VoyagerChannel *> _channels;
 };
 
 namespace VoyagerXSoundXObj {


Commit: 3ec095a40b0260dd229f737b212fd0c73935a7c3
    https://github.com/scummvm/scummvm/commit/3ec095a40b0260dd229f737b212fd0c73935a7c3
Author: Scott Percival (code at moral.net.au)
Date: 2025-07-21T12:43:25+02:00

Commit Message:
DIRECTOR: XOBJ: Add file read/write commands to Ednox

Fixes save games in Grackon's Curse.

Changed paths:
    engines/director/lingo/xlibs/ednox.cpp


diff --git a/engines/director/lingo/xlibs/ednox.cpp b/engines/director/lingo/xlibs/ednox.cpp
index 8171ec8560b..d97a0d6c382 100644
--- a/engines/director/lingo/xlibs/ednox.cpp
+++ b/engines/director/lingo/xlibs/ednox.cpp
@@ -55,6 +55,7 @@
  * SSS    deleteDocumentFile,hDir,hFile  -- delete a file from the directory
 */
 
+#include "common/savefile.h"
 #include "director/director.h"
 #include "director/lingo/lingo.h"
 #include "director/lingo/lingo-object.h"
@@ -121,13 +122,6 @@ void Ednox::m_new(int nargs) {
 
 XOBJSTUBNR(Ednox::m_dispose)
 
-void Ednox::m_getdocumentfile(int nargs) {
-	// Common::U32String hFile = g_lingo->pop().asString();
-	// Common::U32String hDir = g_lingo->pop().asString();
-	g_lingo->printSTUBWithArglist("Ednox::m_getdocumentfile", nargs);
-	g_lingo->dropStack(nargs);
-}
-
 void Ednox::m_getpathx(int nargs) {
 	/* int mMacMode = */ g_lingo->pop().asInt();
 	Common::U32String hStrIn = g_lingo->pop().asString();
@@ -145,25 +139,17 @@ void Ednox::m_iscdx(int nargs) {
 	Common::U32String hDrive = g_lingo->pop().asString();
 	// g_lingo->printSTUBWithArglist("Ednox::m_iscdx", nargs);
 	if (hDrive == "d:\\"){
-		g_lingo->push(Datum(0));
+		g_lingo->push(Datum("0"));
 	} else {
-		g_lingo->push(Datum(-1));
+		g_lingo->push(Datum("-1"));
 	}
 }
 
-void Ednox::m_savedocumentfile(int nargs) {
-	// Common::U32String hStrIn = g_lingo->pop().asString();
-	// Common::U32String hFile = g_lingo->pop().asString();
-	// Common::U32String hDir = g_lingo->pop().asString();
-	g_lingo->printSTUBWithArglist("Ednox::m_savedocumentfile", nargs);
-	g_lingo->dropStack(nargs);
-}
-
 void Ednox::m_setdrivex(int nargs) {
 	// Common::U32String hStrIn = g_lingo->pop().asString();
 	g_lingo->printSTUBWithArglist("Ednox::m_setdrivex", nargs);
 	g_lingo->dropStack(nargs);
-	g_lingo->push(Datum(0));
+	g_lingo->push(Datum("1"));
 }
 
 XOBJSTUB(Ednox::m_checksoundx, "")
@@ -185,12 +171,71 @@ void Ednox::m_drawbkgndx(int nargs) {
 }
 
 void Ednox::m_getdocumentname(int nargs) {
-	// Common::U32String hExt = g_lingo->pop().asString();
-	// Common::U32String hDir = g_lingo->pop().asString();
+	Common::SaveFileManager *saves = g_system->getSavefileManager();
+	Common::String prefix = savePrefix();
 	g_lingo->printSTUBWithArglist("Ednox::m_getdocumentname", nargs);
-	g_lingo->dropStack(nargs);
+	ARGNUMCHECK(2);
+	Common::String hExt = g_lingo->pop().asString();
+	Common::String hDir = g_lingo->pop().asString();
+	Common::String result;
+	Common::StringArray existing = saves->listSavefiles(Common::String::format("%s*", prefix.c_str()));
+	bool first = true;
+	for (auto &it : existing) {
+		if (first)
+			first = false;
+		else
+			result += ",";
+		result += it.substr(prefix.size());
+	}
+	g_lingo->push(result);
 }
 
+void Ednox::m_getdocumentfile(int nargs) {
+	Common::SaveFileManager *saves = g_system->getSavefileManager();
+	Common::String prefix = savePrefix();
+	g_lingo->printSTUBWithArglist("Ednox::m_getdocumentfile", nargs);
+	ARGNUMCHECK(2);
+	Common::String hFile = g_lingo->pop().asString();
+	Common::String hDir = g_lingo->pop().asString();
+
+	Common::String filename = prefix + hFile;
+	// ignore the directory, we just care about the filename
+	if (!saves->exists(filename)) {
+		warning("Ednox::m_getdocumentfile: No file exists for %s", filename.c_str());
+		g_lingo->push(Datum());
+		return;
+	}
+	Common::SeekableReadStream *stream = saves->openForLoading(filename);
+	if (!stream) {
+		warning("Ednox::m_getdocumentfile: Unable to open file %s", filename.c_str());
+		g_lingo->push(Datum());
+		return;
+	}
+	Common::String result = stream->readString();
+	delete stream;
+	g_lingo->push(Datum(result));
+}
+
+void Ednox::m_savedocumentfile(int nargs) {
+	Common::SaveFileManager *saves = g_system->getSavefileManager();
+	g_lingo->printSTUBWithArglist("Ednox::m_savedocumentfile", nargs);
+	ARGNUMCHECK(3);
+	Common::String hStrIn = g_lingo->pop().asString();
+	Common::String hFile = g_lingo->pop().asString();
+	Common::String hDir = g_lingo->pop().asString();
+	Common::String filename = savePrefix() + hFile;
+	Common::SeekableWriteStream *stream = saves->openForSaving(filename, false);
+	if (!stream) {
+		warning("Ednox::m_savedocumentfile: Unable to open file %s", filename.c_str());
+		g_lingo->push(Datum());
+		return;
+	}
+	stream->writeString(hStrIn);
+	delete stream;
+	g_lingo->push(Common::String(""));
+}
+
+
 void Ednox::m_error(int nargs) {
 	// int code = g_lingo->pop().asInt();
 	g_lingo->printSTUBWithArglist("Ednox::m_error", nargs);


Commit: ef27bd06bb18b1b8fcf364f24d3eba04f0d954e9
    https://github.com/scummvm/scummvm/commit/ef27bd06bb18b1b8fcf364f24d3eba04f0d954e9
Author: Scott Percival (code at moral.net.au)
Date: 2025-07-21T12:43:25+02:00

Commit Message:
DIRECTOR: XOBJ: Implement more of VoyagerXSound

Fixes more transitions and features in Puppet Motel.

Changed paths:
    engines/director/lingo/xlibs/voyagerxsound.cpp
    engines/director/lingo/xlibs/voyagerxsound.h
    engines/director/sound.cpp
    engines/director/sound.h


diff --git a/engines/director/lingo/xlibs/voyagerxsound.cpp b/engines/director/lingo/xlibs/voyagerxsound.cpp
index c888a923c9e..0df3c6ea877 100644
--- a/engines/director/lingo/xlibs/voyagerxsound.cpp
+++ b/engines/director/lingo/xlibs/voyagerxsound.cpp
@@ -22,6 +22,8 @@
 #include "common/system.h"
 
 #include "director/director.h"
+#include "director/movie.h"
+#include "director/score.h"
 #include "director/window.h"
 #include "director/lingo/lingo.h"
 #include "director/lingo/lingo-object.h"
@@ -147,6 +149,42 @@ int VoyagerXSoundXObject::playfile(int chan, Common::String &path, int tstart, i
 	return 1;
 }
 
+int VoyagerXSoundXObject::fade(int chan, int endvol, int duration, bool autostop) {
+	if (!_channels.contains(chan)) {
+		return 0;
+	}
+	int channelID = _channels[chan]->channelID;
+	_soundManager->registerFade(channelID, _soundManager->getChannelVolume(channelID), endvol, duration*60, autostop);
+	Window *window = g_director->getCurrentWindow();
+	Score *score = window->getCurrentMovie()->getScore();
+	score->_activeFade = true;
+	return 1;
+}
+
+void VoyagerXSoundXObject::stop(int chan) {
+	if (!_channels.contains(chan)) {
+		return;
+	}
+	int channelID = _channels[chan]->channelID;
+	_soundManager->stopSound(channelID);
+}
+
+void VoyagerXSoundXObject::volume(int chan, int vol) {
+	if (!_channels.contains(chan)) {
+		return;
+	}
+	int channelID = _channels[chan]->channelID;
+	_soundManager->setChannelVolume(channelID, vol);
+}
+
+void VoyagerXSoundXObject::frequency(int chan, int percent) {
+	if (!_channels.contains(chan)) {
+		return;
+	}
+	int channelID = _channels[chan]->channelID;
+	_soundManager->setChannelPitchShift(channelID, percent);
+}
+
 void VoyagerXSoundXObj::open(ObjectType type, const Common::Path &path) {
     VoyagerXSoundXObject::initMethods(xlibMethods);
     VoyagerXSoundXObject *xobj = new VoyagerXSoundXObject(type);
@@ -235,13 +273,75 @@ void VoyagerXSoundXObj::m_playfile(int nargs) {
 
 XOBJSTUB(VoyagerXSoundXObj::m_loadfile, 0)
 XOBJSTUB(VoyagerXSoundXObj::m_unloadfile, 0)
-XOBJSTUB(VoyagerXSoundXObj::m_playsnd, 0)
+
+void VoyagerXSoundXObj::m_playsnd(int nargs) {
+	m_playfile(nargs);
+}
+
 XOBJSTUB(VoyagerXSoundXObj::m_extplayfile, 0)
-XOBJSTUB(VoyagerXSoundXObj::m_stop, 0)
-XOBJSTUB(VoyagerXSoundXObj::m_volume, 0)
+
+void VoyagerXSoundXObj::m_stop(int nargs) {
+	g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_stop", nargs);
+	VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
+	ARGNUMCHECK(1);
+	int chan = g_lingo->pop().asInt();
+	me->stop(chan);
+	g_lingo->push(Datum(1));
+}
+
+void VoyagerXSoundXObj::m_volume(int nargs) {
+	g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_volume", nargs);
+	VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
+	ARGNUMCHECK(2);
+	int vol = g_lingo->pop().asInt();
+	int chan = g_lingo->pop().asInt();
+	me->volume(chan, vol);
+	g_lingo->push(Datum(1));
+}
+
 XOBJSTUB(VoyagerXSoundXObj::m_leftrightvol, 0)
-XOBJSTUB(VoyagerXSoundXObj::m_fade, 0)
-XOBJSTUB(VoyagerXSoundXObj::m_frequency, 0)
+
+void VoyagerXSoundXObj::m_fade(int nargs) {
+	g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_fade", nargs);
+	VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
+	if (nargs < 2) {
+		warning("VoyagerXSoundXObj::m_fade: expected at least 2 args");
+		g_lingo->dropStack(nargs);
+		g_lingo->push(Datum());
+		return;
+	}
+	if (nargs > 4) {
+		warning("VoyagerXSoundXObj: dropping %d extra args", nargs - 4);
+		g_lingo->dropStack(nargs - 4);
+		nargs = 4;
+	}
+	bool autoStop = false;
+	int duration = 0;
+	if (nargs == 4) {
+		autoStop = (bool)g_lingo->pop().asInt();
+		nargs--;
+	}
+	if (nargs == 3) {
+		duration = g_lingo->pop().asInt();
+		nargs--;
+	}
+	int endVol = g_lingo->pop().asInt();
+	int chan = g_lingo->pop().asInt();
+
+	g_lingo->push(Datum(me->fade(chan, endVol, duration, autoStop)));
+}
+
+void VoyagerXSoundXObj::m_frequency(int nargs) {
+	g_lingo->printSTUBWithArglist("VoyagerXSoundXObj::m_frequency", nargs);
+	VoyagerXSoundXObject *me = static_cast<VoyagerXSoundXObject *>(g_lingo->_state->me.u.obj);
+	ARGNUMCHECK(2);
+	int percent = g_lingo->pop().asInt();
+	int chan = g_lingo->pop().asInt();
+	me->frequency(chan, percent);
+	g_lingo->push(Datum(1));
+}
+
+
 XOBJSTUB(VoyagerXSoundXObj::m_pan, 0)
 XOBJSTUB(VoyagerXSoundXObj::m_startrecord, 0)
 XOBJSTUB(VoyagerXSoundXObj::m_stoprecord, 0)
diff --git a/engines/director/lingo/xlibs/voyagerxsound.h b/engines/director/lingo/xlibs/voyagerxsound.h
index 472ec4f21da..b40e9f227d4 100644
--- a/engines/director/lingo/xlibs/voyagerxsound.h
+++ b/engines/director/lingo/xlibs/voyagerxsound.h
@@ -39,6 +39,10 @@ public:
 	void close();
 	int status(int chan);
 	int playfile(int chan, Common::String &path, int tstart, int tend);
+	int fade(int chan, int endvol, int duration, bool autostop);
+	void stop(int chan);
+	void volume(int chan, int vol);
+	void frequency(int chan, int percent);
 
 	DirectorSound *_soundManager;
 
diff --git a/engines/director/sound.cpp b/engines/director/sound.cpp
index c7e2f4f0e84..75ed5d57e95 100644
--- a/engines/director/sound.cpp
+++ b/engines/director/sound.cpp
@@ -89,6 +89,10 @@ void DirectorSound::playFile(Common::String filename, int soundChannel) {
 
 	setChannelDefaultVolume(soundChannel);
 	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_channels[soundChannel]->handle, sound, -1, getChannelVolume(soundChannel));
+	_channels[soundChannel]->originalRate = (int)_mixer->getChannelRate(_channels[soundChannel]->handle);
+	if (_channels[soundChannel]->pitchShiftPercent != 100) {
+		_mixer->setChannelRate(_channels[soundChannel]->handle, _channels[soundChannel]->originalRate*_channels[soundChannel]->pitchShiftPercent/100);
+	}
 
 	// Set the last played sound so that cast member 0 in the sound channel doesn't stop this file.
 	setLastPlayedSound(soundChannel, SoundID(), false);
@@ -116,6 +120,14 @@ void DirectorSound::setChannelDefaultVolume(int soundChannel) {
 	_channels[soundChannel]->volume = vol;
 }
 
+void DirectorSound::setChannelPitchShift(int soundChannel, int pitchShiftPercent) {
+	_channels[soundChannel]->pitchShiftPercent = pitchShiftPercent;
+	if (isChannelActive(soundChannel)) {
+		_mixer->setChannelRate(_channels[soundChannel]->handle, _channels[soundChannel]->originalRate*_channels[soundChannel]->pitchShiftPercent/100);
+	}
+}
+
+
 void DirectorSound::playStream(Audio::AudioStream &stream, int soundChannel) {
 	if (!assertChannel(soundChannel))
 		return;
@@ -126,7 +138,12 @@ void DirectorSound::playStream(Audio::AudioStream &stream, int soundChannel) {
 
 	setChannelDefaultVolume(soundChannel);
 
+
 	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_channels[soundChannel]->handle, &stream, -1, getChannelVolume(soundChannel));
+	_channels[soundChannel]->originalRate = (int)_mixer->getChannelRate(_channels[soundChannel]->handle);
+	if (_channels[soundChannel]->pitchShiftPercent != 100) {
+		_mixer->setChannelRate(_channels[soundChannel]->handle, _channels[soundChannel]->originalRate*_channels[soundChannel]->pitchShiftPercent/100);
+	}
 }
 
 void DirectorSound::playSound(SoundID soundID, int soundChannel, bool forPuppet) {
@@ -257,11 +274,20 @@ void SNDDecoder::loadExternalSoundStream(Common::SeekableReadStreamEndian &strea
 	_channels = 1;
 }
 
-void DirectorSound::registerFade(int soundChannel, bool fadeIn, int ticks) {
+void DirectorSound::registerFade(int soundChannel, bool fadeIn, int ticks, bool autoStop) {
 	if (!assertChannel(soundChannel))
 		return;
 
-	debugC(5, kDebugSound, "DirectorSound::registerFade(): registered fading channel %d %s over %d ticks", soundChannel, fadeIn ? "in" : "out", ticks);
+	int startVol = fadeIn ? 0 :  _channels[soundChannel]->volume;
+	int targetVol = fadeIn ? _channels[soundChannel]->volume : 0;
+	registerFade(soundChannel, startVol, targetVol, ticks, autoStop);
+}
+
+void DirectorSound::registerFade(int soundChannel, int startVol, int targetVol, int ticks, bool autoStop) {
+	if (!assertChannel(soundChannel))
+		return;
+
+	debugC(5, kDebugSound, "DirectorSound::registerFade(): registered fading channel %d over %d ticks, startVol: %d, targetVol: %d, autostop: %d", soundChannel, ticks, startVol, targetVol, autoStop);
 
 	// sound enable is not working on fade sounds, so we just return directly when sounds are not enabling
 	if (!_enable)
@@ -269,10 +295,7 @@ void DirectorSound::registerFade(int soundChannel, bool fadeIn, int ticks) {
 
 	cancelFade(soundChannel);
 
-	int startVol = fadeIn ? 0 :  _channels[soundChannel]->volume;
-	int targetVol = fadeIn ? _channels[soundChannel]->volume : 0;
-
-	_channels[soundChannel]->fade = new FadeParams(startVol, targetVol, ticks, _window->getVM()->getMacTicks(), fadeIn);
+	_channels[soundChannel]->fade = new FadeParams(startVol, targetVol, ticks, _window->getVM()->getMacTicks(), targetVol > startVol, autoStop);
 	_mixer->setChannelVolume(_channels[soundChannel]->handle, startVol);
 
 	_channels[soundChannel]->volume = startVol;
@@ -288,6 +311,8 @@ bool DirectorSound::fadeChannels() {
 
 		fade->lapsedTicks = _window->getVM()->getMacTicks() - fade->startTicks;
 		if (fade->lapsedTicks > fade->totalTicks) {
+			if (fade->autoStop)
+				stopSound(it._key);
 			continue;
 		}
 
diff --git a/engines/director/sound.h b/engines/director/sound.h
index 8c53c8fcca0..5c83acf65a6 100644
--- a/engines/director/sound.h
+++ b/engines/director/sound.h
@@ -47,9 +47,10 @@ struct FadeParams {
 	int startTicks;
 	int lapsedTicks;
 	bool fadeIn;
+	bool autoStop;
 
-	FadeParams(int sv, int tv, int tt, int st, bool f) :
-		startVol(sv), targetVol(tv), totalTicks(tt), startTicks(st), lapsedTicks(0), fadeIn(f) {}
+	FadeParams(int sv, int tv, int tt, int st, bool f, bool as) :
+		startVol(sv), targetVol(tv), totalTicks(tt), startTicks(st), lapsedTicks(0), fadeIn(f), autoStop(as) {}
 };
 
 const uint16 kMinSampledMenu = 10;
@@ -136,6 +137,8 @@ struct SoundChannel {
 	SoundID lastPlayedSound;
 	bool stopOnZero; // Should the sound be stopped when the channel contains cast member 0?
 	byte volume;
+	int pitchShiftPercent;
+	int originalRate;
 	FadeParams *fade;
 
 	// a non-zero sound ID if the channel is a puppet. i.e. it's controlled by lingo
@@ -150,7 +153,7 @@ struct SoundChannel {
 	// a stop at the end of a loop.
 	Audio::LoopableAudioStream *loopPtr;
 
-	SoundChannel(): handle(), lastPlayedSound(SoundID()), stopOnZero(true), volume(255), fade(nullptr), puppet(SoundID()), newPuppet(false), movieChanged(false), loopPtr(nullptr) {}
+	SoundChannel(): handle(), lastPlayedSound(SoundID()), stopOnZero(true), volume(255), originalRate(-1), pitchShiftPercent(100), fade(nullptr), puppet(SoundID()), newPuppet(false), movieChanged(false), loopPtr(nullptr) {}
 };
 
 class DirectorSound {
@@ -200,7 +203,8 @@ public:
 
 	Common::String getCurrentSound() { return _currentSoundName; }
 
-	void registerFade(int soundChannel, bool fadeIn, int ticks);
+	void registerFade(int soundChannel, int startVol, int targetVol, int ticks, bool autoStop = false);
+	void registerFade(int soundChannel, bool fadeIn, int ticks, bool autoStop = false);
 	bool fadeChannels();
 
 	bool isChannelActive(int soundChannel);
@@ -209,6 +213,7 @@ public:
 	void stopSound(int soundChannel);
 	void stopSound();
 	void setChannelDefaultVolume(int soundChannel);
+	void setChannelPitchShift(int soundChannel, int pitchShiftPercent);
 
 private:
 	void setLastPlayedSound(int soundChannel, SoundID soundId, bool stopOnZero = true);


Commit: b0af6d4218744bd6410907aa081e2aef8812eb48
    https://github.com/scummvm/scummvm/commit/b0af6d4218744bd6410907aa081e2aef8812eb48
Author: Scott Percival (code at moral.net.au)
Date: 2025-07-21T12:43:25+02:00

Commit Message:
DIRECTOR: XOBJ: Add VMPresent stub

Fixes start sequence of Dazzeloids.

Changed paths:
  A engines/director/lingo/xlibs/vmpresent.cpp
  A engines/director/lingo/xlibs/vmpresent.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 20c6accfd4d..0a83ff1aea8 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -126,6 +126,7 @@
 #include "director/lingo/xlibs/valkyrie.h"
 #include "director/lingo/xlibs/videodiscxobj.h"
 #include "director/lingo/xlibs/vmisonxfcn.h"
+#include "director/lingo/xlibs/vmpresent.h"
 #include "director/lingo/xlibs/volumelist.h"
 #include "director/lingo/xlibs/voyagerxsound.h"
 #include "director/lingo/xlibs/widgetxobj.h"
@@ -325,6 +326,7 @@ static const struct XLibProto {
 	XLIBDEF(TenguXObj,			kXObj,					400),	// D4
 	XLIBDEF(TimextraXtra,		kXtraObj,		500),	// D5
 	XLIBDEF(UnitTestXObj,		kXObj,			400),	// D4
+	XLIBDEF(VMPresentXObj,			kXObj,					400),	// D4
 	XLIBDEF(VMisOnXFCN,			kXObj,			400),	// D4
 	XLIBDEF(ValkyrieXObj,		kXObj,			400),	// D4
 	XLIBDEF(VideodiscXObj,		kXObj,			200),	// D2
diff --git a/engines/director/lingo/xlibs/vmpresent.cpp b/engines/director/lingo/xlibs/vmpresent.cpp
new file mode 100644
index 00000000000..8e72d9d93cf
--- /dev/null
+++ b/engines/director/lingo/xlibs/vmpresent.cpp
@@ -0,0 +1,91 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xlibs/vmpresent.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * dazzeloids
+ *
+ **************************************************/
+
+/*
+-- VMPresentObj v1.0
+I 	mNew		--Instantiate the XObject
+I	mVMPresent	--Returns true (1) if VM is turned on, otherwise 0 (false)
+ */
+
+namespace Director {
+
+const char *VMPresentXObj::xlibName = "VMPresent";
+const XlibFileDesc VMPresentXObj::fileNames[] = {
+	{ "VMPresent",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+	{ "new",				VMPresentXObj::m_new,		 0, 0,	400 },
+	{ "vMPresent",				VMPresentXObj::m_vMPresent,		 0, 0,	400 },
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+VMPresentXObject::VMPresentXObject(ObjectType ObjectType) :Object<VMPresentXObject>("VMPresent") {
+	_objType = ObjectType;
+}
+
+void VMPresentXObj::open(ObjectType type, const Common::Path &path) {
+    VMPresentXObject::initMethods(xlibMethods);
+    VMPresentXObject *xobj = new VMPresentXObject(type);
+    if (type == kXtraObj)
+        g_lingo->_openXtras.push_back(xlibName);
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void VMPresentXObj::close(ObjectType type) {
+    VMPresentXObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void VMPresentXObj::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("VMPresentXObj::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+// Dazzeloids won't start unless virtual memory is disabled.
+// If another game uses this XObj, we might need to expand this into a title check.
+XOBJSTUB(VMPresentXObj::m_vMPresent, 0)
+
+}
diff --git a/engines/director/lingo/xlibs/vmpresent.h b/engines/director/lingo/xlibs/vmpresent.h
new file mode 100644
index 00000000000..a4683fdd988
--- /dev/null
+++ b/engines/director/lingo/xlibs/vmpresent.h
@@ -0,0 +1,47 @@
+/* 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 DIRECTOR_LINGO_XLIBS_VMPRESENT_H
+#define DIRECTOR_LINGO_XLIBS_VMPRESENT_H
+
+namespace Director {
+
+class VMPresentXObject : public Object<VMPresentXObject> {
+public:
+	VMPresentXObject(ObjectType objType);
+};
+
+namespace VMPresentXObj {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_vMPresent(int nargs);
+
+} // End of namespace VMPresentXObj
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index 87e969250b7..9070e179ae3 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -156,6 +156,7 @@ MODULE_OBJS = \
 	lingo/xlibs/valkyrie.o \
 	lingo/xlibs/videodiscxobj.o \
 	lingo/xlibs/vmisonxfcn.o \
+	lingo/xlibs/vmpresent.o \
 	lingo/xlibs/volumelist.o \
 	lingo/xlibs/voyagerxsound.o \
 	lingo/xlibs/widgetxobj.o \


Commit: df3c32da7b437b8074362fe3d10c88671e0e86f8
    https://github.com/scummvm/scummvm/commit/df3c32da7b437b8074362fe3d10c88671e0e86f8
Author: Scott Percival (code at moral.net.au)
Date: 2025-07-21T12:43:25+02:00

Commit Message:
DIRECTOR: Update detection entry for Puppet Motel

Changed paths:
    engines/director/detection_tables.h


diff --git a/engines/director/detection_tables.h b/engines/director/detection_tables.h
index e3a0be8c65b..e947fe5094a 100644
--- a/engines/director/detection_tables.h
+++ b/engines/director/detection_tables.h
@@ -7529,9 +7529,9 @@ static const DirectorGameDescription gameDescriptions[] = {
 
 	WINGAME1("princetonsat98", "", "sat98_32.exe", "1a7acbba10a7246ba58c1d53fc7203f5", 1446775, 501),
 
-	// 1998 PC/Mac re-release
-	MACGAME1("puppetmotel", "", "Puppet Motel Folder/Puppet Motel", "rt:04e7ccf432f8f968e6dd2282bf46c3a9", 812398, 501),
-	WINGAME1("puppetmotel", "", "PUPPET/PUPPET.EXE", "t:c43660296f937be289ffaa593b47b4a1", 1434927, 501),
+	// 1998 PC/Mac re-release - box has yellow "New Release" marking
+	MACGAME1("puppetmotel", "New Release", "Puppet Motel Folder/Puppet Motel", "rt:04e7ccf432f8f968e6dd2282bf46c3a9", 812398, 501),
+	WINGAME1("puppetmotel", "New Release", "PUPPET/PUPPET.EXE", "t:c43660296f937be289ffaa593b47b4a1", 1434927, 501),
 
 	MACGAME1_l("putlestory", "", "PutlestorY", "552992fb31c736ca67ffd403096596d6", 720654, Common::JA_JPN, 501),
 	WINGAME1t_l("putlestory", "", "PUTLE32.EXE", "74c72ab0fcdb4c69114047c74cef486e", 1403206, Common::JA_JPN, 501),




More information about the Scummvm-git-logs mailing list