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

sev- noreply at scummvm.org
Fri Jul 3 15:12:36 UTC 2026


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

Summary:
9a299751b7 DIRECTOR: Fix stale pointers when switching movies
ca86fc8bfe DIRECTOR: Run handlers in the window that owns their script


Commit: 9a299751b7cda66c89af3069dd556319f20edde2
    https://github.com/scummvm/scummvm/commit/9a299751b7cda66c89af3069dd556319f20edde2
Author: Zachary Berry (zberry92 at gmail.com)
Date: 2026-07-03T17:12:31+02:00

Commit Message:
DIRECTOR: Fix stale pointers when switching movies

~Movie now deletes the score before the casts so that ~Channel severs
those back-pointers while the cast members are still alive.

This fixes dangling pointers when a movie switches mid-frame.

Changed paths:
    engines/director/cast.h
    engines/director/channel.cpp
    engines/director/movie.cpp
    engines/director/window.cpp


diff --git a/engines/director/cast.h b/engines/director/cast.h
index d6be2246252..61f4be01289 100644
--- a/engines/director/cast.h
+++ b/engines/director/cast.h
@@ -92,6 +92,8 @@ public:
 	void loadArchive();
 	void setArchive(Common::SharedPtr<Archive> archive);
 	Common::SharedPtr<Archive> getArchive() const { return _castArchive; };
+	Movie *getMovie() const { return _movie; }
+	void setMovie(Movie *movie) { _movie = movie; }
 	Common::String getMacName() const { return _macName; }
 	Common::String getCastName() const { return _castName; }
 	void setCastName(const Common::String &name) { _castName = name; }
