[Scummvm-git-logs] scummvm master -> 523d31590e7cc84d1f149507160be56dd110e004

sev- noreply at scummvm.org
Wed May 22 15:24:39 UTC 2024


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

Summary:
5051d1fe9c DIRECTOR: LINGO: Fix list handler override bodge
9e76421275 DIRECTOR: Enable built-in debug logging in Virtual Nightclub
49a4648f6e DIRECTOR: Force Lctx loading after other resources
782ad3f28f DIRECTOR: LINGO: Add better logging for Datum equality/comparison
6819a99fcf DIRECTOR: XOBJ: Fix MMovie save dialog + return ticks
cf47a34a86 DIRECTOR: LINGO: Add POINT/RECT support to Lingo::setObjectProp
417106a9d0 DIRECTOR: LINGO: Fix typo in fetching CastScripts with b_script
fb1f453dc1 DIRECTOR: LINGO: Allow nonsense coercion of symbol to integer
9a822aa057 DIRECTOR: LINGO: Add more list edge cases
f673761135 DIRECTOR: LINGO: Fix b_count for RECT and POINT
6461bdcd25 DIRECTOR: Clean up array compare code, add more tests
337f214ff4 DIRECTOR: Move equality warnings behind debug gate
8e6ba00bc2 DIRECTOR: XOBJ: Fix MMovie to rename autosaves to something sensible
523d31590e DIRECTOR: Prevent exitFrame handler from running twice


Commit: 5051d1fe9c79f47eaf418a050a04e52cc192979b
    https://github.com/scummvm/scummvm/commit/5051d1fe9c79f47eaf418a050a04e52cc192979b
Author: Scott Percival (code at moral.net.au)
Date: 2024-05-22T17:24:31+02:00

Commit Message:
DIRECTOR: LINGO: Fix list handler override bodge

Fixes ARRAY/PARRAY log messages in Virtual Nightclub.

Changed paths:
    engines/director/lingo/lingo-builtins.cpp


diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index fafec6c46f8..22843d083d6 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -260,16 +260,16 @@ void Lingo::initBuiltIns(BuiltinProto protos[]) {
 		case CBLTIN:
 			_builtinCmds[blt->name] = sym;
 			break;
-		case FBLTIN:
 		case FBLTIN_LIST:
+			_builtinListHandlers[blt->name] = sym; // fall-through
+		case FBLTIN:
 			_builtinFuncs[blt->name] = sym;
-			_builtinListHandlers[blt->name] = sym;
 			break;
-		case HBLTIN:
 		case HBLTIN_LIST:
+			_builtinListHandlers[blt->name] = sym; // fall-through
+		case HBLTIN:
 			_builtinCmds[blt->name] = sym;
 			_builtinFuncs[blt->name] = sym;
-			_builtinListHandlers[blt->name] = sym;
 			break;
 		case KBLTIN:
 			_builtinConsts[blt->name] = sym;


Commit: 9e76421275771ad208191906d707215075666476
    https://github.com/scummvm/scummvm/commit/9e76421275771ad208191906d707215075666476
Author: Scott Percival (code at moral.net.au)
Date: 2024-05-22T17:24:31+02:00

Commit Message:
DIRECTOR: Enable built-in debug logging in Virtual Nightclub

Changed paths:
    engines/director/lingo/lingo-patcher.cpp


diff --git a/engines/director/lingo/lingo-patcher.cpp b/engines/director/lingo/lingo-patcher.cpp
index c2b91aacb08..0bbe9bf0478 100644
--- a/engines/director/lingo/lingo-patcher.cpp
+++ b/engines/director/lingo/lingo-patcher.cpp
@@ -330,6 +330,7 @@ const char *vncEnableCheats = " \
 on togCh\r\
   if getFlag(#cheats) then\r\
     setFlag(#cheats, 0)\r\
+	setMode(0) -- disable debug logging\r\
     set the foreColor of field \"viewName_cast\" to 255\r\
     alert(\"VNC Cheats off\")\r\
   else\r\
@@ -339,6 +340,7 @@ on togCh\r\
     set the foreColor of field \"viewName_cast\" to 172\r\
     set the textSize of field \"viewName_cast\" to 9\r\
     setFlag(#cheats)\r\
+	setMode(10) -- enable debug logging\r\
     alert(\"VNC Cheats on\")\r\
   end if\r\
 end\r\


Commit: 49a4648f6ebb4e255bf96d5232fcd13c2ced344f
    https://github.com/scummvm/scummvm/commit/49a4648f6ebb4e255bf96d5232fcd13c2ced344f
Author: Scott Percival (code at moral.net.au)
Date: 2024-05-22T17:24:31+02:00

Commit Message:
DIRECTOR: Force Lctx loading after other resources

It is possible for the bytecode compiler to call
getCastMemberByScriptId, which in turn can force a load() for a cast
member. If this cast member happens to be a TextCastMember, the text
will be missing.

Fixes the maze gallery minigame in Virtual Nightclub.

Changed paths:
    engines/director/cast.cpp


diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 538ec53b220..563f5c4ec9f 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -657,26 +657,6 @@ void Cast::loadCast() {
 	// set up the cache used for cast member name lookups.
 	rebuildCastNameCache();
 
-	// For D4+ we may request to force Lingo scripts and skip precompiled bytecode
-	if (_version >= kFileVer400 && !debugChannelSet(-1, kDebugNoBytecode)) {
-		// Try to load script context
-		if ((r = _castArchive->getMovieResourceIfPresent(MKTAG('L', 'c', 't', 'x'))) != nullptr) {
-			loadLingoContext(*r);
-			delete r;
-		}
-	}
-
-	// PICT resources
-	if (_castArchive->hasResource(MKTAG('P', 'I', 'C', 'T'), -1)) {
-		debug("STUB: Unhandled 'PICT' resource");
-	}
-
-	// External Cast Reference resources
-	// Used only by authoring tools for referring to the external casts
-	if (_castArchive->hasResource(MKTAG('S', 'C', 'R', 'F'), -1)) {
-		debugC(4, kDebugLoading, "'SCRF' resource skipped");
-	}
-
 	// Score Order List resources
 	if ((r = _castArchive->getMovieResourceIfPresent(MKTAG('S', 'o', 'r', 'd'))) != nullptr) {
 		loadSord(*r);
@@ -732,6 +712,27 @@ void Cast::loadCast() {
 		_loadedRTE2s.setVal(iterator, new RTE2(this, *r));
 		delete r;
 	}
+
+	// For D4+ we may request to force Lingo scripts and skip precompiled bytecode
+	if (_version >= kFileVer400 && !debugChannelSet(-1, kDebugNoBytecode)) {
+		// Try to load script context
+		if ((r = _castArchive->getMovieResourceIfPresent(MKTAG('L', 'c', 't', 'x'))) != nullptr) {
+			loadLingoContext(*r);
+			delete r;
+		}
+	}
+
+	// PICT resources
+	if (_castArchive->hasResource(MKTAG('P', 'I', 'C', 'T'), -1)) {
+		debug("STUB: Unhandled 'PICT' resource");
+	}
+
+	// External Cast Reference resources
+	// Used only by authoring tools for referring to the external casts
+	if (_castArchive->hasResource(MKTAG('S', 'C', 'R', 'F'), -1)) {
+		debugC(4, kDebugLoading, "'SCRF' resource skipped");
+	}
+
 }
 
 Common::String Cast::getLinkedPath(int castId) {


Commit: 782ad3f28f8c7f18cfa4b198d50af04ae8ec46c7
    https://github.com/scummvm/scummvm/commit/782ad3f28f8c7f18cfa4b198d50af04ae8ec46c7
Author: Scott Percival (code at moral.net.au)
Date: 2024-05-22T17:24:31+02:00

Commit Message:
DIRECTOR: LINGO: Add better logging for Datum equality/comparison

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


diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index ef9d027ace0..ee1eb2d3d23 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -1348,6 +1348,7 @@ int Datum::equalTo(Datum &d, bool ignoreCase) const {
 	case PICTUREREF:
 		return 0; // Original always returns 0 on picture reference comparison
 	default:
+		warning("Datum::equalTo(): Invalid equality check between types %s and %s", type2str(), d.type2str());
 		break;
 	}
 	return 0;
@@ -1424,7 +1425,7 @@ uint32 Datum::compareTo(Datum &d) const {
 			return kCompareGreater;
 		}
 	} else {
-		warning("Invalid comparison between types %s and %s", type2str(), d.type2str());
+		warning("Datum::compareTo(): Invalid comparison between types %s and %s", type2str(), d.type2str());
 		return kCompareError;
 	}
 }


Commit: 6819a99fcf90037518289019c20339bacbd798fe
    https://github.com/scummvm/scummvm/commit/6819a99fcf90037518289019c20339bacbd798fe
Author: Scott Percival (code at moral.net.au)
Date: 2024-05-22T17:24:31+02:00

Commit Message:
DIRECTOR: XOBJ: Fix MMovie save dialog + return ticks

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


diff --git a/engines/director/lingo/xlibs/mmovie.cpp b/engines/director/lingo/xlibs/mmovie.cpp
index 02eaf8e8c11..b6fd56cdcde 100644
--- a/engines/director/lingo/xlibs/mmovie.cpp
+++ b/engines/director/lingo/xlibs/mmovie.cpp
@@ -27,6 +27,7 @@
 #include "video/qt_decoder.h"
 
 #include "director/director.h"
+#include "director/movie.h"
 #include "director/lingo/lingo.h"
 #include "director/lingo/lingo-object.h"
 #include "director/lingo/lingo-utils.h"
@@ -127,9 +128,11 @@ MMovieXObject::~MMovieXObject() {
 	}
 }
 