diff --git a/engines/director/channel.cpp b/engines/director/channel.cpp
index 02614e45a73..0ce8593cf34 100644
--- a/engines/director/channel.cpp
+++ b/engines/director/channel.cpp
@@ -108,6 +108,14 @@ Channel& Channel::operator=(const Channel &channel) {
 
 
 Channel::~Channel() {
+	// A digital video cast member can outlive this channel.
+	if (_sprite && _sprite->_cast && _sprite->_cast->_type == kCastDigitalVideo) {
+		DigitalVideoCastMember *video = (DigitalVideoCastMember *)_sprite->_cast;
+		if (video->_channel == this) {
+			video->setChannel(nullptr);
+		}
+	}
+
 	if (_widget) {
 		delete _widget;
 	}
diff --git a/engines/director/movie.cpp b/engines/director/movie.cpp
index e644c5bffc7..bdf964fcc45 100644
--- a/engines/director/movie.cpp
+++ b/engines/director/movie.cpp
@@ -112,9 +112,12 @@ Movie::~Movie() {
 		g_director->_allOpenResFiles.remove(_cast->getArchive()->getPathName());
 	}
 
-	delete _cast;
-	delete _sharedCast;
 	delete _score;
+
+	for (auto &it : _casts) {
+		delete it._value;
+	}
+	delete _sharedCast;
 }
 
 void Movie::setArchive(Common::SharedPtr<Archive> archive) {
diff --git a/engines/director/window.cpp b/engines/director/window.cpp
index fd3f9a76dd4..e9a623b2f4a 100644
--- a/engines/director/window.cpp
+++ b/engines/director/window.cpp
@@ -569,6 +569,7 @@ void Window::loadNewSharedCast(Cast *previousSharedCast) {
 		// Clear those previous widget pointers
 		previousSharedCast->releaseCastMemberWidget();
 		_currentMovie->_sharedCast = previousSharedCast;
+		previousSharedCast->setMovie(_currentMovie);
 
 		debugC(1, kDebugLoading, "Skipping loading already loaded shared cast, path: %s", previousSharedCastPath.toString(Common::Path::kNativeSeparator).c_str());
 		return;
@@ -609,6 +610,9 @@ bool Window::loadNextMovie() {
 	if (_currentMovie) {
 		previousSharedCast = _currentMovie->getSharedCast();
 		_currentMovie->_sharedCast = nullptr;
+		if (previousSharedCast) {
+			previousSharedCast->setMovie(nullptr);
+		}
 	}
 
 	if (_currentMovie) {


Commit: ca86fc8bfe70ca096a8e4a2ce7a3fbcfd4f3f556
    https://github.com/scummvm/scummvm/commit/ca86fc8bfe70ca096a8e4a2ce7a3fbcfd4f3f556
Author: Zachary Berry (zberry92 at gmail.com)
Date: 2026-07-03T17:12:31+02:00

Commit Message:
DIRECTOR: Run handlers in the window that owns their script

When a handler crosses windows it now executes with its
owning window current.

This keeps track of the context window and switched Lingo
context when calling those methods.

Fixes startup in jewels

Changed paths:
    engines/director/cast.cpp
    engines/director/cast.h
    engines/director/director.cpp
    engines/director/director.h
    engines/director/lingo/lingo-code.cpp
    engines/director/lingo/lingo-object.cpp
    engines/director/lingo/lingo-object.h
    engines/director/lingo/lingo.cpp
    engines/director/lingo/lingo.h


diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 95f06cc2042..ce7debe6704 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -97,6 +97,11 @@ Cast::Cast(Movie *movie, uint16 castLibID, bool isShared, bool isExternal, uint1
 }
 
 Cast::~Cast() {
+	for (auto &it : _liveScriptContexts) {
+		it._key->_cast = nullptr;
+	}
+	_liveScriptContexts.clear();
+
 	for (auto &it : _loadedStxts)
 		delete it._value;
 
@@ -1813,6 +1818,7 @@ void Cast::loadLingoContext(Common::SeekableReadStreamEndian &stream) {
 				// Those scripts need to be cleaned up on ~LingoArchive
 				script->setOnlyInLctxContexts();
 			}
+			script->setCast(this);
 		}
 	} else {
 		error("Cast::loadLingoContext: unsupported Director version v%d (%d)", humanVersion(_version), _version);
diff --git a/engines/director/cast.h b/engines/director/cast.h
index 61f4be01289..f8028aa21f6 100644
--- a/engines/director/cast.h
+++ b/engines/director/cast.h
@@ -45,6 +45,7 @@ class DirectorEngine;
 class Lingo;
 struct LingoArchive;
 struct Resource;
+class ScriptContext;
 class Stxt;
 class RTE0;
 class RTE1;
@@ -146,6 +147,10 @@ public:
 	Common::CodePage getFileEncoding();
 	Common::U32String decodeString(const Common::String &str);
 
+	// Script contexts keep a back-pointer to their owning cast.
+	void registerScriptContext(ScriptContext *ctx) { _liveScriptContexts.setVal(ctx, true); }
+	void unregisterScriptContext(ScriptContext *ctx) { _liveScriptContexts.erase(ctx); }
+
 	Common::String formatCastSummary(int castId);
 	PaletteV4 loadPalette(Common::SeekableReadStreamEndian &stream, int id);
 
@@ -269,6 +274,8 @@ private:
 	Common::HashMap<Common::String, int, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _castsNames;
 	Common::HashMap<uint16, int> _castsScriptIds;
 
+	Common::HashMap<ScriptContext *, bool> _liveScriptContexts;
+
 };
 
 } // End of namespace Director
diff --git a/engines/director/director.cpp b/engines/director/director.cpp
index a68ac691bc3..580e1072a76 100644
--- a/engines/director/director.cpp
+++ b/engines/director/director.cpp
@@ -218,6 +218,18 @@ void DirectorEngine::forgetWindow(Window *window) {
 	_windowsToForget.push_back(window);
 }
 
+bool DirectorEngine::isWindowRegistered(Window *window) const {
+	if (window == _stage) {
+		return true;
+	}
+	for (auto &it : _windowList) {
+		if (it == window) {
+			return true;
+		}
+	}
+	return false;
+}
+
 void DirectorEngine::setCurrentWindow(Window *window) {
 	if (_currentWindow == window)
 		return;
diff --git a/engines/director/director.h b/engines/director/director.h
index 496bb872455..decb6185477 100644
--- a/engines/director/director.h
+++ b/engines/director/director.h
@@ -181,6 +181,7 @@ public:
 	Window *getCurrentWindow() const { return _currentWindow; }
 	Window *getOrCreateWindow(Common::String &name);
 	void forgetWindow(Window *window);
+	bool isWindowRegistered(Window *window) const;
 	void setCurrentWindow(Window *window);
 	Window *getCursorWindow() const { return _cursorWindow; }
 	void setCursorWindow(Window *window) { _cursorWindow = window; }
diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 967390ab122..8d758122047 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -47,6 +47,7 @@
 
 #include "director/director.h"
 #include "director/debugger.h"
+#include "director/cast.h"
 #include "director/movie.h"
 #include "director/score.h"
 #include "director/sprite.h"
@@ -258,6 +259,18 @@ void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRet
 		_state->context->incRefCount();
 	}
 
+	// Run the handler in the window that owns its script (MIAW scoping); mirrors c_tell()
+	if (funcSym.ctx && funcSym.ctx->getCast() && funcSym.ctx->getCast()->getMovie()) {
+		Window *targetWindow = funcSym.ctx->getCast()->getMovie()->getWindow();
+		Window *currentWindow = _vm->getCurrentWindow();
+		if (targetWindow && targetWindow != currentWindow) {
+			fp->retWindow = currentWindow;
+			currentWindow->incRefCount();
+			currentWindow->moveLingoState(targetWindow);
+			_vm->setCurrentWindow(targetWindow);
+		}
+	}
+
 	DatumHash *localvars = new DatumHash;
 	if (funcSym.anonymous && _state->localVars) {
 		// Execute anonymous functions within the current var frame.
@@ -364,8 +377,20 @@ void Lingo::popContext(bool aborting) {
 		printCallStack(_state->pc);
 	}
 
+	// Undo the pushContext window switch.
+	Window *retWindow = fp->retWindow;
+
 	delete fp;
 
+	if (retWindow) {
+		// If the window was closed while the handler ran, don't switch back.
+		if (_vm->getCurrentWindow() != retWindow && _vm->isWindowRegistered(retWindow)) {
+			_vm->getCurrentWindow()->moveLingoState(retWindow);
+			_vm->setCurrentWindow(retWindow);
+		}
+		retWindow->decRefCount();
+	}
+
 	g_debugger->popContextHook();
 }
 
diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 50c5803071b..23bdb9c502d 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -24,6 +24,7 @@
 #include "graphics/macgui/mactext.h"
 
 #include "director/director.h"
+#include "director/cast.h"
 #include "director/movie.h"
 #include "director/window.h"
 #include "director/lingo/lingo-ast.h"
@@ -586,9 +587,26 @@ ScriptContext::ScriptContext(const ScriptContext &sc) : Object<ScriptContext>(sc
 
 	_id = sc._id;
 	_castLibHint = sc._castLibHint;
+	setCast(sc._cast);
 }
 
 ScriptContext::~ScriptContext() {
+	if (_cast) {
+		_cast->unregisterScriptContext(this);
+	}
+}
+
+void ScriptContext::setCast(Cast *cast) {
+	if (_cast == cast) {
+		return;
+	}
+	if (_cast) {
+		_cast->unregisterScriptContext(this);
+	}
+	_cast = cast;
+	if (_cast) {
+		_cast->registerScriptContext(this);
+	}
 }
 
 Common::String ScriptContext::asString() {
diff --git a/engines/director/lingo/lingo-object.h b/engines/director/lingo/lingo-object.h
index ebd29576ad4..8ceaeda70d3 100644
--- a/engines/director/lingo/lingo-object.h
+++ b/engines/director/lingo/lingo-object.h
@@ -227,15 +227,21 @@ public:
 	Common::SharedPtr<Node> _assemblyAST;	// Optionally contains AST when we compile Lingo
 
 private:
+	friend class Cast;	// clears _cast directly in ~Cast
+
 	DatumHash _properties;
 	Common::Array<Common::String> _propertyNames;
 	bool _onlyInLctxContexts = false;
+	Cast *_cast = nullptr;
 
 public:
 	ScriptContext(Common::String name, ScriptType type = kNoneScript, int id = 0, uint16 castLibHint = 0, uint16 parentNumber = 0, int scriptId = 0);
 	ScriptContext(const ScriptContext &sc);
 	~ScriptContext() override;
 
+	Cast *getCast() const { return _cast; }
+	void setCast(Cast *cast);
+
 	bool isFactory() const { return _objType == kFactoryObj; };
 	void setFactory(bool flag) { _objType = flag ? kFactoryObj : kScriptObj; }
 
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index d11bbe941ad..7988a750681 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -164,6 +164,9 @@ LingoState::~LingoState() {
 		if (callstack[i]->retContext) {
 			callstack[i]->retContext->decRefCount();
 		}
+		if (callstack[i]->retWindow) {
+			callstack[i]->retWindow->decRefCount();
+		}
 		delete callstack[i];
 	}
 	if (localVars)
@@ -401,6 +404,7 @@ void LingoArchive::addCode(const Common::U32String &code, ScriptType type, uint1
 
 	ScriptContext *sc = g_lingo->_compiler->compileLingo(code, this, type, CastMemberID(id, cast->_castLibID), contextName, false, preprocFlags);
 	if (sc) {
+		sc->setCast(cast);
 		scriptContexts[type][id] = sc;
 		sc->incRefCount();
 	}
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index e140baedd48..e03bc6ec9d7 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -46,6 +46,7 @@ class Cast;
 class ScriptContext;
 class DirectorEngine;
 class Frame;
+class Window;
 class LingoCompiler;
 struct Breakpoint;
 
@@ -262,6 +263,7 @@ struct CFrame {	/* proc/func call stack frame */
 	Datum			defaultRetVal;		/* default return value */
 	int				paramCount;			/* original number of arguments submitted */
 	Common::Array<Datum> paramList;		/* original argument list */
+	Window			*retWindow = nullptr;	/* window to restore on return */
 };
 
 struct LingoEvent {




More information about the Scummvm-git-logs mailing list