-bool MMovieXObject::playSegment(int movieIndex, int segIndex, bool looping, bool restore, bool shiftAbort, bool abortOnClick, bool purge, bool async) {
+int MMovieXObject::playSegment(int movieIndex, int segIndex, bool looping, bool restore, bool shiftAbort, bool abortOnClick, bool purge, bool async) {
+	int result = MMovieError::MMOVIE_INVALID_MOVIE_INDEX;
 	if (_movies.contains(movieIndex)) {
 		MMovieFile &movie = _movies.getVal(movieIndex);
+		result = MMovieError::MMOVIE_INDEX_OUT_OF_RANGE;
 		if (segIndex <= (int)movie.segments.size() && segIndex > 0) {
 			MMovieSegment &segment = movie.segments[segIndex - 1];
 			_currentMovieIndex = movieIndex;
@@ -143,15 +146,13 @@ bool MMovieXObject::playSegment(int movieIndex, int segIndex, bool looping, bool
 			debugC(5, kDebugXObj, "MMovieXObject::playSegment(): hitting play on movie %s (%d) segment %s (%d) - %d", movie._path.toString().c_str(), movieIndex, segment._name.c_str(), segIndex, segment._start);
 			movie._video->seek(Audio::Timestamp(0, segment._start, movie._video->getTimeScale()));
 			movie._video->start();
-
+			result = MMovieError::MMOVIE_NONE;
 			if (!_async) {
-				updateScreenBlocking();
+				result = updateScreenBlocking();
 			}
-
-			return true;
 		}
 	}
-	return false;
+	return result;
 }
 
 bool MMovieXObject::stopSegment() {
@@ -169,7 +170,8 @@ bool MMovieXObject::stopSegment() {
 	return false;
 }
 
-void MMovieXObject::updateScreenBlocking() {
+int MMovieXObject::updateScreenBlocking() {
+	MMovieError result = MMovieError::MMOVIE_PLAYBACK_FINISHED;
 	while (_currentMovieIndex && _currentSegmentIndex) {
 		Common::Event event;
 		bool keepPlaying = true;
@@ -181,8 +183,10 @@ void MMovieXObject::updateScreenBlocking() {
 			case Common::EVENT_KEYDOWN:
 			case Common::EVENT_RBUTTONDOWN:
 			case Common::EVENT_LBUTTONDOWN:
-				if (_abortOnClick)
+				if (_abortOnClick) {
+					result = MMovieError::MMOVIE_ABORT_DOUBLE_CLICK;
 					keepPlaying = false;
+				}
 				break;
 			default:
 				break;
@@ -195,17 +199,20 @@ void MMovieXObject::updateScreenBlocking() {
 			break;
 		updateScreen();
 	}
+	return result;
 }
 
-void MMovieXObject::updateScreen() {
+int MMovieXObject::updateScreen() {
+	int result = MMovieError::MMOVIE_CONTINUE_WITHOUT_PLAYING;
 	if (_currentMovieIndex) {
 		MMovieFile &movie = _movies.getVal(_currentMovieIndex);
 		if (_currentSegmentIndex) {
 			MMovieSegment &seg = movie.segments[_currentSegmentIndex - 1];
+			result = getTicks();
 			if (movie._video && movie._video->isPlaying() && movie._video->needsUpdate()) {
 				const Graphics::Surface *frame = movie._video->decodeNextFrame();
-				if (frame) {
-					debugC(8, kDebugXObj, "MMovieXObject: rendering movie %s (%d), time %d", movie._path.toString().c_str(), _currentMovieIndex, movie._video->getTime());
+				if (frame && !_bounds.isEmpty()) {
+					debugC(8, kDebugXObj, "MMovieXObject: rendering movie %s (%d), ticks %d", movie._path.toString().c_str(), _currentMovieIndex, getTicks());
 					Graphics::Surface *temp1 = frame->scale(_bounds.width(), _bounds.height(), false);
 					Graphics::Surface *temp2 = temp1->convertTo(g_director->_pixelformat, movie._video->getPalette());
 					_lastFrame.copyFrom(*temp2);
@@ -215,33 +222,38 @@ void MMovieXObject::updateScreen() {
 					delete temp1;
 				}
 			}
-			g_system->copyRectToScreen(_lastFrame.getPixels(), _lastFrame.pitch, _bounds.left, _bounds.top, _bounds.width(), _bounds.height());
+			if (!_bounds.isEmpty())
+				g_system->copyRectToScreen(_lastFrame.getPixels(), _lastFrame.pitch, _bounds.left, _bounds.top, _bounds.width(), _bounds.height());
 			// do a time check
 			uint32 endTime = Audio::Timestamp(0, seg._length + seg._start, movie._video->getTimeScale()).msecs();
-			debugC(8, kDebugXObj, "MMovieXObject::updateScreen(): time: %d, endTime: %d", movie._video->getTime(), endTime);
+			debugC(8, kDebugXObj, "MMovieXObject::updateScreen(): time: %d, endTime: %d, ticks: %d, endTicks: %d", movie._video->getTime(), endTime, getTicks(), seg._length + seg._start);
 			if (movie._video->getTime() >= endTime) {
 				if (_looping) {
-					debugC(5, kDebugXObj, "MMovieXObject::updateScreen(): rewinding loop on %s (%d), time %d", movie._path.toString().c_str(), _currentMovieIndex, movie._video->getTime());
+					debugC(5, kDebugXObj, "MMovieXObject::updateScreen(): rewinding loop on %s (%d), time: %d, ticks: %d", movie._path.toString().c_str(), _currentMovieIndex, movie._video->getTime(), getTicks());
 					movie._video->seek(Audio::Timestamp(0, seg._start, movie._video->getTimeScale()));
 				} else {
-					debugC(5, kDebugXObj, "MMovieXObject::updateScreen(): stopping %s (%d), time %d", movie._path.toString().c_str(), _currentMovieIndex, movie._video->getTime());
+					debugC(5, kDebugXObj, "MMovieXObject::updateScreen(): stopping %s (%d), time: %d, ticks: %d", movie._path.toString().c_str(), _currentMovieIndex, movie._video->getTime(), getTicks());
 					stopSegment();
+					result = MMovieError::MMOVIE_PLAYBACK_FINISHED;
 				}
 			}
 		}
 	}
 	g_system->updateScreen();
 	g_director->delayMillis(10);
+	return result;
 }
 
 int MMovieXObject::getTicks() {
-	if (_currentMovieIndex) {
+	if (_currentMovieIndex && _currentSegmentIndex) {
 		MMovieFile &movie = _movies.getVal(_currentMovieIndex);
+		MMovieSegment &segment = movie.segments[_currentSegmentIndex - 1];
 		if (movie._video) {
-			return movie._video->getTime() * 60 / 1000;
+			_lastTicks = movie._video->getTime() - Audio::Timestamp(0, segment._start, movie._video->getTimeScale()).msecs();
+			_lastTicks = _lastTicks * 60 / 1000;
 		}
 	}
-	return -1;
+	return _lastTicks;
 }
 
 void MMovieXObj::open(ObjectType type, const Common::Path &path) {
@@ -346,7 +358,7 @@ void MMovieXObj::m_closeMMovie(int nargs) {
 		file._video = nullptr;
 	}
 	me->_movies.erase(index);
-	g_lingo->push(0);
+	g_lingo->push(Datum(MMovieError::MMOVIE_NONE));
 }
 
 void MMovieXObj::m_playSegment(int nargs) {
@@ -368,18 +380,14 @@ void MMovieXObj::m_playSegment(int nargs) {
 	bool abortOnClick = abortOpt.equalsIgnoreCase("abortOnClick");
 	bool purge = purgeOpt.equalsIgnoreCase("purge");
 	bool async = asyncOpt.equalsIgnoreCase("async");
-
 	MMovieXObject *me = static_cast<MMovieXObject *>(g_lingo->_state->me.u.obj);
 	for (auto &it : me->_movies) {
 		if (it._value.segLookup.contains(segmentName)) {
 			int segIndex = it._value.segLookup.getVal(segmentName);
-			if (!me->playSegment(it._key, segIndex, false, restore, shiftAbort, abortOnClick, purge, async)) {
-				g_lingo->push(MMovieError::MMOVIE_INDEX_OUT_OF_RANGE);
-				return;
-			}
-			int result = me->getTicks();
-			debugC(5, kDebugXObj, "MMovieXObj::m_playSegment: ticks: %d", result);
-			g_lingo->push(0);
+			int result = me->playSegment(it._key, segIndex, false, restore, shiftAbort, abortOnClick, purge, async);
+			int ticks = me->getTicks();
+			debugC(5, kDebugXObj, "MMovieXObj::m_playSegment: ticks: %d, result: %d", ticks, result);
+			g_lingo->push(Datum(result));
 			return;
 		}
 	}
@@ -411,10 +419,10 @@ void MMovieXObj::m_playSegLoop(int nargs) {
 	for (auto &it : me->_movies) {
 		if (it._value.segLookup.contains(segmentName)) {
 			int segIndex = it._value.segLookup.getVal(segmentName);
-			me->playSegment(it._key, segIndex, true, restore, shiftAbort, abortOnClick, purge, async);
-			int result = me->getTicks();
-			debugC(5, kDebugXObj, "MMovieXObj::m_playSegLoop: ticks: %d", result);
-			g_lingo->push(0);
+			int result = me->playSegment(it._key, segIndex, true, restore, shiftAbort, abortOnClick, purge, async);
+			int ticks = me->getTicks();
+			debugC(5, kDebugXObj, "MMovieXObj::m_playSegLoop: ticks: %d, result: %d", ticks, result);
+			g_lingo->push(Datum(result));
 			return;
 		}
 	}
@@ -427,10 +435,11 @@ void MMovieXObj::m_idleSegment(int nargs) {
 		g_lingo->dropStack(nargs);
 	}
 	MMovieXObject *me = static_cast<MMovieXObject *>(g_lingo->_state->me.u.obj);
-	me->updateScreen();
-	int result = me->getTicks();
-	debugC(5, kDebugXObj, "MMovieXObj::m_idleSegment(): ticks: %d", result);
-	g_lingo->push(result);
+	int result = me->updateScreen();
+	int ticks = me->getTicks();
+	debugC(5, kDebugXObj, "MMovieXObj::m_idleSegment(): ticks: %d, result: %d", ticks, result);
+
+	g_lingo->push(Datum(result));
 }
 
 void MMovieXObj::m_stopSegment(int nargs) {
@@ -447,17 +456,18 @@ void MMovieXObj::m_seekSegment(int nargs) {
 	g_lingo->printArgs("MMovieXObj::m_seekSegment", nargs);
 	if (nargs != 1) {
 		g_lingo->dropStack(nargs);
-		g_lingo->push(MMovieError::MMOVIE_INVALID_SEGMENT_NAME);
+		g_lingo->push(Datum(MMovieError::MMOVIE_INVALID_SEGMENT_NAME));
 		return;
 	}
 	Common::String segmentName = g_lingo->pop().asString();
 	MMovieXObject *me = static_cast<MMovieXObject *>(g_lingo->_state->me.u.obj);
 	for (auto &it : me->_movies) {
 		if (it._value.segLookup.contains(segmentName)) {
-
+			g_lingo->push(Datum(MMovieError::MMOVIE_NONE));
+			return;
 		}
 	}
-
+	g_lingo->push(Datum(MMovieError::MMOVIE_INVALID_SEGMENT_NAME));
 }
 
 
@@ -480,6 +490,8 @@ void MMovieXObj::m_setDisplayBounds(int nargs) {
 	me->_bounds = Common::Rect((int16)left.asInt(), (int16)top.asInt(), (int16)right.asInt(), (int16)bottom.asInt());
 	me->_lastFrame.free();
 	me->_lastFrame.create(me->_bounds.width(), me->_bounds.height(), g_director->_pixelformat);
+	Common::Rect screen = g_director->getCurrentMovie()->_movieRect;
+	me->_bounds.clip(Common::Rect(screen.width(), screen.height()));
 	g_lingo->push(Datum(0));
 }
 
@@ -627,7 +639,7 @@ void MMovieXObj::m_writeFile(int nargs) {
 
 	Common::String prefix = savePrefix();
 	if (origPath.empty()) {
-		path = getFileNameFromModal(false, Common::String(), "txt");
+		path = getFileNameFromModal(true, Common::String(), "txt");
 		if (path.empty()) {
 			debugC(5, kDebugXObj, "MMovieXObj::m_writeFile(): read cancelled by modal");
 			g_lingo->push(result);
diff --git a/engines/director/lingo/xlibs/mmovie.h b/engines/director/lingo/xlibs/mmovie.h
index 467991e479b..a704e59bcff 100644
--- a/engines/director/lingo/xlibs/mmovie.h
+++ b/engines/director/lingo/xlibs/mmovie.h
@@ -28,6 +28,7 @@ namespace Director {
 
 // taken from shared:1124:mHandleError
 enum MMovieError {
+	MMOVIE_NONE = 0,
 	MMOVIE_NO_STAGE = -1,
 	MMOVIE_TOO_MANY_OPEN_FILES = -2,
 	MMOVIE_MOVIE_ALREADY_OPEN = -3,
@@ -39,6 +40,7 @@ enum MMovieError {
 	MMOVIE_INDEX_OUT_OF_RANGE = -9,
 	MMOVIE_CONTINUE_WITHOUT_PLAYING = -10,
 	MMOVIE_ABORT_DOUBLE_CLICK = -11,
+	MMOVIE_PLAYBACK_FINISHED = -12,
 };
 
 struct MMovieSegment {
@@ -75,16 +77,17 @@ public:
 	bool _abortOnClick = false;
 	bool _purge = false;
 	bool _async = false;
+	int _lastTicks = -1;
 
 	Common::HashMap<int, MMovieFile> _movies;
 	Common::HashMap<Common::String, int, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _moviePathMap;
 
 	Graphics::Surface _lastFrame;
 
-	bool playSegment(int movieIndex, int segIndex, bool looping, bool restore, bool shiftAbort, bool abortOnClick, bool purge, bool async);
+	int playSegment(int movieIndex, int segIndex, bool looping, bool restore, bool shiftAbort, bool abortOnClick, bool purge, bool async);
 	bool stopSegment();
-	void updateScreenBlocking();
-	void updateScreen();
+	int updateScreenBlocking();
+	int updateScreen();
 	int getTicks();
 };
 


Commit: cf47a34a86813815e7c2ac30d01bcaee6d7edf43
    https://github.com/scummvm/scummvm/commit/cf47a34a86813815e7c2ac30d01bcaee6d7edf43
Author: Scott Percival (code at moral.net.au)
Date: 2024-05-22T17:24:31+02:00

Commit Message:
DIRECTOR: LINGO: Add POINT/RECT support to Lingo::setObjectProp

Changed paths:
    engines/director/lingo/lingo-the.cpp


diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index d512cdfde4f..aba6e883705 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -2097,6 +2097,30 @@ void Lingo::setObjectProp(Datum &obj, Common::String &propName, Datum &val) {
 			obj.u.parr->arr.push_back(cell);
 		}
 		g_debugger->propWriteHook(propName);
+	} else if (obj.type == POINT) {
+		if (propName.equalsIgnoreCase("locH")) {
+			obj.u.farr->arr[0] = val.asInt();
+		} else if (propName.equalsIgnoreCase("locV")) {
+			obj.u.farr->arr[1] = val.asInt();
+		} else {
+			g_lingo->lingoError("Lingo::setObjectProp: Point <%s> has no property '%s'", obj.asString(true).c_str(), propName.c_str());
+		}
+		g_debugger->propWriteHook(propName);
+		return;
+	} else if (obj.type == RECT) {
+		if (propName.equalsIgnoreCase("left")) {
+			obj.u.farr->arr[0] = val.asInt();
+		} else if (propName.equalsIgnoreCase("top")) {
+			obj.u.farr->arr[1] = val.asInt();
+		} else if (propName.equalsIgnoreCase("right")) {
+			obj.u.farr->arr[2] = val.asInt();
+		} else if (propName.equalsIgnoreCase("bottom")) {
+			obj.u.farr->arr[3] = val.asInt();
+		} else {
+			g_lingo->lingoError("Lingo::setObjectProp: Rect <%s> has no property '%s'", obj.asString(true).c_str(), propName.c_str());
+		}
+		g_debugger->propWriteHook(propName);
+		return;
 	} else if (obj.type == CASTREF) {
 		Movie *movie = _vm->getCurrentMovie();
 		if (!movie) {


Commit: 417106a9d0cb07a788684c9943e0a8e3153c19e2
    https://github.com/scummvm/scummvm/commit/417106a9d0cb07a788684c9943e0a8e3153c19e2
Author: Scott Percival (code at moral.net.au)
Date: 2024-05-22T17:24:31+02:00

Commit Message:
DIRECTOR: LINGO: Fix typo in fetching CastScripts with b_script

Changed paths:
    engines/director/lingo/lingo-builtins.cpp


diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 22843d083d6..53b4bd66fb3 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -3368,7 +3368,7 @@ void LB::b_script(int nargs) {
 			if (!script)
 				script = g_director->getCurrentMovie()->getScriptContext(kParentScript, memberID);
 		} else {
-			g_director->getCurrentMovie()->getScriptContext(kCastScript, memberID);
+			script = g_director->getCurrentMovie()->getScriptContext(kCastScript, memberID);
 		}
 
 		if (script) {


Commit: fb1f453dc10616f85587fbb58e86ebcd8c0ac663
    https://github.com/scummvm/scummvm/commit/fb1f453dc10616f85587fbb58e86ebcd8c0ac663
Author: Scott Percival (code at moral.net.au)
Date: 2024-05-22T17:24:31+02:00

Commit Message:
DIRECTOR: LINGO: Allow nonsense coercion of symbol to integer

Fixes the collision detection loop in the maze minigame of Virtual
Nightclub.

Changed paths:
    engines/director/lingo/lingo.cpp
    engines/director/lingo/tests/strings.lingo


diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index ee1eb2d3d23..8f0722bb2d7 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -833,6 +833,9 @@ int Lingo::getAlignedType(const Datum &d1, const Datum &d2, bool equality) {
 		opType = FLOAT;
 	} else if ((d1Type == STRING && d2Type == INT) || (d1Type == INT && d2Type == STRING)) {
 		opType = STRING;
+	} else if ((d1Type == SYMBOL && d2Type != SYMBOL) || (d2Type == SYMBOL && d1Type != SYMBOL)) {
+		// some fun undefined behaviour: adding anything to a symbol returns an int.
+		opType = INT;
 	} else if (d1Type == d2Type) {
 		opType = d1Type;
 	}
@@ -1041,6 +1044,11 @@ int Datum::asInt() const {
 			res = (int)u.f;
 		}
 		break;
+	case SYMBOL:
+		// Undefined behaviour, but relied on by bad game code that e.g. adds things to symbols.
+		// Return a 32-bit number that's sort of related.
+		res = (int)((uint64)u.s & 0xffffffffL);
+		break;
 	default:
 		warning("Incorrect operation asInt() for type: %s", type2str());
 	}
diff --git a/engines/director/lingo/tests/strings.lingo b/engines/director/lingo/tests/strings.lingo
index 36f32e612c5..1787d750d4f 100644
--- a/engines/director/lingo/tests/strings.lingo
+++ b/engines/director/lingo/tests/strings.lingo
@@ -80,6 +80,21 @@ scummvmAssert("incorrect" + 5 > 10000)
 scummvmAssert("    2.5     " + 5 > 10000)
 scummvmAssert("2 uhhh" + 5 > 10000)
 scummvmAssert("2.5 uhhh" + 5 > 10000)
+put "sausages" into testString
+put (testString + 0) into testPointer
+scummvmAssertEqual(testPointer > 10000, TRUE)
+scummvmAssertEqual(testString + 4, testPointer + 4)
+scummvmAssertEqual(testString - 4, testPointer - 4)
+scummvmAssertEqual(testString * 4, testPointer * 4)
+scummvmAssertEqual(testString / 4, testPointer / 4)
+-- same horrible logic should apply to symbols
+put #haggis into testString
+put (testString + 0) into testPointer
+scummvmAssertEqual(testPointer > 10000, TRUE)
+scummvmAssertEqual(testString + 4, testPointer + 4)
+scummvmAssertEqual(testString - 4, testPointer - 4)
+scummvmAssertEqual(testString * 4, testPointer * 4)
+scummvmAssertEqual(testString / 4, testPointer / 4)
 
 -- casting to integer
 scummvmAssertEqual(integer("2"), 2)
@@ -109,14 +124,6 @@ scummvmAssertEqual(float("     2.5 extra"), "     2.5 extra")
 scummvmAssertEqual(float("2extra"), "2extra")
 scummvmAssertEqual(float("     2extra"), "     2extra")
 
-put "sausages" into testString
-put (testString + 0) into testPointer
-scummvmAssertEqual(testPointer > 10000, TRUE)
-scummvmAssertEqual(testString + 4, testPointer + 4)
-scummvmAssertEqual(testString - 4, testPointer - 4)
-scummvmAssertEqual(testString * 4, testPointer * 4)
-scummvmAssertEqual(testString / 4, testPointer / 4)
-
 
 -- LC::charOF
 set string to "Macromedia"


Commit: 9a822aa057ffe1550bdfc0992286f2cfa4de1cc0
    https://github.com/scummvm/scummvm/commit/9a822aa057ffe1550bdfc0992286f2cfa4de1cc0
Author: Scott Percival (code at moral.net.au)
Date: 2024-05-22T17:24:31+02:00

Commit Message:
DIRECTOR: LINGO: Add more list edge cases

Changed paths:
    engines/director/lingo/lingo-builtins.cpp
    engines/director/lingo/tests/lists.lingo


diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 53b4bd66fb3..69e460a9945 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -719,7 +719,6 @@ void LB::b_deleteAt(int nargs) {
 void LB::b_deleteOne(int nargs) {
 	Datum val = g_lingo->pop();
 	Datum list = g_lingo->pop();
-	TYPECHECK3(val, INT, FLOAT, SYMBOL);
 	TYPECHECK2(list, ARRAY, PARRAY);
 
 	switch (list.type) {
diff --git a/engines/director/lingo/tests/lists.lingo b/engines/director/lingo/tests/lists.lingo
index 55ea56482d4..83226174d92 100644
--- a/engines/director/lingo/tests/lists.lingo
+++ b/engines/director/lingo/tests/lists.lingo
@@ -155,3 +155,30 @@ set the mood of templst to 2
 set tempmood to the mood of templst
 scummvmAssert(tempmood = 2)
 put templst
+
+-- deleteAt
+set testList to [1, 2, 3, 4, 5]
+deleteAt testList, 3
+scummvmAssert(testList = [1, 2, 4, 5])
+set testList to [#a: 1, #b: 2, #c: 3, #d: 4, #e: 5]
+deleteAt testList, 3
+scummvmAssert(testList = [#a: 1, #b: 2, #d: 4, #e: 5])
+
+-- deleteOne
+set testList to [5, 4, 3, 1, 4]
+deleteOne testList, 4
+scummvmAssert(testList = [5, 3, 1, 4])
+set testlist to [5, "4.0", 3, 1, 4]
+deleteOne testList, 4
+scummvmAssert(testList = [5, 3, 1, 4])
+set testlist to [5, 4.0, 3, 1, 4]
+deleteOne testList, 4
+scummvmAssert(testList = [5, 3, 1, 4])
+set testlist to [5, "urgh", 3, 1, "urgh"]
+deleteOne testList, "urgh"
+scummvmAssert(testList = [5, 3, 1, "urgh"])
+set testlist to [5, "URGH", 3, 1, "urgh"]
+deleteOne testList, "urgh"
+scummvmAssert(testList = [5, "URGH", 3, 1])
+
+


Commit: f673761135095ff6ed9e2e2370591a54fcb035a6
    https://github.com/scummvm/scummvm/commit/f673761135095ff6ed9e2e2370591a54fcb035a6
Author: Scott Percival (code at moral.net.au)
Date: 2024-05-22T17:24:31+02:00

Commit Message:
DIRECTOR: LINGO: Fix b_count for RECT and POINT

Changed paths:
    engines/director/lingo/lingo-builtins.cpp
    engines/director/lingo/lingo-code.cpp
    engines/director/lingo/tests/listoverride.lingo


diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 69e460a9945..614c289fbaa 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -682,6 +682,8 @@ void LB::b_count(int nargs) {
 
 	switch (list.type) {
 	case ARRAY:
+	case RECT:
+	case POINT:
 		result.u.i = list.u.farr->arr.size();
 		break;
 	case PARRAY:
diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 12e58cd544a..c7a81f24ccb 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -1596,7 +1596,8 @@ void LC::call(const Common::String &name, int nargs, bool allowRetVal) {
 		// If the first argument is an ARRAY or PARRAY, it will use the builtin.
 		// Otherwise, it will fall back to whatever handler is defined globally.
 		Datum firstArg = g_lingo->peek(nargs - 1);
-		if (firstArg.type == ARRAY || firstArg.type == PARRAY) {
+		if (firstArg.type == ARRAY || firstArg.type == PARRAY ||
+				firstArg.type == POINT || firstArg.type == RECT) {
 			funcSym = g_lingo->_builtinListHandlers[name];
 		}
 	}
diff --git a/engines/director/lingo/tests/listoverride.lingo b/engines/director/lingo/tests/listoverride.lingo
index 6b3c22aa114..bf1378cb6cc 100644
--- a/engines/director/lingo/tests/listoverride.lingo
+++ b/engines/director/lingo/tests/listoverride.lingo
@@ -13,6 +13,12 @@ scummvmAssertEqual(result, 3)
 set result = count([1: 2, 3: 4, 5: 6])
 scummvmAssertEqual(result, 3)
 
+set result = count(rect(1, 2, 3, 4))
+scummvmAssertEqual(result, 4)
+
+set result = count(point(1, 2))
+scummvmAssertEqual(result, 2)
+
 set result = count("not an array")
 scummvmAssertEqual(result, "what is this")
 
@@ -22,7 +28,7 @@ scummvmAssertEqual(result, "this is the worst")
 set result = add("even less of an array", 8)
 scummvmAssertEqual(result, "this is the worst")
 
-set target = [1, 2, 3] 
+set target = [1, 2, 3]
 add(target, 4)
 scummvmAssertEqual(count(target), 4)
 


Commit: 6461bdcd25ba00e7331c78ab620dd2c6781cf141
    https://github.com/scummvm/scummvm/commit/6461bdcd25ba00e7331c78ab620dd2c6781cf141
Author: Scott Percival (code at moral.net.au)
Date: 2024-05-22T17:24:31+02:00

Commit Message:
DIRECTOR: Clean up array compare code, add more tests

Changed paths:
    engines/director/lingo/lingo-builtins.cpp
    engines/director/lingo/lingo-code.cpp
    engines/director/lingo/lingo-code.h
    engines/director/lingo/lingo-utils.h
    engines/director/lingo/tests/equality.lingo
    engines/director/lingo/tests/lists.lingo


diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 614c289fbaa..6c850f5549e 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -920,7 +920,7 @@ void LB::b_getPos(int nargs) {
 	switch (list.type) {
 	case ARRAY: {
 		Datum d(0);
-		int index = LC::compareArrays(LC::eqData, list, val, true).u.i;
+		int index = LC::compareArrays(LC::eqDataStrict, list, val, true).u.i;
 		if (index > 0) {
 			d.u.i = index;
 		}
@@ -929,7 +929,7 @@ void LB::b_getPos(int nargs) {
 	}
 	case PARRAY: {
 		Datum d(0);
-		int index = LC::compareArrays(LC::eqData, list, val, true, true).u.i;
+		int index = LC::compareArrays(LC::eqDataStrict, list, val, true, true).u.i;
 		if (index > 0) {
 			d.u.i = index;
 		}
@@ -947,16 +947,21 @@ void LB::b_getProp(int nargs) {
 
 	switch (list.type) {
 	case ARRAY:
-		g_lingo->push(list);
-		g_lingo->push(prop);
-		b_getPos(nargs);
+		if (g_director->getVersion() < 500) {
+			// D4 allows getProp to be called on ARRAYs
+			g_lingo->push(list);
+			g_lingo->push(prop);
+			b_getAt(nargs);
+		} else {
+			g_lingo->lingoError("BUILDBOT: b_getProp: Attempted to call on an ARRAY");
+		}
 		break;
 	case PARRAY: {
 		int index = LC::compareArrays(LC::eqData, list, prop, true).u.i;
 		if (index > 0) {
 			g_lingo->push(list.u.parr->arr[index - 1].v);
 		} else {
-			error("b_getProp: Property %s not found", prop.asString().c_str());
+			g_lingo->lingoError("BUILDBOT: b_getProp: Property %s not found", prop.asString().c_str());
 		}
 		break;
 	}
@@ -2968,8 +2973,8 @@ void LB::b_updateStage(int nargs) {
 // Point
 ///////////////////
 void LB::b_point(int nargs) {
-	Datum y(g_lingo->pop().asFloat());
-	Datum x(g_lingo->pop().asFloat());
+	Datum y(g_lingo->pop().asInt());
+	Datum x(g_lingo->pop().asInt());
 	Datum d;
 
 	d.u.farr = new FArray;
diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index c7a81f24ccb..a91a9ef40eb 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -660,13 +660,13 @@ void LC::c_swap() {
 }
 
 static DatumType getArrayAlignedType(Datum &d1, Datum &d2) {
-	if (d1.type == POINT && d2.type == ARRAY && d2.u.farr->arr.size() < 2)
+	if (d1.type == POINT && (d2.type == RECT || (d2.type == ARRAY && d2.u.farr->arr.size() != 2)))
 		return ARRAY;
 
 	if (d1.type == POINT)
 		return POINT;
 
-	if (d1.type == RECT && (d2.type == POINT || (d2.type == ARRAY && d2.u.farr->arr.size() < 4)))
+	if (d1.type == RECT && (d2.type == POINT || (d2.type == ARRAY && d2.u.farr->arr.size() != 4)))
 		return ARRAY;
 
 	if (d1.type == RECT)
@@ -1263,23 +1263,17 @@ void LC::c_not() {
 
 Datum LC::compareArrays(Datum (*compareFunc)(Datum, Datum), Datum d1, Datum d2, bool location, bool value) {
 	// At least one of d1 and d2 must be an array
-	uint arraySize;
-	if (d1.isArray() && d2.isArray()) {
-		arraySize = MIN(d1.u.farr->arr.size(), d2.u.farr->arr.size());
-	} else if (d1.type == PARRAY && d2.type == PARRAY) {
-		arraySize = MIN(d1.u.parr->arr.size(), d2.u.parr->arr.size());
-	} else if (d1.isArray()) {
-		arraySize = d1.u.farr->arr.size();
-	} else if (d1.type == PARRAY) {
-		arraySize = d1.u.parr->arr.size();
-	} else if (d2.isArray()) {
-		arraySize = d2.u.farr->arr.size();
-	} else if (d2.type == PARRAY) {
-		arraySize = d2.u.parr->arr.size();
-	} else {
-		warning("LC::compareArrays(): Called with wrong data types: %s and %s", d1.type2str(), d2.type2str());
-		return Datum(0);
-	}
+	bool d1isArr = d1.isArray() || d1.type == PARRAY;
+	bool d2isArr = d2.isArray() || d2.type == PARRAY;
+	uint32 d1size = d1.isArray() ? d1.u.farr->arr.size() : d1.type == PARRAY ? d1.u.parr->arr.size() : 0;
+	uint32 d2size = d2.isArray() ? d2.u.farr->arr.size() : d2.type == PARRAY ? d2.u.parr->arr.size() : 0;
+	// The calling convention of this checking function is a bit weird:
+	// - If the location flag is set, you're searching for element d2 in list d1
+	// - If the location flag is not set and there are two array-like arguments passed, you are comparing
+	//   elements and therefore need to truncate output to the smaller size.
+	// - Otherwise, you are comparing an array to a single element, and all elements
+	//   of the array need to be checked.
+	uint arraySize = location ? d1size : ((d1isArr && d2isArr) ? MIN(d1size, d2size) : MAX(d1size, d2size));
 
 	Datum res;
 	res = location ? -1 : 1;
@@ -1293,9 +1287,9 @@ Datum LC::compareArrays(Datum (*compareFunc)(Datum, Datum), Datum d1, Datum d2,
 			a = value ? t.v : t.p;
 		}
 
-		if (d2.isArray()) {
+		if (!location && d2.isArray()) {
 			b = d2.u.farr->arr[i];
-		} else if (d2.type == PARRAY) {
+		} else if (!location && d2.type == PARRAY) {
 			PCell t = d2.u.parr->arr[i];
 			b = value ? t.v : t.p;
 		}
@@ -1309,7 +1303,6 @@ Datum LC::compareArrays(Datum (*compareFunc)(Datum, Datum), Datum d1, Datum d2,
             b = Datum(b.asString());
         }
 
-
 		res = compareFunc(a, b);
 		if (!location) {
 			if (res.u.i == 0) {
@@ -1327,27 +1320,25 @@ Datum LC::compareArrays(Datum (*compareFunc)(Datum, Datum), Datum d1, Datum d2,
 }
 
 Datum LC::eqData(Datum d1, Datum d2) {
-	if ((d1.isArray() && d2.isArray()) || (d1.type == PARRAY && d2.type == PARRAY)) {
-		// D4 has a bug, and only checks the elements on the left array.
-		// Therefore if the left array is bigger, don't bother checking.
-		// LC::compareArrays will trim the inputs to the shortest length.
-		// (Mac 4.0.4 is fixed, Win 4.0.4 is not)
-		bool hasArrayBug = (g_director->getVersion() < 500 && g_director->getPlatform() == Common::kPlatformWindows) ||
-			(g_director->getVersion() < 404 && g_director->getPlatform() == Common::kPlatformMacintosh);
-		if (hasArrayBug &&
-			d1.u.farr->arr.size() > d2.u.farr->arr.size()) {
+	// D4 has a bug, and only checks the elements on the left array.
+	// Therefore if the left array is bigger, don't bother checking.
+	// LC::compareArrays will trim the inputs to the shortest length.
+	// (Mac 4.0.4 is fixed, Win 4.0.4 is not)
+	bool hasArrayBug = (g_director->getVersion() < 500 && g_director->getPlatform() == Common::kPlatformWindows) ||
+		(g_director->getVersion() < 404 && g_director->getPlatform() == Common::kPlatformMacintosh);
+
+	if (d1.isArray() || d2.isArray() || d1.type == PARRAY || d2.type == PARRAY) {
+		bool d1isArr = d1.isArray() || d1.type == PARRAY;
+		bool d2isArr = d2.isArray() || d2.type == PARRAY;
+		uint32 d1size = d1.isArray() ? d1.u.farr->arr.size() : d1.type == PARRAY ? d1.u.parr->arr.size() : 0;
+		uint32 d2size = d2.isArray() ? d2.u.farr->arr.size() : d2.type == PARRAY ? d2.u.parr->arr.size() : 0;
+		if (hasArrayBug && d1isArr && d2isArr && d1size > d2size) {
+			// D4; only check arrays if the left size is less than or equal to the right side
 			return Datum(0);
-		} else if (!hasArrayBug && d1.u.farr->arr.size() != d2.u.farr->arr.size()) {
+		} else if (!hasArrayBug && d1isArr && d2isArr && d1size != d2size) {
 			// D5 and up is fixed; only check arrays if the sizes are the same.
 			return Datum(0);
 		}
-	}
-	if (d1.type == PARRAY && d2.type == PARRAY &&
-			d1.u.parr->arr.size() != d2.u.parr->arr.size()) {
-		return Datum(0);
-	}
-	if (d1.isArray() || d2.isArray() ||
-			d1.type == PARRAY || d2.type == PARRAY) {
 		return LC::compareArrays(LC::eqData, d1, d2, false, true);
 	}
 	Datum check;
@@ -1355,6 +1346,25 @@ Datum LC::eqData(Datum d1, Datum d2) {
 	return check;
 }
 
+Datum LC::eqDataStrict(Datum d1, Datum d2) {
+	// b_getPos and b_getOne will do case-sensitive
+	// string comparison when determining a match.
+	// As opposed to, y'know, the whole rest of
+	// Director which is case insensitive.
+	if (d1.type == STRING && d2.type == STRING) {
+		return Datum(*d1.u.s == *d2.u.s ? 1 : 0);
+	}
+	// ARRAYs and PARRAYs will do a pointer check,
+	// not a contents check
+	if (d1.isArray() && d2.isArray()) {
+		return Datum(d1.u.farr == d2.u.farr ? 1 : 0);
+	}
+	if (d1.type == PARRAY && d2.type == PARRAY) {
+		return Datum(d1.u.parr == d2.u.parr ? 1 : 0);
+	}
+	return LC::eqData(d1, d2);
+}
+
 void LC::c_eq() {
 	Datum d2 = g_lingo->pop();
 	Datum d1 = g_lingo->pop();
@@ -1373,8 +1383,7 @@ void LC::c_neq() {
 }
 
 Datum LC::gtData(Datum d1, Datum d2) {
-	if (d1.isArray() || d2.isArray() ||
-			d1.type == PARRAY || d2.type == PARRAY) {
+	if (d1.isArray() || d2.isArray() || d1.type == PARRAY || d2.type == PARRAY) {
 		return LC::compareArrays(LC::gtData, d1, d2, false, true);
 	}
 	Datum check;
@@ -1389,8 +1398,7 @@ void LC::c_gt() {
 }
 
 Datum LC::ltData(Datum d1, Datum d2) {
-	if (d1.isArray() || d2.isArray() ||
-			d1.type == PARRAY || d2.type == PARRAY) {
+	if (d1.isArray() || d2.isArray() || d1.type == PARRAY || d2.type == PARRAY) {
 		return LC::compareArrays(LC::ltData, d1, d2, false, true);
 	}
 	Datum check;
@@ -1405,8 +1413,7 @@ void LC::c_lt() {
 }
 
 Datum LC::geData(Datum d1, Datum d2) {
-	if (d1.isArray() || d2.isArray() ||
-			d1.type == PARRAY || d2.type == PARRAY) {
+	if (d1.isArray() || d2.isArray() || d1.type == PARRAY || d2.type == PARRAY) {
 		return LC::compareArrays(LC::geData, d1, d2, false, true);
 	}
 	Datum check;
@@ -1421,8 +1428,7 @@ void LC::c_ge() {
 }
 
 Datum LC::leData(Datum d1, Datum d2) {
-	if (d1.isArray() || d2.isArray() ||
-			d1.type == PARRAY || d2.type == PARRAY) {
+	if (d1.isArray() || d2.isArray() || d1.type == PARRAY || d2.type == PARRAY) {
 		return LC::compareArrays(LC::leData, d1, d2, false, true);
 	}
 	Datum check;
@@ -1907,7 +1913,7 @@ void LC::c_asserterror() {
 
 void LC::c_asserterrordone() {
 	if (!g_lingo->_caughtError) {
-		warning("c_asserterrordone: did not catch error");
+		warning("BUILDBOT: c_asserterrordone: did not catch error");
 	}
 	g_lingo->_expectError = false;
 }
diff --git a/engines/director/lingo/lingo-code.h b/engines/director/lingo/lingo-code.h
index 06c4f2c0914..54390a51e9f 100644
--- a/engines/director/lingo/lingo-code.h
+++ b/engines/director/lingo/lingo-code.h
@@ -106,6 +106,7 @@ void c_tell();
 void c_telldone();
 Datum compareArrays(Datum (*compareFunc)(Datum, Datum), Datum d1, Datum d2, bool location = false, bool value = false);
 Datum eqData(Datum d1, Datum d2);
+Datum eqDataStrict(Datum d1, Datum d2);
 void c_eq();
 Datum neqData(Datum d1, Datum d2);
 void c_neq();
diff --git a/engines/director/lingo/lingo-utils.h b/engines/director/lingo/lingo-utils.h
index 59ad92b7944..7829f1604ab 100644
--- a/engines/director/lingo/lingo-utils.h
+++ b/engines/director/lingo/lingo-utils.h
@@ -55,7 +55,7 @@
 
 #define ARRBOUNDSCHECK(idx,array) \
 	if ((idx)-1 < 0 || (idx) > (int)(array).u.farr->arr.size()) { \
-		warning("BUILDBOT: %s: index out of bounds (%d of %d)", __FUNCTION__, (idx), (array).u.farr->arr.size()); \
+		g_lingo->lingoError("%s: index out of bounds (%d of %d)", __FUNCTION__, (idx), (array).u.farr->arr.size()); \
 		return; \
 	}
 
diff --git a/engines/director/lingo/tests/equality.lingo b/engines/director/lingo/tests/equality.lingo
index ec9267ebcbf..3afa8e0cee7 100644
--- a/engines/director/lingo/tests/equality.lingo
+++ b/engines/director/lingo/tests/equality.lingo
@@ -67,6 +67,8 @@ scummvmAssert([1, 2] = [1, "2"])
 scummvmAssert(not([1, 2] <> [1, "2"]))
 scummvmAssert([1, 2, 3] = [1, "2", 3.0])
 scummvmAssert(not([1, 2, 3] <> [1, "2", 3.0]))
+scummvmAssert(["testa", "testb"] = ["testa", "TESTB"])
+scummvmAssert([#a: "testa", #b: "testb"] = [#a: "testa", #b: "TESTB"])
 
 -- D4 has a quirk where only the left side list elements are checked
 set the scummvmVersion to 400
@@ -78,6 +80,14 @@ scummvmAssert([1, 2, 3] <> [1, "2", 4])
 scummvmAssert(not([1, 2, 3] = [1, "2", 4]))
 scummvmAssert([1, 2, 3] <> [1, "2"])
 scummvmAssert(not([1, 2, 3] = [1, "2"]))
+scummvmAssert([:] = [#a: 1, #b: "2", #c: 4])
+scummvmAssert(not([:] <> [#a: 1, #b: "2", #c: 4]))
+scummvmAssert([#a: 1, #b: 2] = [#a: 1, #b: "2", #c: 4])
+scummvmAssert(not([#a: 1, #b: 2] <> [#a: 1, #b: "2", #c: 4]))
+scummvmAssert([#a: 1, #b: 2, #c: 3] <> [#a: 1, #b: "2", #c: 4])
+scummvmAssert(not([#a: 1, #b: 2, #c: 3] = [#a: 1, #b: "2", #c: 4]))
+scummvmAssert([#a: 1, #b: 2, #c: 3] <> [#a: 1, #b: "2"])
+scummvmAssert(not([#a: 1, #b: 2, #c: 3] = [#a: 1, #b: "2"]))
 
 set the scummvmVersion to 500
 scummvmAssert([] <> [1, "2", 4])
@@ -88,6 +98,57 @@ scummvmAssert([1, 2, 3] <> [1, "2", 4])
 scummvmAssert(not([1, 2, 3] = [1, "2", 4]))
 scummvmAssert([1, 2, 3] <> [1, "2"])
 scummvmAssert(not([1, 2, 3] = [1, "2"]))
+scummvmAssert([:] <> [#a: 1, #b: "2", #c: 4])
+scummvmAssert(not([:] = [#a: 1, #b: "2", #c: 4]))
+scummvmAssert([#a: 1, #b: 2] <> [#a: 1, #b: "2", #c: 4])
+scummvmAssert(not([#a: 1, #b: 2] = [#a: 1, #b: "2", #c: 4]))
+scummvmAssert([#a: 1, #b: 2, #c: 3] <> [#a: 1, #b: "2", #c: 4])
+scummvmAssert(not([#a: 1, #b: 2, #c: 3] = [#a: 1, #b: "2", #c: 4]))
+scummvmAssert([#a: 1, #b: 2, #c: 3] <> [#a: 1, #b: "2"])
+scummvmAssert(not([#a: 1, #b: 2, #c: 3] = [#a: 1, #b: "2"]))
+
+-- single-element equality check, check to see if all list elements match
+scummvmAssert(0 = [])
+scummvmAssert([] = 0)
+scummvmAssert(not(0 <> []))
+scummvmAssert(not([] <> 0))
+scummvmAssert(2 = [])
+scummvmAssert([] = 2)
+scummvmAssert(not(2 <> []))
+scummvmAssert(not([] <> 2))
+scummvmAssert(2 = [2, 2.0, "2", "2.0"])
+scummvmAssert([2, 2.0, "2", "2.0"] = 2)
+scummvmAssert(2 <> [2, 2.0, "2", "2.1"])
+scummvmAssert([2, 2.0, "2", "2.1"] <> 2)
+
+scummvmAssert(0 = [:])
+scummvmAssert([:] = 0)
+scummvmAssert(not(0 <> [:]))
+scummvmAssert(not([:] <> 0))
+scummvmAssert(2 = [:])
+scummvmAssert([:] = 2)
+scummvmAssert(not(2 <> [:]))
+scummvmAssert(not([:] <> 2))
+scummvmAssert(2 = [#a: 2, #b: 2.0, #c: "2", #d: "2.0"])
+scummvmAssert([#a: 2, #b: 2.0, #c: "2", #d: "2.0"] = 2)
+scummvmAssert(2 <> [#a: 2, #b: 2.0, #c: "2", #d: "2.1"])
+scummvmAssert([#a: 2, #b: 2.0, #c: "2", #d: "2.1"] <> 2)
+
+scummvmAssert(0 = point(0, 0))
+scummvmAssert(point(0, 0) = 0)
+scummvmAssert(2 = point(2, 2))
+scummvmAssert(point(2, 2) = 2)
+scummvmAssert(2 <> point(2, 3))
+scummvmAssert(point(2, 3) <> 2)
+
+scummvmAssert(0 = rect(0, 0, 0, 0))
+scummvmAssert(rect(0, 0, 0, 0) = 0)
+scummvmAssert(2 = rect(2, 2, 2, 2))
+scummvmAssert(rect(2, 2, 2, 2) = 2)
+scummvmAssert(2 <> rect(2, 2, 2, 3))
+scummvmAssert(rect(2, 2, 2, 3) <> 2)
+
+
 
 -- Void comparison
 set v1 = value("!")
diff --git a/engines/director/lingo/tests/lists.lingo b/engines/director/lingo/tests/lists.lingo
index 83226174d92..473adb5526f 100644
--- a/engines/director/lingo/tests/lists.lingo
+++ b/engines/director/lingo/tests/lists.lingo
@@ -93,16 +93,6 @@ set res to item 3 of delim_array
 scummvmAssert(res=" three")
 set the itemDelimiter = save
 
--- setAt
-set lst to []
-setAt lst,1,5
-scummvmAssert(lst = [5])
-set lst to []
-setAt lst,3,5
-scummvmAssert(lst = [0,0,5])
-setAt lst,2,5
-scummvmAssert(lst = [0,5,5])
-
 -- rects
 set rct to rect(0, 0, 100, 100)
 set gA to getAt(rct, 2)
@@ -113,31 +103,86 @@ setAt rct, 2, 20
 scummvmAssertEqual(getAt(rct, 2), 20)
 
 -- array conversions
-set a to point(10, 10)
-set b to rect(20, 20, 20, 20)
-set c to [30]
-set d to [40, 40]
-set e to [50, 50, 50]
-set f to [60, 60, 60, 60]
-set g to [70, 70, 70, 70]
-put a + d -- point(50, 50)
-put d + a -- [50, 50]
-put b + f -- rect(80, 80, 80, 80)
-put f + b -- [80, 80, 80, 80]
-put a + c -- [40.0000f]
-put a + d -- point(50, 50)
-put a + e -- point(60, 60)
-put a + f -- point(70, 70)
-put f + a -- [70, 70]
-put b + a -- [30, 30]
-put b + c -- [50]
-put b + e -- [70, 70, 70]
-put b + f -- rect(80, 80, 80, 80)
-put b + g -- rect(90, 90, 90, 90)
-put a + 5 -- point(15, 15)
-put 5 + a -- point(15, 15)
-put b + 5 -- rect(25, 25, 25, 25)
-put 5 + b -- rect(25, 25, 25, 25)
+set a to point(11, 12)
+set b to rect(21, 22, 23, 24)
+set c to [31]
+set d to [41, 42]
+set e to [51, 52, 53]
+set f to [61, 62, 63, 64]
+set g to [71, 72, 73, 74, 75]
+set h to 5
+
+scummvmAssertEqual(string(a + a), "point(22, 24)")
+scummvmAssertEqual(string(a + b), "[32, 34]")
+scummvmAssertEqual(string(a + c), "[42]")
+scummvmAssertEqual(string(a + d), "point(52, 54)")
+scummvmAssertEqual(string(a + e), "[62, 64]")
+scummvmAssertEqual(string(a + f), "[72, 74]")
+scummvmAssertEqual(string(a + g), "[82, 84]")
+scummvmAssertEqual(string(a + h), "point(16, 17)")
+
+scummvmAssertEqual(string(b + a), "[32, 34]")
+scummvmAssertEqual(string(b + b), "rect(42, 44, 46, 48)")
+scummvmAssertEqual(string(b + c), "[52]")
+scummvmAssertEqual(string(b + d), "[62, 64]")
+scummvmAssertEqual(string(b + e), "[72, 74, 76]")
+scummvmAssertEqual(string(b + f), "rect(82, 84, 86, 88)")
+scummvmAssertEqual(string(b + g), "[92, 94, 96, 98]")
+scummvmAssertEqual(string(b + h), "rect(26, 27, 28, 29)")
+
+scummvmAssertEqual(string(c + a), "[42]")
+scummvmAssertEqual(string(c + b), "[52]")
+scummvmAssertEqual(string(c + c), "[62]")
+scummvmAssertEqual(string(c + d), "[72]")
+scummvmAssertEqual(string(c + e), "[82]")
+scummvmAssertEqual(string(c + f), "[92]")
+scummvmAssertEqual(string(c + g), "[102]")
+scummvmAssertEqual(string(c + h), "[36]")
+
+scummvmAssertEqual(string(d + a), "[52, 54]")
+scummvmAssertEqual(string(d + b), "[62, 64]")
+scummvmAssertEqual(string(d + c), "[72]")
+scummvmAssertEqual(string(d + d), "[82, 84]")
+scummvmAssertEqual(string(d + e), "[92, 94]")
+scummvmAssertEqual(string(d + f), "[102, 104]")
+scummvmAssertEqual(string(d + g), "[112, 114]")
+scummvmAssertEqual(string(d + h), "[46, 47]")
+
+scummvmAssertEqual(string(e + a), "[62, 64]")
+scummvmAssertEqual(string(e + b), "[72, 74, 76]")
+scummvmAssertEqual(string(e + c), "[82]")
+scummvmAssertEqual(string(e + d), "[92, 94]")
+scummvmAssertEqual(string(e + e), "[102, 104, 106]")
+scummvmAssertEqual(string(e + f), "[112, 114, 116]")
+scummvmAssertEqual(string(e + g), "[122, 124, 126]")
+scummvmAssertEqual(string(e + h), "[56, 57, 58]")
+
+scummvmAssertEqual(string(f + a), "[72, 74]")
+scummvmAssertEqual(string(f + b), "[82, 84, 86, 88]")
+scummvmAssertEqual(string(f + c), "[92]")
+scummvmAssertEqual(string(f + d), "[102, 104]")
+scummvmAssertEqual(string(f + e), "[112, 114, 116]")
+scummvmAssertEqual(string(f + f), "[122, 124, 126, 128]")
+scummvmAssertEqual(string(f + g), "[132, 134, 136, 138]")
+scummvmAssertEqual(string(f + h), "[66, 67, 68, 69]")
+
+scummvmAssertEqual(string(g + a), "[82, 84]")
+scummvmAssertEqual(string(g + b), "[92, 94, 96, 98]")
+scummvmAssertEqual(string(g + c), "[102]")
+scummvmAssertEqual(string(g + d), "[112, 114]")
+scummvmAssertEqual(string(g + e), "[122, 124, 126]")
+scummvmAssertEqual(string(g + f), "[132, 134, 136, 138]")
+scummvmAssertEqual(string(g + g), "[142, 144, 146, 148, 150]")
+scummvmAssertEqual(string(g + h), "[76, 77, 78, 79, 80]")
+
+scummvmAssertEqual(string(h + a), "point(16, 17)")
+scummvmAssertEqual(string(h + b), "rect(26, 27, 28, 29)")
+scummvmAssertEqual(string(h + c), "[36]")
+scummvmAssertEqual(string(h + d), "[46, 47]")
+scummvmAssertEqual(string(h + e), "[56, 57, 58]")
+scummvmAssertEqual(string(h + f), "[66, 67, 68, 69]")
+scummvmAssertEqual(string(h + g), "[76, 77, 78, 79, 80]")
+scummvmAssertEqual(string(h + h), "10")
 
 -- proplist with missing keys
 set proplist_without_keys = ["key": "value", "keyless expr 1", "keyless expr 2"]
@@ -156,29 +201,147 @@ set tempmood to the mood of templst
 scummvmAssert(tempmood = 2)
 put templst
 
--- deleteAt
+
+-- add
+
+
+-- addAt
+
+
+-- addProp
+
+
+-- append
+
+
+-- count
+
+
+-- deleteAt (uses getAt as basis)
 set testList to [1, 2, 3, 4, 5]
 deleteAt testList, 3
-scummvmAssert(testList = [1, 2, 4, 5])
+scummvmAssertEqual(testList, [1, 2, 4, 5])
 set testList to [#a: 1, #b: 2, #c: 3, #d: 4, #e: 5]
 deleteAt testList, 3
-scummvmAssert(testList = [#a: 1, #b: 2, #d: 4, #e: 5])
+scummvmAssertEqual(testList, [#a: 1, #b: 2, #d: 4, #e: 5])
 
--- deleteOne
+
+-- deleteOne (uses getOne as basis)
 set testList to [5, 4, 3, 1, 4]
 deleteOne testList, 4
-scummvmAssert(testList = [5, 3, 1, 4])
+scummvmAssertEqual(testList, [5, 3, 1, 4])
 set testlist to [5, "4.0", 3, 1, 4]
 deleteOne testList, 4
-scummvmAssert(testList = [5, 3, 1, 4])
+scummvmAssertEqual(testList, [5, 3, 1, 4])
 set testlist to [5, 4.0, 3, 1, 4]
 deleteOne testList, 4
-scummvmAssert(testList = [5, 3, 1, 4])
+scummvmAssertEqual(testList, [5, 3, 1, 4])
 set testlist to [5, "urgh", 3, 1, "urgh"]
 deleteOne testList, "urgh"
-scummvmAssert(testList = [5, 3, 1, "urgh"])
+scummvmAssertEqual(testList, [5, 3, 1, "urgh"])
 set testlist to [5, "URGH", 3, 1, "urgh"]
 deleteOne testList, "urgh"
-scummvmAssert(testList = [5, "URGH", 3, 1])
+scummvmAssertEqual(testList, [5, "URGH", 3, 1])
+
+
+-- deleteProp
+
+
+-- findPos
+
+
+-- findPosNear
+
+
+-- getaProp
+set testList to [#a, #b, #c, #d, #e]
+scummvmAssertEqual(getaProp(testList, 4), #d)
+scummvmAssertError getaProp(testList, 7)
+set testList to [#a: 5, #b: 4, #c: 3, #d: 2, #e: 1]
+scummvmAssertEqual(getaProp(testList, #d), 2)
+scummvmAssert(voidp(getaProp(testList, #g)))
+
+
+-- getAt
+set testList to [5, 4, 3, 2, 1]
+scummvmAssertEqual(getAt(testList, 2), 4)
+scummvmAssertError getAt(testList, 7)
+set testList to [#a: 5, #b: 4, #c: 3, #d: 2, #e: 1]
+scummvmAssertEqual(getAt(testList, 2), 4)
+scummvmAssertError getAt(testList, 7)
+
+
+-- getLast
+set testList to [#a, #b, #c]
+scummvmAssertEqual(getLast(testList), #c)
+scummvmAssert(voidp(getLast([])))
+set testList to [#a: 3, #b: 2, #c: 1]
+scummvmAssertEqual(getLast(testList), 1)
+scummvmAssert(voidp(getLast([:])))
+
+
+-- getOne
+set testList to [#a, #b, #c, #d, #d, #e]
+scummvmAssertEqual(getOne(testList, #d), 4)
+scummvmAssertEqual(getOne(testList, #g), 0)
+
+set testList to [5, 4, 3, 1, 4]
+scummvmAssertEqual(getOne(testList, 4), 2)
+set testlist to [5, "4.0", 3, 1, 4]
+scummvmAssertEqual(getOne(testList, 4), 2)
+set testlist to [5, 4.0, 3, 1, 4]
+scummvmAssertEqual(getOne(testList, 4), 2)
+set testlist to [5, "urgh", 3, 1, "urgh"]
+scummvmAssertEqual(getOne(testList, "urgh"), 2)
+set testlist to [5, "URGH", 3, 1, "urgh"]
+scummvmAssertEqual(getOne(testList, "urgh"), 5)
+
+-- for finding ARRAY/PARRAY, check the pointer, not the contents
+set testList to [#a, #b, [1, 2, 3]]
+scummvmAssertEqual(getOne(testList, [1, 2, 3]), 0)
+set subItem to [1, 2, 3]
+set testList to [#a, #b, subItem]
+scummvmAssertEqual(getOne(testList, subItem), 3)
+
+
+-- getProp
+set testList to [#a, #b, #c, #d, #e]
+scummvmAssertEqual(getProp(testList, 3), #c)
+
+
+-- getPropAt
+
+
+-- ilk
+
+
+-- list
+
+
+-- listP
+
+
+-- max
+
+
+-- min
+
+
+-- setaProp
+
+
+-- setAt
+set lst to []
+setAt lst,1,5
+scummvmAssertEqual(lst, [5])
+set lst to []
+setAt lst,3,5
+scummvmAssertEqual(lst, [0,0,5])
+setAt lst,2,5
+scummvmAssertEqual(lst, [0,5,5])
+
+
+-- setProp
 
 
+-- sort


Commit: 337f214ff4be1a790717b341b8f05d95401676a6
    https://github.com/scummvm/scummvm/commit/337f214ff4be1a790717b341b8f05d95401676a6
Author: Scott Percival (code at moral.net.au)
Date: 2024-05-22T17:24:31+02:00

Commit Message:
DIRECTOR: Move equality warnings behind debug gate

Changed paths:
    engines/director/cursor.cpp
    engines/director/lingo/lingo.cpp


diff --git a/engines/director/cursor.cpp b/engines/director/cursor.cpp
index 281a6b9e00b..dac60f201f1 100644
--- a/engines/director/cursor.cpp
+++ b/engines/director/cursor.cpp
@@ -28,6 +28,7 @@
 #include "director/movie.h"
 #include "director/castmember/bitmap.h"
 #include "director/picture.h"
+#include "director/lingo/lingo-code.h"
 
 namespace Director {
 
@@ -62,7 +63,7 @@ void Cursor::readFromCast(Datum cursorCasts) {
 		warning("Cursor::readFromCast: Needs array of 2");
 		return;
 	}
-	if (_cursorResId == cursorCasts)
+	if (_cursorResId.type == ARRAY && LC::eqData(_cursorResId, cursorCasts).asInt())
 		return;
 
 	CastMemberID cursorId = cursorCasts.u.farr->arr[0].asMemberID();
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index 8f0722bb2d7..79e2a51ce84 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -1356,7 +1356,7 @@ int Datum::equalTo(Datum &d, bool ignoreCase) const {
 	case PICTUREREF:
 		return 0; // Original always returns 0 on picture reference comparison
 	default:
-		warning("Datum::equalTo(): Invalid equality check between types %s and %s", type2str(), d.type2str());
+		debugC(1, kDebugLingoExec, "Datum::equalTo(): Invalid equality check between types %s and %s", type2str(), d.type2str());
 		break;
 	}
 	return 0;


Commit: 8e6ba00bc2c841742f9131b6b1663831a954d28d
    https://github.com/scummvm/scummvm/commit/8e6ba00bc2c841742f9131b6b1663831a954d28d
Author: Scott Percival (code at moral.net.au)
Date: 2024-05-22T17:24:31+02:00

Commit Message:
DIRECTOR: XOBJ: Fix MMovie to rename autosaves to something sensible

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


diff --git a/engines/director/lingo/xlibs/mmovie.cpp b/engines/director/lingo/xlibs/mmovie.cpp
index b6fd56cdcde..dc845b6be96 100644
--- a/engines/director/lingo/xlibs/mmovie.cpp
+++ b/engines/director/lingo/xlibs/mmovie.cpp
@@ -586,7 +586,7 @@ void MMovieXObj::m_readFile(int nargs) {
 		}
 	} else {
 		path = lastPathComponent(origPath, g_director->_dirSeparator);
-		if (path.hasSuffixIgnoreCase(".txt"))
+		if (!path.hasSuffixIgnoreCase(".txt"))
 			path += ".txt";
 	}
 	if (!path.hasPrefixIgnoreCase(prefix)) {
@@ -647,7 +647,13 @@ void MMovieXObj::m_writeFile(int nargs) {
 		}
 	} else {
 		path = lastPathComponent(origPath, g_director->_dirSeparator);
-		if (path.hasSuffixIgnoreCase(".txt"))
+		// Virtual Nightclub makes autosaves at random intervals.
+		// Intercept and give them a sensible name.
+		if (path.hasSuffixIgnoreCase(".VNC") && path.hasPrefixIgnoreCase("VNC_")) {
+			path = Common::String::format("Autosave.txt");
+		}
+
+		if (!path.hasSuffixIgnoreCase(".txt"))
 			path += ".txt";
 	}
 	if (!path.hasPrefixIgnoreCase(prefix)) {


Commit: 523d31590e7cc84d1f149507160be56dd110e004
    https://github.com/scummvm/scummvm/commit/523d31590e7cc84d1f149507160be56dd110e004
Author: Scott Percival (code at moral.net.au)
Date: 2024-05-22T17:24:31+02:00

Commit Message:
DIRECTOR: Prevent exitFrame handler from running twice

It is possible to call "pause" in the exitFrame handler, then rely on
another handler (e.g. mouseDown) to call "continue" to start the movie
playback. exitFrame should not be called again once playback is resumed.

Fixes entering the first hatch in L-Zone.
Fixes D4-unit/T_EVNT12.DIR in director-tests.

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


diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index cdcda48be17..a74744e78ce 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -80,6 +80,7 @@ Score::Score(Movie *movie) {
 	_waitForClick = false;
 	_waitForClickCursor = false;
 	_activeFade = false;
+	_exitFrameCalled = false;
 	_playState = kPlayNotStarted;
 
 	_numChannelsDisplayed = 0;
@@ -536,9 +537,10 @@ void Score::update() {
 		// When Lingo::func_goto* is called, _nextFrame is set
 		// and _skipFrameAdvance is set to true.
 		// exitFrame is not called in this case.
-		if (!_vm->_skipFrameAdvance) {
+		if (!_vm->_skipFrameAdvance && !_exitFrameCalled) {
 			// Exit the current frame. This can include scopeless ScoreScripts.
 			_movie->processEvent(kEventExitFrame);
+			_exitFrameCalled = true;
 		}
 	}
 
@@ -629,8 +631,11 @@ void Score::update() {
 
 	// then call the enterFrame hook (if one exists)
 	count = _window->frozenLingoStateCount();
-	if (!_vm->_playbackPaused && _vm->getVersion() >= 400) {
-		_movie->processEvent(kEventEnterFrame);
+	if (!_vm->_playbackPaused) {
+		_exitFrameCalled = false;
+		if (_vm->getVersion() >= 400) {
+			_movie->processEvent(kEventEnterFrame);
+		}
 	}
 	if (_window->frozenLingoStateCount() > count)
 		return;
diff --git a/engines/director/score.h b/engines/director/score.h
index 43db7ede618..465eaa36ae4 100644
--- a/engines/director/score.h
+++ b/engines/director/score.h
@@ -185,6 +185,7 @@ public:
 	bool _waitForClickCursor;
 	bool _cursorDirty;
 	bool _activeFade;
+	bool _exitFrameCalled;
 	Cursor _defaultCursor;
 	CursorRef _currentCursor;
 	bool _skipTransition;




More information about the Scummvm-git-logs mailing list