[Scummvm-git-logs] scummvm master -> 689d08a39609870d960afb092e9ab5e8187c2517

sev- noreply at scummvm.org
Fri Dec 9 00:11:07 UTC 2022


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

Summary:
873095c87e DIRECTOR: Move current Lingo state into Window-owned struct
3ea7f10467 DIRECTOR: Freeze LingoState instead of using callstack
30e9d330df DIRECTOR: Implement the paramCount
225460f875 DIRECTOR: LINGO: Refactor SpaceMgr XObj, add LLink support
7c975eff43 DIRECTOR: LINGO: Adjust freezing/thawing behaviour
7a8d4bfeb9 DIRECTOR: LINGO: Add func_goto* logs to kDebugLingoExec
65b341b5c1 DIRECTOR: LINGO: Fix LC::negateData to work on a copy
f84d643e95 DIRECTOR: LINGO: Freeze state when score stops
93373b0795 DIRECTOR: Allow Lingo::pushContext to set me handle to null
94c064e84b DIRECTOR: LINGO: Add b_deleteOne
7714e09382 DIRECTOR: LINGO: Remove duplicate insert in b_addProp
2674b628c4 DIRECTOR: Free frozen lingo states in window destructor
689d08a396 DIRECTOR: Add changes from code review


Commit: 873095c87e2decbf6ace850a3599e75ff687ccca
    https://github.com/scummvm/scummvm/commit/873095c87e2decbf6ace850a3599e75ff687ccca
Author: Scott Percival (code at moral.net.au)
Date: 2022-12-09T01:10:57+01:00

Commit Message:
DIRECTOR: Move current Lingo state into Window-owned struct

The previous arrangement was to copy the bits of Lingo state back and forth from
the Window object to the Lingo instance, and query the window for the
callstack. This refactor consolidates the current Lingo state into a single
struct, owned by the Window object and accessible via a pointer on the
Lingo object.

Changed paths:
    engines/director/debugger.cpp
    engines/director/director.cpp
    engines/director/lingo/lingo-builtins.cpp
    engines/director/lingo/lingo-bytecode.cpp
    engines/director/lingo/lingo-code.cpp
    engines/director/lingo/lingo-object.cpp
    engines/director/lingo/lingo.cpp
    engines/director/lingo/lingo.h
    engines/director/lingo/xlibs/aiff.cpp
    engines/director/lingo/xlibs/applecdxobj.cpp
    engines/director/lingo/xlibs/askuser.cpp
    engines/director/lingo/xlibs/barakeobj.cpp
    engines/director/lingo/xlibs/cdromxobj.cpp
    engines/director/lingo/xlibs/ednox.cpp
    engines/director/lingo/xlibs/fileio.cpp
    engines/director/lingo/xlibs/flushxobj.cpp
    engines/director/lingo/xlibs/gpid.cpp
    engines/director/lingo/xlibs/jitdraw3.cpp
    engines/director/lingo/xlibs/jwxini.cpp
    engines/director/lingo/xlibs/labeldrvxobj.cpp
    engines/director/lingo/xlibs/memoryxobj.cpp
    engines/director/lingo/xlibs/miscx.cpp
    engines/director/lingo/xlibs/moovxobj.cpp
    engines/director/lingo/xlibs/movemousexobj.cpp
    engines/director/lingo/xlibs/movutils.cpp
    engines/director/lingo/xlibs/orthoplayxobj.cpp
    engines/director/lingo/xlibs/palxobj.cpp
    engines/director/lingo/xlibs/popupmenuxobj.cpp
    engines/director/lingo/xlibs/serialportxobj.cpp
    engines/director/lingo/xlibs/soundjam.cpp
    engines/director/lingo/xlibs/spacemgr.cpp
    engines/director/lingo/xlibs/videodiscxobj.cpp
    engines/director/lingo/xlibs/widgetxobj.cpp
    engines/director/lingo/xlibs/winxobj.cpp
    engines/director/score.cpp
    engines/director/window.cpp
    engines/director/window.h


diff --git a/engines/director/debugger.cpp b/engines/director/debugger.cpp
index ece454253b4..78d34eec517 100644
--- a/engines/director/debugger.cpp
+++ b/engines/director/debugger.cpp
@@ -334,7 +334,7 @@ bool Debugger::cmdFuncs(int argc, const char **argv) {
 	Lingo *lingo = g_director->getLingo();
 	Movie *movie = g_director->getCurrentMovie();
 	Score *score = movie->getScore();
-	ScriptContext *csc = lingo->_currentScriptContext;
+	ScriptContext *csc = lingo->_state->context;
 	if (csc) {
 		debugPrintf("Functions attached to frame %d:\n", score->getCurrentFrame());
 		debugPrintf("  %d:", csc->_id);
@@ -365,7 +365,7 @@ bool Debugger::cmdFuncs(int argc, const char **argv) {
 
 bool Debugger::cmdBacktrace(int argc, const char **argv) {
 	Lingo *lingo = g_director->getLingo();
-	debugPrintf("%s\n", lingo->formatCallStack(lingo->_pc).c_str());
+	debugPrintf("%s\n", lingo->formatCallStack(lingo->_state->pc).c_str());
 	return true;
 }
 
@@ -375,7 +375,7 @@ bool Debugger::cmdDisasm(int argc, const char **argv) {
 		if (!strcmp(argv[1], "all")) {
 			Movie *movie = g_director->getCurrentMovie();
 			Score *score = movie->getScore();
-			ScriptContext *csc = lingo->_currentScriptContext;
+			ScriptContext *csc = lingo->_state->context;
 			if (csc) {
 				debugPrintf("Functions attached to frame %d:\n", score->getCurrentFrame());
 				for (auto &it : csc->_functionHandlers) {
@@ -459,7 +459,7 @@ bool Debugger::cmdDisasm(int argc, const char **argv) {
 		}
 
 	} else {
-		Common::Array<CFrame *> &callstack = g_director->getCurrentWindow()->_callstack;
+		Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;
 		if (callstack.size() == 0) {
 			debugPrintf("Lingo is not executing, nothing to disassemble.\n");
 			return true;
@@ -511,7 +511,7 @@ bool Debugger::cmdBpSet(int argc, const char **argv) {
 	bp.id = _bpNextId;
 	bp.type = kBreakpointFunction;
 	if (argc == 1) {
-		Common::Array<CFrame *> &callstack = g_director->getCurrentWindow()->_callstack;
+		Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;
 		if (callstack.size() == 0) {
 			debugPrintf("Lingo is not executing, no current function to add breakpoint to.\n");
 			return true;
@@ -527,14 +527,14 @@ bool Debugger::cmdBpSet(int argc, const char **argv) {
 		}
 		bp.scriptId = frame->sp.ctx->_id;
 		bp.funcName = *frame->sp.name;
-		bp.funcOffset = g_lingo->_pc;
+		bp.funcOffset = g_lingo->_state->pc;
 	} else if (argc == 2 || argc == 3) {
 		Common::String target(argv[1]);
 		uint splitPoint = target.findFirstOf(":");
 		if (splitPoint == Common::String::npos) {
 			if (argc == 2 && atoi(argv[1]) > 0) {
 				// first and only argument is a number, use as an offset for the current function
-				Common::Array<CFrame *> &callstack = g_director->getCurrentWindow()->_callstack;
+				Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;
 				if (callstack.size() == 0) {
 					debugPrintf("Lingo is not executing, no current function to add breakpoint to.\n");
 					return true;
@@ -780,7 +780,7 @@ void Debugger::bpUpdateState() {
 	_bpCheckEntityRead = false;
 	_bpCheckEntityWrite = false;
 	Movie *movie = g_director->getCurrentMovie();
-	Common::Array<CFrame *> &callstack = g_director->getCurrentWindow()->_callstack;
+	Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;
 	for (auto &it : _breakpoints) {
 		if (!it.enabled)
 			continue;
@@ -829,7 +829,7 @@ void Debugger::bpTest(bool forceCheck) {
 
 	// Check if there's a funcName/offset or frame/movie match
 	bool stop = forceCheck;
-	uint funcOffset = g_lingo->_pc;
+	uint funcOffset = g_lingo->_state->pc;
 	Score *score = g_director->getCurrentMovie()->getScore();
 	uint frameOffset = score->getCurrentFrame();
 	if (_bpCheckFunc) {
diff --git a/engines/director/director.cpp b/engines/director/director.cpp
index f3a9dcea0c3..2f4f74a6caa 100644
--- a/engines/director/director.cpp
+++ b/engines/director/director.cpp
@@ -221,6 +221,7 @@ Common::Error DirectorEngine::run() {
 	_currentWindow = _stage;
 
 	_lingo = new Lingo(this);
+	_lingo->switchStateFromWindow();
 
 	if (getGameGID() == GID_TEST) {
 		_currentWindow->runTests();
@@ -248,9 +249,8 @@ Common::Error DirectorEngine::run() {
 			processEvents();
 
 		_currentWindow = _stage;
-		g_lingo->loadStateFromWindow();
+		g_lingo->switchStateFromWindow();
 		loop = _currentWindow->step();
-		g_lingo->saveStateToWindow();
 
 		if (loop) {
 			FArray *windowList = g_lingo->_windowList.u.farr;
@@ -259,9 +259,8 @@ Common::Error DirectorEngine::run() {
 					continue;
 
 				_currentWindow = static_cast<Window *>(windowList->arr[i].u.obj);
-				g_lingo->loadStateFromWindow();
+				g_lingo->switchStateFromWindow();
 				_currentWindow->step();
-				g_lingo->saveStateToWindow();
 			}
 		}
 
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 403d778b395..f4bd63132b3 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -1599,7 +1599,7 @@ void LB::b_quit(int nargs) {
 }
 
 void LB::b_return(int nargs) {
-	CFrame *fp = g_director->getCurrentWindow()->_callstack.back();
+	CFrame *fp = g_lingo->_state->callstack.back();
 
 	Datum retVal;
 	if (nargs > 0) {
@@ -1612,7 +1612,7 @@ void LB::b_return(int nargs) {
 		g_lingo->pop();
 
 	// Do not allow a factory's mNew method to return a value
-	if (nargs > 0 && !(g_lingo->_currentMe.type == OBJECT && g_lingo->_currentMe.u.obj->getObjType() == kFactoryObj
+	if (nargs > 0 && !(g_lingo->_state->me.type == OBJECT && g_lingo->_state->me.u.obj->getObjType() == kFactoryObj
 			&& fp->sp.name->equalsIgnoreCase("mNew"))) {
 		g_lingo->push(retVal);
 	}
@@ -1773,8 +1773,8 @@ void LB::b_showGlobals(int nargs) {
 
 void LB::b_showLocals(int nargs) {
 	Common::String local_out = "-- Local Variables --\n";
-	if (g_lingo->_localvars) {
-		for (auto it = g_lingo->_localvars->begin(); it != g_lingo->_localvars->end(); it++) {
+	if (g_lingo->_state->localVars) {
+		for (auto it = g_lingo->_state->localVars->begin(); it != g_lingo->_state->localVars->end(); it++) {
 			local_out += it->_key + " = " + it->_value.asString() + "\n";
 		}
 	}
diff --git a/engines/director/lingo/lingo-bytecode.cpp b/engines/director/lingo/lingo-bytecode.cpp
index 9ca7aab53f3..0ff165560f3 100644
--- a/engines/director/lingo/lingo-bytecode.cpp
+++ b/engines/director/lingo/lingo-bytecode.cpp
@@ -347,7 +347,7 @@ Datum Lingo::findVarV4(int varType, const Datum &id) {
 	case 4: // arg
 	case 5: // local
 		{
-			Common::Array<CFrame *> &callstack = _vm->getCurrentWindow()->_callstack;
+			Common::Array<CFrame *> &callstack = _state->callstack;
 			if (callstack.empty()) {
 				warning("BUILDBOT: findVarV4: no call frame");
 				return res;
@@ -420,7 +420,7 @@ void LC::cb_localcall() {
 
 	Datum nargs = g_lingo->pop();
 	if ((nargs.type == ARGC) || (nargs.type == ARGCNORET)) {
-		Common::String name = g_lingo->_currentScriptContext->_functionNames[functionId];
+		Common::String name = g_lingo->_state->context->_functionNames[functionId];
 		if (debugChannelSet(3, kDebugLingoExec))
 			printWithArgList(name.c_str(), nargs.u.i, "localcall:");
 
@@ -587,9 +587,9 @@ void LC::cb_theassign() {
 	// cb_theassign is for setting script/factory-level properties
 	Common::String name = g_lingo->readString();
 	Datum value = g_lingo->pop();
-	if (g_lingo->_currentMe.type == OBJECT) {
-		if (g_lingo->_currentMe.u.obj->hasProp(name)) {
-			g_lingo->_currentMe.u.obj->setProp(name, value);
+	if (g_lingo->_state->me.type == OBJECT) {
+		if (g_lingo->_state->me.u.obj->hasProp(name)) {
+			g_lingo->_state->me.u.obj->setProp(name, value);
 		} else {
 			warning("cb_theassign: me object has no property '%s'", name.c_str());
 		}
@@ -616,9 +616,9 @@ void LC::cb_theassign2() {
 
 void LC::cb_thepush() {
 	Common::String name = g_lingo->readString();
-	if (g_lingo->_currentMe.type == OBJECT) {
-		if (g_lingo->_currentMe.u.obj->hasProp(name)) {
-			g_lingo->push(g_lingo->_currentMe.u.obj->getProp(name));
+	if (g_lingo->_state->me.type == OBJECT) {
+		if (g_lingo->_state->me.u.obj->hasProp(name)) {
+			g_lingo->push(g_lingo->_state->me.u.obj->getProp(name));
 			return;
 		}
 		warning("cb_thepush: me object has no property '%s'", name.c_str());
diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 840b96e0bc5..06a6e36d734 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -219,54 +219,39 @@ void LC::c_xpop() {
 	g_lingo->pop();
 }
 
-void Lingo::loadStateFromWindow() {
+void Lingo::switchStateFromWindow() {
 	Window *window = _vm->getCurrentWindow();
-	_pc = window->_retPC;
-	_currentScript = window->_retScript;
-	_currentScriptContext = window->_retContext;
-	_freezeContext = window->_retFreezeContext;
-	_localvars = window->_retLocalVars;
-	_currentMe = window->_retMe;
-}
-
-void Lingo::saveStateToWindow() {
-	Window *window = _vm->getCurrentWindow();
-	window->_retPC = _pc;
-	window->_retScript = _currentScript;
-	window->_retContext = _currentScriptContext;
-	window->_retFreezeContext = _freezeContext;
-	window->_retLocalVars = _localvars;
-	window->_retMe = _currentMe;
+	_state = window->getLingoState();
 }
 
 void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRetVal) {
-	Common::Array<CFrame *> &callstack = _vm->getCurrentWindow()->_callstack;
+	Common::Array<CFrame *> &callstack = _state->callstack;
 
 	debugC(5, kDebugLingoExec, "Pushing frame %d", callstack.size() + 1);
 	CFrame *fp = new CFrame;
 
-	fp->retPC = g_lingo->_pc;
-	fp->retScript = g_lingo->_currentScript;
-	fp->retContext = g_lingo->_currentScriptContext;
-	fp->retFreezeContext = g_lingo->_freezeContext;
-	fp->retLocalVars = g_lingo->_localvars;
-	fp->retMe = g_lingo->_currentMe;
+	fp->retPC = _state->pc;
+	fp->retScript = _state->script;
+	fp->retContext = _state->context;
+	fp->retFreezeContext = _freezeContext;
+	fp->retLocalVars = _state->localVars;
+	fp->retMe = _state->me;
 	fp->sp = funcSym;
 	fp->allowRetVal = allowRetVal;
 	fp->defaultRetVal = defaultRetVal;
 
-	g_lingo->_currentScript = funcSym.u.defn;
+	_state->script = funcSym.u.defn;
 
 	if (funcSym.target)
-		g_lingo->_currentMe = funcSym.target;
+		_state->me = funcSym.target;
 
 	if (funcSym.ctx) {
-		g_lingo->_currentScriptContext = funcSym.ctx;
-		*g_lingo->_currentScriptContext->_refCount += 1;
+		_state->context = funcSym.ctx;
+		*_state->context->_refCount += 1;
 	}
-	g_lingo->_freezeContext = false;
+	_freezeContext = false;
 
-	DatumHash *localvars = g_lingo->_localvars;
+	DatumHash *localvars = _state->localVars;
 	if (!funcSym.anonymous) {
 		// Execute anonymous functions within the current var frame.
 		localvars = new DatumHash;
@@ -278,7 +263,7 @@ void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRet
 			int dropSize = symNArgs - funcSym.argNames->size();
 			warning("%d arg names defined for %d args! Dropping the last %d values", funcSym.argNames->size(), symNArgs, dropSize);
 			for (int i = 0; i < dropSize; i++) {
-				g_lingo->pop();
+				pop();
 				symNArgs -= 1;
 			}
 		} else if ((int)funcSym.argNames->size() > symNArgs) {
@@ -287,11 +272,11 @@ void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRet
 		for (int i = symNArgs - 1; i >= 0; i--) {
 			Common::String name = (*funcSym.argNames)[i];
 			if (!localvars->contains(name)) {
-				Datum value = g_lingo->pop();
+				Datum value = pop();
 				(*localvars)[name] = value;
 			} else {
 				warning("Argument %s already defined", name.c_str());
-				g_lingo->pop();
+				pop();
 			}
 		}
 	}
@@ -305,21 +290,21 @@ void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRet
 			}
 		}
 	}
-	g_lingo->_localvars = localvars;
+	_state->localVars = localvars;
 
 	fp->stackSizeBefore = _stack.size();
 
 	callstack.push_back(fp);
 
 	if (debugChannelSet(2, kDebugLingoExec)) {
-		g_lingo->printCallStack(0);
+		printCallStack(0);
 	}
-	g_lingo->_pc = 0;
+	_state->pc = 0;
 	g_debugger->pushContextHook();
 }
 
 void Lingo::popContext(bool aborting) {
-	Common::Array<CFrame *> &callstack = _vm->getCurrentWindow()->_callstack;
+	Common::Array<CFrame *> &callstack = _state->callstack;
 
 	debugC(5, kDebugLingoExec, "Popping frame %d", callstack.size());
 	CFrame *fp = callstack.back();
@@ -328,7 +313,7 @@ void Lingo::popContext(bool aborting) {
 	if (_stack.size() == fp->stackSizeBefore + 1) {
 		if (!fp->allowRetVal) {
 			debugC(5, kDebugLingoExec, "dropping return value");
-			g_lingo->pop();
+			pop();
 		}
 	} else if (_stack.size() == fp->stackSizeBefore) {
 		if (fp->allowRetVal) {
@@ -337,14 +322,14 @@ void Lingo::popContext(bool aborting) {
 			if (fp->defaultRetVal.type == VOID) {
 				warning("handler %s did not return value", fp->sp.name->c_str());
 			}
-			g_lingo->push(fp->defaultRetVal);
+			push(fp->defaultRetVal);
 		}
 	} else if (_stack.size() > fp->stackSizeBefore) {
 		if (aborting) {
 			// Since we're aborting execution, we should expect that some extra
 			// values are left on the stack.
 			while (_stack.size() > fp->stackSizeBefore) {
-				g_lingo->pop();
+				pop();
 			}
 		} else {
 			error("handler %s returned extra %d values", fp->sp.name->c_str(), _stack.size() - fp->stackSizeBefore);
@@ -353,25 +338,25 @@ void Lingo::popContext(bool aborting) {
 		error("handler %s popped extra %d values", fp->sp.name->c_str(), fp->stackSizeBefore - _stack.size());
 	}
 
-	*g_lingo->_currentScriptContext->_refCount -= 1;
-	if (*g_lingo->_currentScriptContext->_refCount <= 0) {
-		delete g_lingo->_currentScriptContext;
+	*_state->context->_refCount -= 1;
+	if (*_state->context->_refCount <= 0) {
+		delete _state->context;
 	}
 
-	g_lingo->_currentScript = fp->retScript;
-	g_lingo->_currentScriptContext = fp->retContext;
-	g_lingo->_freezeContext = fp->retFreezeContext;
-	g_lingo->_pc = fp->retPC;
-	g_lingo->_currentMe = fp->retMe;
+	_state->script = fp->retScript;
+	_state->context = fp->retContext;
+	_freezeContext = fp->retFreezeContext;
+	_state->pc = fp->retPC;
+	_state->me = fp->retMe;
 
 	// Restore local variables
 	if (!fp->sp.anonymous) {
-		g_lingo->cleanLocalVars();
-		g_lingo->_localvars = fp->retLocalVars;
+		cleanLocalVars();
+		_state->localVars = fp->retLocalVars;
 	}
 
 	if (debugChannelSet(2, kDebugLingoExec)) {
-		g_lingo->printCallStack(g_lingo->_pc);
+		printCallStack(_state->pc);
 	}
 
 	delete fp;
@@ -380,10 +365,10 @@ void Lingo::popContext(bool aborting) {
 }
 
 bool Lingo::hasFrozenContext() {
-	if (g_lingo->_freezeContext)
+	if (_freezeContext)
 		return true;
 
-	Common::Array<CFrame *> &callstack = _vm->getCurrentWindow()->_callstack;
+	Common::Array<CFrame *> &callstack = _state->callstack;
 	for (uint i = 0; i < callstack.size(); i++) {
 		if (callstack[i]->retFreezeContext)
 			return true;
@@ -1419,14 +1404,14 @@ void LC::c_le() {
 
 void LC::c_jump() {
 	int jump = g_lingo->readInt();
-	g_lingo->_pc = g_lingo->_pc + jump - 2;
+	g_lingo->_state->pc = g_lingo->_state->pc + jump - 2;
 }
 
 void LC::c_jumpifz() {
 	int jump = g_lingo->readInt();
 	int test = g_lingo->pop().asInt();
 	if (test == 0) {
-		g_lingo->_pc = g_lingo->_pc + jump - 2;
+		g_lingo->_state->pc = g_lingo->_state->pc + jump - 2;
 	}
 }
 
@@ -1642,10 +1627,10 @@ void LC::call(const Symbol &funcSym, int nargs, bool allowRetVal) {
 		if (target.type != VOID) {
 			// Only need to update the me obj
 			// Pushing an entire stack frame is not necessary
-			Datum retMe = g_lingo->_currentMe;
-			g_lingo->_currentMe = target;
+			Datum retMe = g_lingo->_state->me;
+			g_lingo->_state->me = target;
 			(*funcSym.u.bltin)(nargs);
-			g_lingo->_currentMe = retMe;
+			g_lingo->_state->me = retMe;
 		} else {
 			(*funcSym.u.bltin)(nargs);
 		}
@@ -1679,7 +1664,7 @@ void LC::call(const Symbol &funcSym, int nargs, bool allowRetVal) {
 }
 
 void LC::c_procret() {
-	Common::Array<CFrame *> &callstack = g_director->getCurrentWindow()->_callstack;
+	Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;
 
 	if (callstack.size() == 0) {
 		warning("LC::c_procret(): Call stack underflow");
diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 9c65b74eac4..274decc859f 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -269,11 +269,11 @@ void Lingo::reloadOpenXLibs() {
 void LM::m_new(int nargs) {
 	// This is usually overridden by a user-defined mNew
 	g_lingo->printSTUBWithArglist("m_new", nargs);
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void LM::m_dispose(int nargs) {
-	g_lingo->_currentMe.u.obj->dispose();
+	g_lingo->_state->me.u.obj->dispose();
 }
 
 /* ScriptContext */
@@ -418,7 +418,7 @@ Common::String ScriptContext::formatFunctionList(const char *prefix) {
 // Object array
 
 void LM::m_get(int nargs) {
-	ScriptContext *me = static_cast<ScriptContext *>(g_lingo->_currentMe.u.obj);
+	ScriptContext *me = static_cast<ScriptContext *>(g_lingo->_state->me.u.obj);
 	Datum indexD = g_lingo->pop();
 	uint index = MAX(0, indexD.asInt());
 	if (me->_objArray.contains(index)) {
@@ -429,7 +429,7 @@ void LM::m_get(int nargs) {
 }
 
 void LM::m_put(int nargs) {
-	ScriptContext *me = static_cast<ScriptContext *>(g_lingo->_currentMe.u.obj);
+	ScriptContext *me = static_cast<ScriptContext *>(g_lingo->_state->me.u.obj);
 	Datum value = g_lingo->pop();
 	Datum indexD = g_lingo->pop();
 	uint index = MAX(0, indexD.asInt());
@@ -441,7 +441,7 @@ void LM::m_put(int nargs) {
 void LM::m_perform(int nargs) {
 	// Lingo doesn't seem to bother cloning the object when
 	// mNew is called with mPerform
-	Datum d(g_lingo->_currentMe);
+	Datum d(g_lingo->_state->me);
 	AbstractObject *me = d.u.obj;
 	Datum methodName = g_lingo->_stack.remove_at(g_lingo->_stack.size() - nargs); // Take method name out of stack
 	Symbol funcSym = me->getMethod(*methodName.u.s);
@@ -457,7 +457,7 @@ void LM::m_describe(int nargs) {
 }
 
 void LM::m_instanceRespondsTo(int nargs) {
-	AbstractObject *me = g_lingo->_currentMe.u.obj;
+	AbstractObject *me = g_lingo->_state->me.u.obj;
 	Datum d = g_lingo->pop();
 	Common::String methodName = d.asString();
 
@@ -474,12 +474,12 @@ void LM::m_messageList(int nargs) {
 }
 
 void LM::m_name(int nargs) {
-	AbstractObject *me = g_lingo->_currentMe.u.obj;
+	AbstractObject *me = g_lingo->_state->me.u.obj;
 	g_lingo->push(me->getName());
 }
 
 void LM::m_respondsTo(int nargs) {
-	AbstractObject *me = g_lingo->_currentMe.u.obj;
+	AbstractObject *me = g_lingo->_state->me.u.obj;
 	Datum d = g_lingo->pop();
 	Common::String methodName = d.asString();
 
@@ -580,12 +580,12 @@ bool Window::setField(int field, const Datum &value) {
 }
 
 void LM::m_close(int nargs) {
-	Window *me = static_cast<Window *>(g_lingo->_currentMe.u.obj);
+	Window *me = static_cast<Window *>(g_lingo->_state->me.u.obj);
 	me->setVisible(false);
 }
 
 void LM::m_forget(int nargs) {
-	Window *me = static_cast<Window *>(g_lingo->_currentMe.u.obj);
+	Window *me = static_cast<Window *>(g_lingo->_state->me.u.obj);
 	FArray *windowList = g_lingo->_windowList.u.farr;
 
 	uint i;
@@ -613,7 +613,7 @@ void LM::m_forget(int nargs) {
 }
 
 void LM::m_open(int nargs) {
-	Window *me = static_cast<Window *>(g_lingo->_currentMe.u.obj);
+	Window *me = static_cast<Window *>(g_lingo->_state->me.u.obj);
 	me->setVisible(true);
 }
 
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index 8152d4c201f..1e95ae6ba70 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -152,12 +152,9 @@ MenuReference::MenuReference() {
 Lingo::Lingo(DirectorEngine *vm) : _vm(vm) {
 	g_lingo = this;
 
-	_currentScript = nullptr;
-	_currentScriptContext = nullptr;
-
+	_state = nullptr;
 	_currentChannelId = -1;
 	_globalCounter = 0;
-	_pc = 0;
 	_freezeContext = false;
 	_abort = false;
 	_expectError = false;
@@ -166,8 +163,6 @@ Lingo::Lingo(DirectorEngine *vm) : _vm(vm) {
 	_floatPrecision = 4;
 	_floatPrecisionFormat = "%.4f";
 
-	_localvars = nullptr;
-
 	//kTheEntities
 	_actorList.type = ARRAY;
 	_actorList.u.farr = new FArray;
@@ -293,8 +288,8 @@ Symbol Lingo::getHandler(const Common::String &name) {
 	Symbol sym;
 
 	// local functions
-	if (_currentScriptContext && _currentScriptContext->_functionHandlers.contains(name))
-		return _currentScriptContext->_functionHandlers[name];
+	if (_state->context && _state->context->_functionHandlers.contains(name))
+		return _state->context->_functionHandlers[name];
 
 	sym = g_director->getCurrentMovie()->getHandler(name);
 	if (sym.type != VOIDSYM)
@@ -366,7 +361,7 @@ void Lingo::printStack(const char *s, uint pc) {
 
 Common::String Lingo::formatCallStack(uint pc) {
 	Common::String result;
-	Common::Array<CFrame *> &callstack = _vm->getCurrentWindow()->_callstack;
+	Common::Array<CFrame *> &callstack = _state->callstack;
 	if (callstack.size() == 0) {
 		result += Common::String("End of execution\n");
 		return result;
@@ -402,26 +397,26 @@ void Lingo::printCallStack(uint pc) {
 
 Common::String Lingo::formatFrame() {
 	Common::String result;
-	Common::Array<CFrame *> &callstack = _vm->getCurrentWindow()->_callstack;
+	Common::Array<CFrame *> &callstack = _state->callstack;
 	if (callstack.size() == 0) {
 		return Common::String("End of execution");
 	}
-	if (_currentScriptContext->_id)
-		result += Common::String::format("%d:", _currentScriptContext->_id);
+	if (_state->context->_id)
+		result += Common::String::format("%d:", _state->context->_id);
 	CFrame *frame = callstack[callstack.size() - 1];
 	if (frame->sp.type == VOIDSYM || !frame->sp.name)
 		result += "[unknown]";
 	else
 		result += frame->sp.name->c_str();
-	result += Common::String::format(" at [%5d]", _pc);
+	result += Common::String::format(" at [%5d]", _state->pc);
 	return result;
 }
 
 Common::String Lingo::formatCurrentInstruction() {
-	Common::String instr = decodeInstruction(_currentScript, _pc);
+	Common::String instr = decodeInstruction(_state->script, _state->pc);
 	if (instr.empty())
 		return instr;
-	return Common::String::format("[%5d]: %s", _pc, instr.c_str());
+	return Common::String::format("[%5d]: %s", _state->pc, instr.c_str());
 }
 
 Common::String Lingo::decodeInstruction(ScriptData *sd, uint pc, uint *newPc) {
@@ -546,7 +541,7 @@ Common::String Lingo::formatFunctionBody(Symbol &sym) {
 void Lingo::execute() {
 	uint localCounter = 0;
 
-	while (!_abort && !_freezeContext && _currentScript && (*_currentScript)[_pc] != STOP) {
+	while (!_abort && !_freezeContext && _state->script && (*_state->script)[_state->pc] != STOP) {
 		if (_globalCounter > 1000 && debugChannelSet(-1, kDebugFewFramesOnly)) {
 			warning("Lingo::execute(): Stopping due to debug few frames only");
 			_vm->getCurrentMovie()->getScore()->_playState = kPlayStopped;
@@ -561,7 +556,7 @@ void Lingo::execute() {
 				break;
 		}
 
-		uint current = _pc;
+		uint current = _state->pc;
 
 		if (debugChannelSet(5, kDebugLingoExec))
 			printStack("Stack before: ", current);
@@ -569,19 +564,19 @@ void Lingo::execute() {
 		if (debugChannelSet(9, kDebugLingoExec)) {
 			debug("Vars before");
 			printAllVars();
-			if (_currentMe.type == OBJECT)
-				debug("me: %s", _currentMe.asString(true).c_str());
+			if (_state->me.type == OBJECT)
+				debug("me: %s", _state->me.asString(true).c_str());
 		}
 
 		if (debugChannelSet(3, kDebugLingoExec)) {
-			Common::String instr = decodeInstruction(_currentScript, _pc);
+			Common::String instr = decodeInstruction(_state->script, _state->pc);
 			debugC(3, kDebugLingoExec, "[%5d]: %s", current, instr.c_str());
 		}
 
 		g_debugger->stepHook();
 
-		_pc++;
-		(*((*_currentScript)[_pc - 1]))();
+		_state->pc++;
+		(*((*_state->script)[_state->pc - 1]))();
 
 		if (debugChannelSet(5, kDebugLingoExec))
 			printStack("Stack after: ", current);
@@ -594,15 +589,15 @@ void Lingo::execute() {
 		_globalCounter++;
 		localCounter++;
 
-		if (!_abort && _pc >= (*_currentScript).size()) {
-			warning("Lingo::execute(): Bad PC (%d)", _pc);
+		if (!_abort && _state->pc >= (*_state->script).size()) {
+			warning("Lingo::execute(): Bad PC (%d)", _state->pc);
 			break;
 		}
 	}
 
 	if (_abort || _vm->getCurrentMovie()->getScore()->_playState == kPlayStopped) {
 		// Clean up call stack
-		while (_vm->getCurrentWindow()->_callstack.size()) {
+		while (_state->callstack.size()) {
 			popContext(true);
 		}
 	}
@@ -672,7 +667,7 @@ void Lingo::resetLingo() {
 
 	g_director->_wm->removeMenu();
 
-	while (_vm->getCurrentWindow()->_callstack.size()) {
+	while (_state->callstack.size()) {
 		popContext(true);
 	}
 
@@ -1332,9 +1327,9 @@ void Lingo::executeImmediateScripts(Frame *frame) {
 			// From D5 only explicit event handlers are processed
 			// Before that you could specify commands which will be executed on mouse up
 			if (_vm->getVersion() < 500)
-				g_lingo->processEvent(kEventGeneric, kScoreScript, frame->_sprites[i]->_scriptId, i);
+				processEvent(kEventGeneric, kScoreScript, frame->_sprites[i]->_scriptId, i);
 			else
-				g_lingo->processEvent(kEventMouseUp, kScoreScript, frame->_sprites[i]->_scriptId, i);
+				processEvent(kEventMouseUp, kScoreScript, frame->_sprites[i]->_scriptId, i);
 		}
 	}
 }
@@ -1366,20 +1361,20 @@ void Lingo::executePerFrameHook(int frame, int subframe) {
 
 void Lingo::cleanLocalVars() {
 	// Clean up current scope local variables and clean up memory
-	debugC(3, kDebugLingoExec, "cleanLocalVars: have %d vars", _localvars->size());
+	debugC(3, kDebugLingoExec, "cleanLocalVars: have %d vars", _state->localVars->size());
 
-	g_lingo->_localvars->clear();
-	delete g_lingo->_localvars;
+	_state->localVars->clear();
+	delete _state->localVars;
 
-	g_lingo->_localvars = nullptr;
+	_state->localVars = nullptr;
 }
 
 Common::String Lingo::formatAllVars() {
 	Common::String result;
 
 	result += Common::String("  Local vars:\n");
-	if (_localvars) {
-		for (DatumHash::iterator i = _localvars->begin(); i != _localvars->end(); ++i) {
+	if (_state->localVars) {
+		for (DatumHash::iterator i = _state->localVars->begin(); i != _state->localVars->end(); ++i) {
 			result += Common::String::format("    %s - [%s] %s\n", (*i)._key.c_str(), (*i)._value.type2str(), (*i)._value.asString(true).c_str());
 		}
 	} else {
@@ -1387,8 +1382,8 @@ Common::String Lingo::formatAllVars() {
 	}
 	result += Common::String("\n");
 
-	if (_currentMe.type == OBJECT && _currentMe.u.obj->getObjType() & (kFactoryObj | kScriptObj)) {
-		ScriptContext *script = static_cast<ScriptContext *>(_currentMe.u.obj);
+	if (_state->me.type == OBJECT && _state->me.u.obj->getObjType() & (kFactoryObj | kScriptObj)) {
+		ScriptContext *script = static_cast<ScriptContext *>(_state->me.u.obj);
 		result += Common::String("  Instance/property vars: \n");
 		for (DatumHash::iterator i = script->_properties.begin(); i != script->_properties.end(); ++i) {
 			result += Common::String::format("    %s - [%s] %s\n", (*i)._key.c_str(), (*i)._value.type2str(), (*i)._value.asString(true).c_str());
@@ -1409,7 +1404,7 @@ void Lingo::printAllVars() {
 }
 
 int Lingo::getInt(uint pc) {
-	return (int)READ_UINT32(&((*_currentScript)[pc]));
+	return (int)READ_UINT32(&((*_state->script)[pc]));
 }
 
 void Lingo::varAssign(const Datum &var, const Datum &value) {
@@ -1417,13 +1412,13 @@ void Lingo::varAssign(const Datum &var, const Datum &value) {
 	case VARREF:
 		{
 			Common::String name = *var.u.s;
-			if (_localvars && _localvars->contains(name)) {
-				(*_localvars)[name] = value;
+			if (_state->localVars && _state->localVars->contains(name)) {
+				(*_state->localVars)[name] = value;
 				g_debugger->varWriteHook(name);
 				return;
 			}
-			if (_currentMe.type == OBJECT && _currentMe.u.obj->hasProp(name)) {
-				_currentMe.u.obj->setProp(name, value);
+			if (_state->me.type == OBJECT && _state->me.u.obj->hasProp(name)) {
+				_state->me.u.obj->setProp(name, value);
 				g_debugger->varWriteHook(name);
 				return;
 			}
@@ -1441,8 +1436,8 @@ void Lingo::varAssign(const Datum &var, const Datum &value) {
 	case LOCALREF:
 		{
 			Common::String name = *var.u.s;
-			if (_localvars && _localvars->contains(name)) {
-				(*_localvars)[name] = value;
+			if (_state->localVars && _state->localVars->contains(name)) {
+				(*_state->localVars)[name] = value;
 				g_debugger->varWriteHook(name);
 			} else {
 				warning("varAssign: local variable %s not defined", name.c_str());
@@ -1452,8 +1447,8 @@ void Lingo::varAssign(const Datum &var, const Datum &value) {
 	case PROPREF:
 		{
 			Common::String name = *var.u.s;
-			if (_currentMe.type == OBJECT && _currentMe.u.obj->hasProp(name)) {
-				_currentMe.u.obj->setProp(name, value);
+			if (_state->me.type == OBJECT && _state->me.u.obj->hasProp(name)) {
+				_state->me.u.obj->setProp(name, value);
 				g_debugger->varWriteHook(name);
 			} else {
 				warning("varAssign: property %s not defined", name.c_str());
@@ -1536,11 +1531,11 @@ Datum Lingo::varFetch(const Datum &var, bool silent) {
 			Common::String name = *var.u.s;
 			g_debugger->varReadHook(name);
 
-			if (_localvars && _localvars->contains(name)) {
-				return (*_localvars)[name];
+			if (_state->localVars && _state->localVars->contains(name)) {
+				return (*_state->localVars)[name];
 			}
-			if (_currentMe.type == OBJECT && _currentMe.u.obj->hasProp(name)) {
-				return _currentMe.u.obj->getProp(name);
+			if (_state->me.type == OBJECT && _state->me.u.obj->hasProp(name)) {
+				return _state->me.u.obj->getProp(name);
 			}
 			if (_globalvars.contains(name)) {
 				return _globalvars[name];
@@ -1566,8 +1561,8 @@ Datum Lingo::varFetch(const Datum &var, bool silent) {
 		{
 			Common::String name = *var.u.s;
 			g_debugger->varReadHook(name);
-			if (_localvars && _localvars->contains(name)) {
-				return (*_localvars)[name];
+			if (_state->localVars && _state->localVars->contains(name)) {
+				return (*_state->localVars)[name];
 			}
 			warning("varFetch: local variable %s not defined", name.c_str());
 			return result;
@@ -1577,8 +1572,8 @@ Datum Lingo::varFetch(const Datum &var, bool silent) {
 		{
 			Common::String name = *var.u.s;
 			g_debugger->varReadHook(name);
-			if (_currentMe.type == OBJECT && _currentMe.u.obj->hasProp(name)) {
-				return _currentMe.u.obj->getProp(name);
+			if (_state->me.type == OBJECT && _state->me.u.obj->hasProp(name)) {
+				return _state->me.u.obj->getProp(name);
 			}
 			warning("varFetch: property %s not defined", name.c_str());
 			return result;
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index e189b016357..8c332559efa 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -291,6 +291,15 @@ struct LingoArchive {
 	void addNamesV4(Common::SeekableReadStreamEndian &stream);
 };
 
+struct LingoState {
+	Common::Array<CFrame *> callstack;
+	uint pc = 0;
+	ScriptData *script = nullptr;
+	ScriptContext *context = nullptr;
+	DatumHash *localVars = nullptr;
+	Datum me;
+};
+
 class Lingo {
 
 public:
@@ -348,8 +357,7 @@ public:
 
 public:
 	void execute();
-	void loadStateFromWindow();
-	void saveStateToWindow();
+	void switchStateFromWindow();
 	void pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRetVal);
 	void popContext(bool aborting = false);
 	bool hasFrozenContext();
@@ -366,14 +374,14 @@ public:
 	Common::String formatAllVars();
 	void printAllVars();
 
-	inst readInst() { return getInst(_pc++); }
-	inst getInst(uint pc) { return (*_currentScript)[pc]; }
-	int readInt() { return getInt(_pc++); }
+	inst readInst() { return getInst(_state->pc++); }
+	inst getInst(uint pc) { return (*_state->script)[pc]; }
+	int readInt() { return getInt(_state->pc++); }
 	int getInt(uint pc);
-	double readFloat() { double d = getFloat(_pc); _pc += calcCodeAlignment(sizeof(double)); return d; }
-	double getFloat(uint pc) { return *(double *)(&((*_currentScript)[pc])); }
-	char *readString() { char *s = getString(_pc); _pc += calcStringAlignment(s); return s; }
-	char *getString(uint pc) { return (char *)(&((*_currentScript)[pc])); }
+	double readFloat() { double d = getFloat(_state->pc); _state->pc += calcCodeAlignment(sizeof(double)); return d; }
+	double getFloat(uint pc) { return *(double *)(&((*_state->script)[_state->pc])); }
+	char *readString() { char *s = getString(_state->pc); _state->pc += calcStringAlignment(s); return s; }
+	char *getString(uint pc) { return (char *)(&((*_state->script)[_state->pc])); }
 
 	void pushVoid();
 
@@ -436,11 +444,9 @@ private:
 
 public:
 	LingoCompiler *_compiler;
+	LingoState *_state;
 
 	int _currentChannelId;
-	ScriptContext *_currentScriptContext;
-	ScriptData *_currentScript;
-	Datum _currentMe;
 
 	bool _freezeContext;
 	bool _abort;
@@ -474,7 +480,6 @@ public:
 	Common::HashMap<Common::String, Audio::AudioStream *> _audioAliases;
 
 	DatumHash _globalvars;
-	DatumHash *_localvars;
 
 	FuncHash _functions;
 
@@ -482,7 +487,6 @@ public:
 	Common::HashMap<int, LingoV4TheEntity *> _lingoV4TheEntity;
 
 	uint _globalCounter;
-	uint _pc;
 
 	StackData _stack;
 
diff --git a/engines/director/lingo/xlibs/aiff.cpp b/engines/director/lingo/xlibs/aiff.cpp
index 5cdc5dbe624..22603afd956 100644
--- a/engines/director/lingo/xlibs/aiff.cpp
+++ b/engines/director/lingo/xlibs/aiff.cpp
@@ -89,7 +89,7 @@ AiffXObject::AiffXObject(ObjectType ObjectType) :Object<AiffXObject>("AiffXObj")
 
 void AiffXObj::m_new(int nargs) {
 	g_lingo->printSTUBWithArglist("AiffXObj::new", nargs);
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void AiffXObj::m_duration(int nargs) {
diff --git a/engines/director/lingo/xlibs/applecdxobj.cpp b/engines/director/lingo/xlibs/applecdxobj.cpp
index 352458423c4..c9e50a4be04 100644
--- a/engines/director/lingo/xlibs/applecdxobj.cpp
+++ b/engines/director/lingo/xlibs/applecdxobj.cpp
@@ -125,7 +125,7 @@ AppleCDXObject::AppleCDXObject(ObjectType ObjectType) :Object<AppleCDXObject>("A
 }
 
 void AppleCDXObj::m_new(int nargs) {
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void AppleCDXObj::m_service(int nargs) {
diff --git a/engines/director/lingo/xlibs/askuser.cpp b/engines/director/lingo/xlibs/askuser.cpp
index f8c9de09a64..74d998615c2 100644
--- a/engines/director/lingo/xlibs/askuser.cpp
+++ b/engines/director/lingo/xlibs/askuser.cpp
@@ -85,7 +85,7 @@ void AskUser::m_new(int nargs) {
 		warning("AskUser::m_new: expected 0 arguments");
 		g_lingo->dropStack(nargs);
 	}
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void AskUser::m_ask(int nargs) {
diff --git a/engines/director/lingo/xlibs/barakeobj.cpp b/engines/director/lingo/xlibs/barakeobj.cpp
index 244cc0e5745..7efdf2ce26e 100644
--- a/engines/director/lingo/xlibs/barakeobj.cpp
+++ b/engines/director/lingo/xlibs/barakeobj.cpp
@@ -77,7 +77,7 @@ BarakeObject::BarakeObject(ObjectType ObjectType) :Object<BarakeObject>("BarakeO
 }
 
 void BarakeObj::m_new(int nargs) {
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void BarakeObj::m_clear(int nargs) {
diff --git a/engines/director/lingo/xlibs/cdromxobj.cpp b/engines/director/lingo/xlibs/cdromxobj.cpp
index f811435b1d0..041fecdd36b 100644
--- a/engines/director/lingo/xlibs/cdromxobj.cpp
+++ b/engines/director/lingo/xlibs/cdromxobj.cpp
@@ -234,7 +234,7 @@ void CDROMXObj::m_new(int nargs) {
 	g_director->_system->getAudioCDManager()->open();
 	g_lingo->printSTUBWithArglist("CDROMXObj::m_new", nargs);
 	g_lingo->dropStack(nargs);
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 // Returns the name of the XObj
@@ -243,7 +243,7 @@ void CDROMXObj::m_name(int nargs) {
 }
 
 void CDROMXObj::m_play(int nargs) {
-	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_currentMe.u.obj);
+	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_state->me.u.obj);
 
 	// This is a request to play the current track from the start,
 	// which we can't do if there's no track information.
@@ -255,7 +255,7 @@ void CDROMXObj::m_play(int nargs) {
 }
 
 void CDROMXObj::m_playTrack(int nargs) {
-	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_currentMe.u.obj);
+	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_state->me.u.obj);
 
 	int track = g_lingo->pop().asInt();
 	g_director->_system->getAudioCDManager()->play(track, -1, 0, 0);
@@ -264,7 +264,7 @@ void CDROMXObj::m_playTrack(int nargs) {
 
 // Name format is "TRACK NN", with one-digit tracks padded with a leading space
 void CDROMXObj::m_playName(int nargs) {
-	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_currentMe.u.obj);
+	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_state->me.u.obj);
 
 	Common::String track = g_lingo->pop().asString();
 	if (track.size() < 8) {
@@ -315,14 +315,14 @@ void CDROMXObj::m_askPlay(int nargs) {
 }
 
 void CDROMXObj::m_stepFwd(int nargs) {
-	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_currentMe.u.obj);
+	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_state->me.u.obj);
 
 	g_director->_system->getAudioCDManager()->play(me->_cdda_status.track + 1, -1, 0, 0);
 	me->_cdda_status = g_director->_system->getAudioCDManager()->getStatus();
 }
 
 void CDROMXObj::m_stepBwd(int nargs) {
-	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_currentMe.u.obj);
+	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_state->me.u.obj);
 
 	int track = me->_cdda_status.track - 1;
 	if (track < 1)
@@ -333,7 +333,7 @@ void CDROMXObj::m_stepBwd(int nargs) {
 }
 
 void CDROMXObj::m_pause(int nargs) {
-	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_currentMe.u.obj);
+	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_state->me.u.obj);
 
 	// Leaves a trace of the current position so we can resume from it
 	me->_cdda_status = g_director->_system->getAudioCDManager()->getStatus();
@@ -342,7 +342,7 @@ void CDROMXObj::m_pause(int nargs) {
 }
 
 void CDROMXObj::m_continue(int nargs) {
-	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_currentMe.u.obj);
+	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_state->me.u.obj);
 
 	// Can only resume if there's data to resume from
 	if (me->_cdda_status.track == 0)
@@ -353,14 +353,14 @@ void CDROMXObj::m_continue(int nargs) {
 }
 
 void CDROMXObj::m_stop(int nargs) {
-	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_currentMe.u.obj);
+	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_state->me.u.obj);
 
 	g_director->_system->getAudioCDManager()->stop();
 	me->_cdda_status = g_director->_system->getAudioCDManager()->getStatus();
 }
 
 void CDROMXObj::m_stopTrack(int nargs) {
-	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_currentMe.u.obj);
+	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_state->me.u.obj);
 
 	Datum track = g_lingo->pop();
 	AudioCDManager::Status status = g_director->_system->getAudioCDManager()->getStatus();
@@ -387,7 +387,7 @@ void CDROMXObj::m_stopAbsTime(int nargs) {
 }
 
 void CDROMXObj::m_removeStop(int nargs) {
-	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_currentMe.u.obj);
+	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_state->me.u.obj);
 
 	Datum track = g_lingo->pop();
 	AudioCDManager::Status status = g_director->_system->getAudioCDManager()->getStatus();
@@ -459,7 +459,7 @@ void CDROMXObj::m_currentFormat(int nargs) {
 }
 
 void CDROMXObj::m_currentTrack(int nargs) {
-	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_currentMe.u.obj);
+	CDROMXObject *me = static_cast<CDROMXObject *>(g_lingo->_state->me.u.obj);
 
 	g_lingo->push(Datum(me->_cdda_status.track));
 }
diff --git a/engines/director/lingo/xlibs/ednox.cpp b/engines/director/lingo/xlibs/ednox.cpp
index c22a24c9c87..39abd7a5c45 100644
--- a/engines/director/lingo/xlibs/ednox.cpp
+++ b/engines/director/lingo/xlibs/ednox.cpp
@@ -114,7 +114,7 @@ EdnoxObject::EdnoxObject(ObjectType ObjectType) :Object<EdnoxObject>("Ednox") {
 }
 
 void Ednox::m_new(int nargs) {
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void Ednox::m_dispose(int nargs) {
diff --git a/engines/director/lingo/xlibs/fileio.cpp b/engines/director/lingo/xlibs/fileio.cpp
index 74ca661d83a..7313174bebd 100644
--- a/engines/director/lingo/xlibs/fileio.cpp
+++ b/engines/director/lingo/xlibs/fileio.cpp
@@ -195,7 +195,7 @@ void FileIO::saveFileError() {
 }
 
 void FileIO::m_new(int nargs) {
-	FileObject *me = static_cast<FileObject *>(g_lingo->_currentMe.u.obj);
+	FileObject *me = static_cast<FileObject *>(g_lingo->_state->me.u.obj);
 
 	Datum d2 = g_lingo->pop();
 	Datum d1 = g_lingo->pop();
@@ -284,13 +284,13 @@ void FileIO::m_new(int nargs) {
 
 	me->_filename = new Common::String(filename);
 
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 // Read
 
 void FileIO::m_readChar(int nargs) {
-	FileObject *me = static_cast<FileObject *>(g_lingo->_currentMe.u.obj);
+	FileObject *me = static_cast<FileObject *>(g_lingo->_state->me.u.obj);
 
 	if (!me->_inStream || me->_inStream->eos() || me->_inStream->err()) {
 		g_lingo->push(Datum(kErrorEOF));
@@ -334,7 +334,7 @@ bool FileIO::charInMatchString(char ch, const Common::String &matchString) {
 }
 
 void FileIO::m_readToken(int nargs) {
-	FileObject *me = static_cast<FileObject *>(g_lingo->_currentMe.u.obj);
+	FileObject *me = static_cast<FileObject *>(g_lingo->_state->me.u.obj);
 
 	Datum d2 = g_lingo->pop();
 	Datum d1 = g_lingo->pop();
@@ -378,7 +378,7 @@ void FileIO::m_readToken(int nargs) {
 }
 
 void FileIO::m_readFile(int nargs) {
-	FileObject *me = static_cast<FileObject *>(g_lingo->_currentMe.u.obj);
+	FileObject *me = static_cast<FileObject *>(g_lingo->_state->me.u.obj);
 
 	if (!me->_inStream || me->_inStream->eos() || me->_inStream->err()) {
 		g_lingo->push(Datum(""));
@@ -398,7 +398,7 @@ void FileIO::m_readFile(int nargs) {
 // Write
 
 void FileIO::m_writeChar(int nargs) {
-	FileObject *me = static_cast<FileObject *>(g_lingo->_currentMe.u.obj);
+	FileObject *me = static_cast<FileObject *>(g_lingo->_state->me.u.obj);
 	Datum d = g_lingo->pop();
 
 	if (!me->_outStream) {
@@ -411,7 +411,7 @@ void FileIO::m_writeChar(int nargs) {
 }
 
 void FileIO::m_writeString(int nargs) {
-	FileObject *me = static_cast<FileObject *>(g_lingo->_currentMe.u.obj);
+	FileObject *me = static_cast<FileObject *>(g_lingo->_state->me.u.obj);
 	Datum d = g_lingo->pop();
 
 	if (!me->_outStream) {
@@ -438,7 +438,7 @@ void FileIO::m_setFinderInfo(int nargs) {
 }
 
 void FileIO::m_getPosition(int nargs) {
-	FileObject *me = static_cast<FileObject *>(g_lingo->_currentMe.u.obj);
+	FileObject *me = static_cast<FileObject *>(g_lingo->_state->me.u.obj);
 
 	if (me->_inStream) {
 		g_lingo->push(Datum((int)me->_inStream->pos()));
@@ -451,7 +451,7 @@ void FileIO::m_getPosition(int nargs) {
 }
 
 void FileIO::m_setPosition(int nargs) {
-	FileObject *me = static_cast<FileObject *>(g_lingo->_currentMe.u.obj);
+	FileObject *me = static_cast<FileObject *>(g_lingo->_state->me.u.obj);
 	Datum d = g_lingo->pop();
 	int pos = d.asInt();
 
@@ -478,7 +478,7 @@ void FileIO::m_setPosition(int nargs) {
 }
 
 void FileIO::m_getLength(int nargs) {
-	FileObject *me = static_cast<FileObject *>(g_lingo->_currentMe.u.obj);
+	FileObject *me = static_cast<FileObject *>(g_lingo->_state->me.u.obj);
 
 	if (me->_inStream) {
 		g_lingo->push(Datum((int)me->_inStream->size()));
@@ -491,7 +491,7 @@ void FileIO::m_getLength(int nargs) {
 }
 
 void FileIO::m_fileName(int nargs) {
-	FileObject *me = static_cast<FileObject *>(g_lingo->_currentMe.u.obj);
+	FileObject *me = static_cast<FileObject *>(g_lingo->_state->me.u.obj);
 
 	if (me->_filename) {
 		Common::String prefix = g_director->getTargetName() + '-';
@@ -522,7 +522,7 @@ void FileIO::m_status(int nargs) {
 // Other
 
 void FileIO::m_delete(int nargs) {
-	FileObject *me = static_cast<FileObject *>(g_lingo->_currentMe.u.obj);
+	FileObject *me = static_cast<FileObject *>(g_lingo->_state->me.u.obj);
 
 	if (me->_filename) {
 		Common::String filename = *me->_filename;
diff --git a/engines/director/lingo/xlibs/flushxobj.cpp b/engines/director/lingo/xlibs/flushxobj.cpp
index 717ba94c020..a0b87e73aea 100644
--- a/engines/director/lingo/xlibs/flushxobj.cpp
+++ b/engines/director/lingo/xlibs/flushxobj.cpp
@@ -88,7 +88,7 @@ FlushXObject::FlushXObject(ObjectType ObjectType) :Object<FlushXObject>("FlushXO
 }
 
 void FlushXObj::m_new(int nargs) {
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void FlushXObj::m_clearMask(int nargs) {
diff --git a/engines/director/lingo/xlibs/gpid.cpp b/engines/director/lingo/xlibs/gpid.cpp
index d754194f9b5..55c9730d122 100644
--- a/engines/director/lingo/xlibs/gpid.cpp
+++ b/engines/director/lingo/xlibs/gpid.cpp
@@ -84,7 +84,7 @@ ProductIdXObject::ProductIdXObject(ObjectType ObjectType) :Object<ProductIdXObje
 
 void GpidXObj::m_new(int nargs) {
 	g_lingo->printSTUBWithArglist("gpid::new", nargs);
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 } // End of namespace Director
diff --git a/engines/director/lingo/xlibs/jitdraw3.cpp b/engines/director/lingo/xlibs/jitdraw3.cpp
index eef00b77ee7..3368e5ee99e 100644
--- a/engines/director/lingo/xlibs/jitdraw3.cpp
+++ b/engines/director/lingo/xlibs/jitdraw3.cpp
@@ -110,7 +110,7 @@ JITDraw3XObject::JITDraw3XObject(ObjectType ObjectType) :Object<JITDraw3XObject>
 }
 
 void JITDraw3XObj::m_new(int nargs) {
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void JITDraw3XObj::m_dispose(int nargs) {
diff --git a/engines/director/lingo/xlibs/jwxini.cpp b/engines/director/lingo/xlibs/jwxini.cpp
index ab710db8cf5..c8fdb69f49e 100644
--- a/engines/director/lingo/xlibs/jwxini.cpp
+++ b/engines/director/lingo/xlibs/jwxini.cpp
@@ -87,7 +87,7 @@ JourneyWareXINIXObject::JourneyWareXINIXObject(ObjectType ObjectType) :Object<Jo
 
 void JourneyWareXINIXObj::m_new(int nargs) {
    g_lingo->printSTUBWithArglist("JWXIni::new", nargs);
-   g_lingo->push(g_lingo->_currentMe);
+   g_lingo->push(g_lingo->_state->me);
 }
 
 void JourneyWareXINIXObj::m_GetPrivateProfileInt(int nargs) {
diff --git a/engines/director/lingo/xlibs/labeldrvxobj.cpp b/engines/director/lingo/xlibs/labeldrvxobj.cpp
index c563c3f0d9a..f3f2cd8fe4b 100644
--- a/engines/director/lingo/xlibs/labeldrvxobj.cpp
+++ b/engines/director/lingo/xlibs/labeldrvxobj.cpp
@@ -75,16 +75,16 @@ LabelDrvXObject::LabelDrvXObject(ObjectType ObjectType) :Object<LabelDrvXObject>
 }
 
 void LabelDrvXObj::m_new(int nargs) {
-	LabelDrvXObject *me = static_cast<LabelDrvXObject *>(g_lingo->_currentMe.u.obj);
+	LabelDrvXObject *me = static_cast<LabelDrvXObject *>(g_lingo->_state->me.u.obj);
 
 	Common::Rect rect;
 	me->_range = "C";
 
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void LabelDrvXObj::m_setRange(int nargs) {
-	LabelDrvXObject *me = static_cast<LabelDrvXObject *>(g_lingo->_currentMe.u.obj);
+	LabelDrvXObject *me = static_cast<LabelDrvXObject *>(g_lingo->_state->me.u.obj);
 
 	Datum d2 = g_lingo->pop();
 	Datum d1 = g_lingo->pop();
@@ -96,7 +96,7 @@ void LabelDrvXObj::m_setRange(int nargs) {
 }
 
 void LabelDrvXObj::m_getDrive(int nargs) {
-	LabelDrvXObject *me = static_cast<LabelDrvXObject *>(g_lingo->_currentMe.u.obj);
+	LabelDrvXObject *me = static_cast<LabelDrvXObject *>(g_lingo->_state->me.u.obj);
 
 	Datum d1 = g_lingo->pop();
 
diff --git a/engines/director/lingo/xlibs/memoryxobj.cpp b/engines/director/lingo/xlibs/memoryxobj.cpp
index e48fedb87b7..2428366f5a3 100644
--- a/engines/director/lingo/xlibs/memoryxobj.cpp
+++ b/engines/director/lingo/xlibs/memoryxobj.cpp
@@ -91,7 +91,7 @@ MemoryXObject::MemoryXObject(ObjectType ObjectType) :Object<MemoryXObject>("Memo
 }
 
 void MemoryXObj::m_new(int nargs) {
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void MemoryXObj::m_clear(int nargs) {
diff --git a/engines/director/lingo/xlibs/miscx.cpp b/engines/director/lingo/xlibs/miscx.cpp
index 65aae31e95d..15b6a248c9f 100644
--- a/engines/director/lingo/xlibs/miscx.cpp
+++ b/engines/director/lingo/xlibs/miscx.cpp
@@ -101,7 +101,7 @@ MiscXObject::MiscXObject(ObjectType ObjectType) :Object<MiscXObject>("MiscX") {
 }
 
 void MiscX::m_new(int nargs) {
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void MiscX::m_bootName(int nargs) {
diff --git a/engines/director/lingo/xlibs/moovxobj.cpp b/engines/director/lingo/xlibs/moovxobj.cpp
index 17ee5bfccd7..d134289a6f9 100644
--- a/engines/director/lingo/xlibs/moovxobj.cpp
+++ b/engines/director/lingo/xlibs/moovxobj.cpp
@@ -106,12 +106,12 @@ MoovXObject::~MoovXObject() {
 }
 
 void MoovXObj::m_new(int nargs) {
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void MoovXObj::m_dispose(int nargs) {
 	debug(5, "MoovXObj::m_dispose");
-	MoovXObject *me = static_cast<MoovXObject *>(g_lingo->_currentMe.u.obj);
+	MoovXObject *me = static_cast<MoovXObject *>(g_lingo->_state->me.u.obj);
 	if (me->_video) {
 		delete me->_video;
 		me->_video = nullptr;
@@ -130,14 +130,14 @@ void MoovXObj::m_movieInit(int nargs) {
 
 void MoovXObj::m_movieKill(int nargs) {
 	debug(5, "MoovXObj::m_movieKill");
-	MoovXObject *me = static_cast<MoovXObject *>(g_lingo->_currentMe.u.obj);
+	MoovXObject *me = static_cast<MoovXObject *>(g_lingo->_state->me.u.obj);
 
 	if (me->_video)
 		me->_video->stop();
 }
 
 void MoovXObj::m_fondler(int nargs) {
-	MoovXObject *me = static_cast<MoovXObject *>(g_lingo->_currentMe.u.obj);
+	MoovXObject *me = static_cast<MoovXObject *>(g_lingo->_state->me.u.obj);
 
 	debug(10, "MoovXObj::m_fondler");
 	Graphics::Surface const *frame;
@@ -152,7 +152,7 @@ void MoovXObj::m_fondler(int nargs) {
 }
 
 void MoovXObj::m_playMovie(int nargs) {
-	MoovXObject *me = static_cast<MoovXObject *>(g_lingo->_currentMe.u.obj);
+	MoovXObject *me = static_cast<MoovXObject *>(g_lingo->_state->me.u.obj);
 
 	me->_y = g_lingo->pop().asInt();
 	me->_x = g_lingo->pop().asInt();
@@ -189,7 +189,7 @@ void MoovXObj::m_stopMovie(int nargs) {
 }
 
 void MoovXObj::m_movieDone(int nargs) {
-	MoovXObject *me = static_cast<MoovXObject *>(g_lingo->_currentMe.u.obj);
+	MoovXObject *me = static_cast<MoovXObject *>(g_lingo->_state->me.u.obj);
 	debug(10, "MoovXObj::m_movieDone");
 	bool result = (me->_video && !me->_video->endOfVideo());
 	g_lingo->push(result);
diff --git a/engines/director/lingo/xlibs/movemousexobj.cpp b/engines/director/lingo/xlibs/movemousexobj.cpp
index c71d6fc14d5..f9cdf257a0d 100644
--- a/engines/director/lingo/xlibs/movemousexobj.cpp
+++ b/engines/director/lingo/xlibs/movemousexobj.cpp
@@ -72,7 +72,7 @@ void MoveMouseXObj::m_new(int nargs) {
 		warning("MoveMouse::m_new: expected 0 arguments");
 		g_lingo->dropStack(nargs);
 	}
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void MoveMouseXObj::m_setMouseLoc(int nargs) {
diff --git a/engines/director/lingo/xlibs/movutils.cpp b/engines/director/lingo/xlibs/movutils.cpp
index dd6c5cd1551..df5fa9ce173 100644
--- a/engines/director/lingo/xlibs/movutils.cpp
+++ b/engines/director/lingo/xlibs/movutils.cpp
@@ -131,7 +131,7 @@ MovieUtilsXObject::MovieUtilsXObject(ObjectType ObjectType) :Object<MovieUtilsXO
 
 void MovUtilsXObj::m_new(int nargs) {
 	g_lingo->printSTUBWithArglist("MovUtilsXObj::new", nargs);
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 } // End of namespace Director
diff --git a/engines/director/lingo/xlibs/orthoplayxobj.cpp b/engines/director/lingo/xlibs/orthoplayxobj.cpp
index f2da3eeb281..9a9738ad3cf 100644
--- a/engines/director/lingo/xlibs/orthoplayxobj.cpp
+++ b/engines/director/lingo/xlibs/orthoplayxobj.cpp
@@ -138,7 +138,7 @@ OrthoPlayXObject::OrthoPlayXObject(ObjectType ObjectType) :Object<OrthoPlayXObje
 }
 
 void OrthoPlayXObj::m_new(int nargs) {
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void OrthoPlayXObj::m_setSerialPort(int nargs) {
diff --git a/engines/director/lingo/xlibs/palxobj.cpp b/engines/director/lingo/xlibs/palxobj.cpp
index f089c271d11..bd6a0fd4b78 100644
--- a/engines/director/lingo/xlibs/palxobj.cpp
+++ b/engines/director/lingo/xlibs/palxobj.cpp
@@ -79,7 +79,7 @@ PalXObject::PalXObject(ObjectType ObjectType) :Object<PalXObject>("PalXObj") {
 }
 
 void PalXObj::m_new(int nargs) {
-	PalXObject *me = static_cast<PalXObject *>(g_lingo->_currentMe.u.obj);
+	PalXObject *me = static_cast<PalXObject *>(g_lingo->_state->me.u.obj);
 
 	Common::Rect rect;
 	rect.bottom = g_lingo->pop().asInt();
@@ -88,7 +88,7 @@ void PalXObj::m_new(int nargs) {
 	rect.left  = g_lingo->pop().asInt();
 	me->_stageWindowCoordinates = rect;
 
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void PalXObj::m_patchIt(int nargs) {
diff --git a/engines/director/lingo/xlibs/popupmenuxobj.cpp b/engines/director/lingo/xlibs/popupmenuxobj.cpp
index a86480f2b52..8ad1747404f 100644
--- a/engines/director/lingo/xlibs/popupmenuxobj.cpp
+++ b/engines/director/lingo/xlibs/popupmenuxobj.cpp
@@ -148,7 +148,7 @@ PopUpMenuXObject::PopUpMenuXObject(ObjectType ObjectType) :Object<PopUpMenuXObje
 void PopUpMenuXObj::m_new(int nargs) {
 	g_lingo->printSTUBWithArglist("PopUpMenuXObj::m_new", nargs);
 	g_lingo->dropStack(nargs);
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void PopUpMenuXObj::m_appendMenu(int nargs) {
diff --git a/engines/director/lingo/xlibs/serialportxobj.cpp b/engines/director/lingo/xlibs/serialportxobj.cpp
index 3d6c47ac848..1c055a53cf3 100644
--- a/engines/director/lingo/xlibs/serialportxobj.cpp
+++ b/engines/director/lingo/xlibs/serialportxobj.cpp
@@ -79,7 +79,7 @@ SerialPortXObject::SerialPortXObject(ObjectType ObjectType) :Object<SerialPortXO
 void SerialPortXObj::m_new(int nargs) {
 	g_lingo->printSTUBWithArglist("SerialPortXObj::m_new", nargs);
 	g_lingo->dropStack(nargs);
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void SerialPortXObj::m_getPortNum(int nargs) {
diff --git a/engines/director/lingo/xlibs/soundjam.cpp b/engines/director/lingo/xlibs/soundjam.cpp
index becbb1fcb92..96cd4b435d8 100644
--- a/engines/director/lingo/xlibs/soundjam.cpp
+++ b/engines/director/lingo/xlibs/soundjam.cpp
@@ -104,7 +104,7 @@ void SoundJam::m_new(int nargs) {
 		return;
 	}
 
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void SoundJam::m_defineFileSound(int nargs) {
@@ -114,7 +114,7 @@ void SoundJam::m_defineFileSound(int nargs) {
 }
 
 void SoundJam::m_defineCastSound(int nargs) {
-	SoundJamObject *me = static_cast<SoundJamObject *>(g_lingo->_currentMe.u.obj);
+	SoundJamObject *me = static_cast<SoundJamObject *>(g_lingo->_state->me.u.obj);
 
 	/* Datum numberOfBeats = */ g_lingo->pop();
 	CastMemberID castMemberNumber = g_lingo->pop().asMemberID();
@@ -129,7 +129,7 @@ void SoundJam::m_defineCastSound(int nargs) {
 }
 
 void SoundJam::m_undefineSound(int nargs) {
-	SoundJamObject *me = static_cast<SoundJamObject *>(g_lingo->_currentMe.u.obj);
+	SoundJamObject *me = static_cast<SoundJamObject *>(g_lingo->_state->me.u.obj);
 	int soundID = g_lingo->pop().asInt();
 
 	if (soundID < 0) {
@@ -160,7 +160,7 @@ void SoundJam::m_startSound(int nargs) {
 }
 
 void SoundJam::m_switchNew(int nargs) {
-	SoundJamObject *me = static_cast<SoundJamObject *>(g_lingo->_currentMe.u.obj);
+	SoundJamObject *me = static_cast<SoundJamObject *>(g_lingo->_state->me.u.obj);
 	int soundID = g_lingo->pop().asInt();
 
 	if (!me->_soundMap.contains(soundID)) {
diff --git a/engines/director/lingo/xlibs/spacemgr.cpp b/engines/director/lingo/xlibs/spacemgr.cpp
index 1c8750d1591..aba4c42d19a 100644
--- a/engines/director/lingo/xlibs/spacemgr.cpp
+++ b/engines/director/lingo/xlibs/spacemgr.cpp
@@ -156,7 +156,7 @@ void SpaceMgr::m_new(int nargs) {
 		warning("SpaceMgr::m_new: expected 0 arguments");
 		g_lingo->dropStack(nargs);
 	}
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void SpaceMgr::m_dispose(int nargs) {
@@ -202,7 +202,7 @@ void SpaceMgr::m_checkForDups(int nargs) {
 		return;
 	}
 
-	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_currentMe.u.obj);
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
 
 	me->_checkForDups = (arg.u.s->c_str()[0] == 't') || (arg.u.s->c_str()[0] == 'T');
 	g_lingo->push(Datum(0));
@@ -221,7 +221,7 @@ void SpaceMgr::m_parseText(int nargs) {
 		g_lingo->push(Datum(0));
 		return;
 	}
-	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_currentMe.u.obj);
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
 
 	Common::String result = *text.u.s;
 	Common::String curSpaceCollection;
@@ -310,7 +310,7 @@ void SpaceMgr::m_getCurData(int nargs) {
 		warning("SpaceMgr::m_getCurData: expected 0 arguments");
 		g_lingo->dropStack(nargs);
 	}
-	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_currentMe.u.obj);
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
 	Common::String result;
 	if (me->_spaceCollections.contains(me->_curSpaceCollection)) {
 		result += "SPACECOLLECTION " + me->_curSpaceCollection;
@@ -351,7 +351,7 @@ void SpaceMgr::m_setCurData(int nargs) {
 		g_lingo->push(Datum(0));
 		return;
 	}
-	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_currentMe.u.obj);
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
 	g_lingo->printSTUBWithArglist("SpaceMgr::m_setCurData", nargs);
 	Datum view = g_lingo->pop();
 	Datum node = g_lingo->pop();
@@ -391,7 +391,7 @@ void SpaceMgr::m_setCurSpaceCollection(int nargs) {
 		g_lingo->push(Datum(0));
 		return;
 	}
-	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_currentMe.u.obj);
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
 	Datum spaceCollection = g_lingo->pop();
 	if (spaceCollection.type != STRING) {
 		warning("SpaceMgr::m_setCurSpaceCollection: expected spaceCollection to be a string, not %s", spaceCollection.type2str());
@@ -410,7 +410,7 @@ void SpaceMgr::m_getCurSpaceCollection(int nargs) {
 		warning("SpaceMgr::m_getCurSpaceCollection: expected 0 arguments");
 		g_lingo->dropStack(nargs);
 	}
-	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_currentMe.u.obj);
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
 	Common::String result;
 	if (!me->_curSpaceCollection.empty()) {
 		if (me->_spaceCollections.contains(me->_curSpaceCollection)) {
@@ -447,7 +447,7 @@ void SpaceMgr::m_setCurSpace(int nargs) {
 		g_lingo->push(Datum(0));
 		return;
 	}
-	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_currentMe.u.obj);
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
 	Datum space = g_lingo->pop();
 	if (space.type != STRING) {
 		warning("SpaceMgr::m_setCurSpace: expected space to be a string, not %s", space.type2str());
@@ -466,7 +466,7 @@ void SpaceMgr::m_getCurSpace(int nargs) {
 		warning("SpaceMgr::m_getCurSpace: expected 0 arguments");
 		g_lingo->dropStack(nargs);
 	}
-	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_currentMe.u.obj);
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
 	Common::String result;
 	if (!me->_curSpaceCollection.empty()) {
 		if (me->_spaceCollections.contains(me->_curSpaceCollection)) {
@@ -506,7 +506,7 @@ void SpaceMgr::m_setCurNode(int nargs) {
 		g_lingo->push(Datum(0));
 		return;
 	}
-	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_currentMe.u.obj);
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
 	Datum node = g_lingo->pop();
 	if (node.type != STRING) {
 		warning("SpaceMgr::m_setCurNode: expected node to be a string, not %s", node.type2str());
@@ -525,7 +525,7 @@ void SpaceMgr::m_getCurNode(int nargs) {
 		warning("SpaceMgr::m_getCurNode: expected 0 arguments");
 		g_lingo->dropStack(nargs);
 	}
-	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_currentMe.u.obj);
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
 	Common::String result;
 	if (!me->_curView.empty()) {
 		if (me->_spaceCollections.contains(me->_curSpaceCollection)) {
@@ -567,7 +567,7 @@ void SpaceMgr::m_setCurView(int nargs) {
 		g_lingo->push(Datum(0));
 		return;
 	}
-	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_currentMe.u.obj);
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
 	Datum view = g_lingo->pop();
 	if (view.type != STRING) {
 		warning("SpaceMgr::m_setCurView: expected view to be a string, not %s", view.type2str());
@@ -586,7 +586,7 @@ void SpaceMgr::m_getCurView(int nargs) {
 		warning("SpaceMgr::m_getCurView: expected 0 arguments");
 		g_lingo->dropStack(nargs);
 	}
-	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_currentMe.u.obj);
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
 	Common::String result;
 	if (!me->_curView.empty()) {
 		if (me->_spaceCollections.contains(me->_curSpaceCollection)) {
diff --git a/engines/director/lingo/xlibs/videodiscxobj.cpp b/engines/director/lingo/xlibs/videodiscxobj.cpp
index ddf8629e2cf..73d996b816e 100644
--- a/engines/director/lingo/xlibs/videodiscxobj.cpp
+++ b/engines/director/lingo/xlibs/videodiscxobj.cpp
@@ -166,7 +166,7 @@ VideodiscXObject::VideodiscXObject(ObjectType ObjectType) :Object<VideodiscXObje
 void VideodiscXObj::m_new(int nargs) {
 	g_lingo->printSTUBWithArglist("VideodiscXObj::m_new", nargs);
 	g_lingo->dropStack(nargs);
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void VideodiscXObj::m_name(int nargs) {
diff --git a/engines/director/lingo/xlibs/widgetxobj.cpp b/engines/director/lingo/xlibs/widgetxobj.cpp
index 251ff40e602..980f89ef156 100644
--- a/engines/director/lingo/xlibs/widgetxobj.cpp
+++ b/engines/director/lingo/xlibs/widgetxobj.cpp
@@ -73,7 +73,7 @@ WidgetXObject::WidgetXObject(ObjectType ObjectType) :Object<WidgetXObject>("Widg
 }
 
 void WidgetXObj::m_new(int nargs) {
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void WidgetXObj::m_dispose(int nargs) {
diff --git a/engines/director/lingo/xlibs/winxobj.cpp b/engines/director/lingo/xlibs/winxobj.cpp
index 8c27e88e03e..4f1aa52845e 100644
--- a/engines/director/lingo/xlibs/winxobj.cpp
+++ b/engines/director/lingo/xlibs/winxobj.cpp
@@ -260,7 +260,7 @@ RearWindowXObject::RearWindowXObject(ObjectType ObjectType) :Object<RearWindowXO
 
 void RearWindowXObj::m_new(int nargs) {
 	Datum d1 = g_lingo->pop();
-	g_lingo->push(g_lingo->_currentMe);
+	g_lingo->push(g_lingo->_state->me);
 }
 
 void RearWindowXObj::m_getMemoryNeeded(int nargs) {
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index 0dd24ed2073..90f14986f13 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -471,7 +471,7 @@ void Score::update() {
 	debugC(1, kDebugLoading, "******************************  Current frame: %d, time: %d", _currentFrame, g_system->getMillis(false));
 	g_debugger->frameHook();
 
-	uint initialCallStackSize = _window->_callstack.size();
+	uint initialCallStackSize = g_lingo->_state->callstack.size();
 
 	_lingo->executeImmediateScripts(_frames[_currentFrame]);
 
@@ -508,7 +508,7 @@ void Score::update() {
 
 	// If we have more call stack frames than we started with, then we have a newly
 	// added frozen context. We'll deal with that later.
-	if (_window->_callstack.size() == initialCallStackSize) {
+	if (g_lingo->_state->callstack.size() == initialCallStackSize) {
 		// We may have a frozen Lingo context from func_goto.
 		// Now that we've entered a new frame, let's unfreeze that context.
 		if (g_lingo->_freezeContext) {
diff --git a/engines/director/window.cpp b/engines/director/window.cpp
index 060a712b64b..4d782edc5df 100644
--- a/engines/director/window.cpp
+++ b/engines/director/window.cpp
@@ -48,6 +48,7 @@ Window::Window(int id, bool scrollable, bool resizable, bool editable, Graphics:
 	_stageColor = _wm->_colorBlack;
 	_puppetTransition = nullptr;
 	_soundManager = new DirectorSound(this);
+	_lingoState = new LingoState;
 
 	_currentMovie = nullptr;
 	_mainArchive = nullptr;
@@ -60,15 +61,10 @@ Window::Window(int id, bool scrollable, bool resizable, bool editable, Graphics:
 	_windowType = -1;
 	_titleVisible = true;
 	updateBorderType();
-
-	_retPC = 0;
-	_retScript = nullptr;
-	_retContext = nullptr;
-	_retFreezeContext = false;
-	_retLocalVars = nullptr;
 }
 
 Window::~Window() {
+	delete _lingoState;
 	delete _soundManager;
 	delete _currentMovie;
 	if (_puppetTransition)
diff --git a/engines/director/window.h b/engines/director/window.h
index 12f21f3c410..a15af75c5a2 100644
--- a/engines/director/window.h
+++ b/engines/director/window.h
@@ -40,6 +40,7 @@ namespace Director {
 class Channel;
 class MacArchive;
 struct MacShape;
+struct LingoState;
 
 struct TransParams {
 	TransitionType type;
@@ -139,6 +140,7 @@ public:
 	int getWindowType() const { return _windowType; }
 	void setTitleVisible(bool titleVisible) { _titleVisible = titleVisible; updateBorderType(); };
 	bool isTitleVisible() { return _titleVisible; };
+	LingoState *getLingoState() { return _lingoState; };
 	Datum getStageRect();
 
 	void updateBorderType();
@@ -191,20 +193,12 @@ public:
 	Common::List<MovieReference> _movieStack;
 	bool _newMovieStarted;
 
-	// saved Lingo state
-	Common::Array<CFrame *> _callstack;
-	uint _retPC;
-	ScriptData *_retScript;
-	ScriptContext *_retContext;
-	bool _retFreezeContext;
-	DatumHash *_retLocalVars;
-	Datum _retMe;
-
 private:
 	uint32 _stageColor;
 
 	DirectorEngine *_vm;
 	DirectorSound *_soundManager;
+	LingoState *_lingoState;
 	bool _isStage;
 	Archive *_mainArchive;
 	Movie *_currentMovie;


Commit: 3ea7f10467ea0291d2dbadf99de685ee9057bf4a
    https://github.com/scummvm/scummvm/commit/3ea7f10467ea0291d2dbadf99de685ee9057bf4a
Author: Scott Percival (code at moral.net.au)
Date: 2022-12-09T01:10:57+01:00

Commit Message:
DIRECTOR: Freeze LingoState instead of using callstack

Previously, the Lingo state would be frozen by persisting the frames on
the bottom of the callstack with a special flag. This required extra
logic to determine whether or not the callstack was intended to be empty
after execution finished, with some complex edge cases.

The new approach is to swap out the LingoState object on the window,
meaning that an empty callstack always signifies execution has completed.
It remains to be seen if we'll need to track more than one frozen
context; for now it seems to be okay.

Fixes driving to locations in DEVO Presents: Adventures of the Smart Patrol.

Changed paths:
    engines/director/lingo/lingo-code.cpp
    engines/director/lingo/lingo-funcs.cpp
    engines/director/lingo/lingo.cpp
    engines/director/lingo/lingo.h
    engines/director/score.cpp
    engines/director/window.cpp
    engines/director/window.h


diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 06a6e36d734..a69bdb382f4 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -233,7 +233,6 @@ void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRet
 	fp->retPC = _state->pc;
 	fp->retScript = _state->script;
 	fp->retContext = _state->context;
-	fp->retFreezeContext = _freezeContext;
 	fp->retLocalVars = _state->localVars;
 	fp->retMe = _state->me;
 	fp->sp = funcSym;
@@ -249,7 +248,6 @@ void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRet
 		_state->context = funcSym.ctx;
 		*_state->context->_refCount += 1;
 	}
-	_freezeContext = false;
 
 	DatumHash *localvars = _state->localVars;
 	if (!funcSym.anonymous) {
@@ -345,7 +343,6 @@ void Lingo::popContext(bool aborting) {
 
 	_state->script = fp->retScript;
 	_state->context = fp->retContext;
-	_freezeContext = fp->retFreezeContext;
 	_state->pc = fp->retPC;
 	_state->me = fp->retMe;
 
@@ -364,17 +361,15 @@ void Lingo::popContext(bool aborting) {
 	g_debugger->popContextHook();
 }
 
-bool Lingo::hasFrozenContext() {
-	if (_freezeContext)
-		return true;
-
-	Common::Array<CFrame *> &callstack = _state->callstack;
-	for (uint i = 0; i < callstack.size(); i++) {
-		if (callstack[i]->retFreezeContext)
-			return true;
-	}
+bool Lingo::hasFrozenState() {
+	Window *window = _vm->getCurrentWindow();
+	return window->hasFrozenLingoState();
+}
 
-	return false;
+void Lingo::freezeState() {
+	Window *window = _vm->getCurrentWindow();
+	window->freezeLingoState();
+	switchStateFromWindow();
 }
 
 void LC::c_constpush() {
diff --git a/engines/director/lingo/lingo-funcs.cpp b/engines/director/lingo/lingo-funcs.cpp
index b0b5c2b0a39..c80eca31ed3 100644
--- a/engines/director/lingo/lingo-funcs.cpp
+++ b/engines/director/lingo/lingo-funcs.cpp
@@ -197,8 +197,8 @@ void Lingo::func_goto(Datum &frame, Datum &movie) {
 
 	// If there isn't already frozen Lingo (e.g. from a previous func_goto we haven't yet unfrozen),
 	// freeze this script context. We'll return to it after entering the next frame.
-	if (!g_lingo->hasFrozenContext()) {
-		g_lingo->_freezeContext = true;
+	if (!g_lingo->hasFrozenState()) {
+		g_lingo->_freezeState = true;
 	}
 
 	if (movie.type != VOID) {
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index 1e95ae6ba70..b45ccc171f7 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -155,7 +155,7 @@ Lingo::Lingo(DirectorEngine *vm) : _vm(vm) {
 	_state = nullptr;
 	_currentChannelId = -1;
 	_globalCounter = 0;
-	_freezeContext = false;
+	_freezeState = false;
 	_abort = false;
 	_expectError = false;
 	_caughtError = false;
@@ -541,7 +541,7 @@ Common::String Lingo::formatFunctionBody(Symbol &sym) {
 void Lingo::execute() {
 	uint localCounter = 0;
 
-	while (!_abort && !_freezeContext && _state->script && (*_state->script)[_state->pc] != STOP) {
+	while (!_abort && !_freezeState && _state->script && (*_state->script)[_state->pc] != STOP) {
 		if (_globalCounter > 1000 && debugChannelSet(-1, kDebugFewFramesOnly)) {
 			warning("Lingo::execute(): Stopping due to debug few frames only");
 			_vm->getCurrentMovie()->getScore()->_playState = kPlayStopped;
@@ -595,17 +595,18 @@ void Lingo::execute() {
 		}
 	}
 
-	if (_abort || _vm->getCurrentMovie()->getScore()->_playState == kPlayStopped) {
+	if (_freezeState) {
+		debugC(5, kDebugLingoExec, "Lingo::execute(): Context is frozen, pausing execution");
+		freezeState();
+	} else if (_abort || _vm->getCurrentMovie()->getScore()->_playState == kPlayStopped) {
 		// Clean up call stack
 		while (_state->callstack.size()) {
 			popContext(true);
 		}
 	}
 	_abort = false;
+	_freezeState = false;
 
-	if (_freezeContext) {
-		debugC(1, kDebugLingoExec, "Lingo::execute(): Context is frozen, pausing execution");
-	}
 	g_debugger->stepHook();
 }
 
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index 8c332559efa..f7ad90fd721 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -241,7 +241,6 @@ struct CFrame {	/* proc/func call stack frame */
 	int				retPC;				/* where to resume after return */
 	ScriptData		*retScript;			/* which script to resume after return */
 	ScriptContext	*retContext;		/* which script context to use after return */
-	bool			retFreezeContext;	/* whether the context should be frozen after return */
 	DatumHash		*retLocalVars;
 	Datum			retMe;				/* which me obj to use after return */
 	uint			stackSizeBefore;
@@ -358,9 +357,10 @@ public:
 public:
 	void execute();
 	void switchStateFromWindow();
+	void freezeState();
+	bool hasFrozenState();
 	void pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRetVal);
 	void popContext(bool aborting = false);
-	bool hasFrozenContext();
 	void cleanLocalVars();
 	void varAssign(const Datum &var, const Datum &value);
 	Datum varFetch(const Datum &var, bool silent = false);
@@ -448,7 +448,7 @@ public:
 
 	int _currentChannelId;
 
-	bool _freezeContext;
+	bool _freezeState;
 	bool _abort;
 	bool _expectError;
 	bool _caughtError;
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index 90f14986f13..bb2edac8a8e 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -471,7 +471,11 @@ void Score::update() {
 	debugC(1, kDebugLoading, "******************************  Current frame: %d, time: %d", _currentFrame, g_system->getMillis(false));
 	g_debugger->frameHook();
 
-	uint initialCallStackSize = g_lingo->_state->callstack.size();
+	if (_window->hasFrozenLingoState()) {
+		_window->thawLingoState();
+		g_lingo->switchStateFromWindow();
+		g_lingo->execute();
+	}
 
 	_lingo->executeImmediateScripts(_frames[_currentFrame]);
 
@@ -506,18 +510,6 @@ void Score::update() {
 		_movie->_lastTimeOut = _vm->getMacTicks();
 	}
 
-	// If we have more call stack frames than we started with, then we have a newly
-	// added frozen context. We'll deal with that later.
-	if (g_lingo->_state->callstack.size() == initialCallStackSize) {
-		// We may have a frozen Lingo context from func_goto.
-		// Now that we've entered a new frame, let's unfreeze that context.
-		if (g_lingo->_freezeContext) {
-			debugC(1, kDebugLingoExec, "Score::update(): Unfreezing Lingo context");
-			g_lingo->_freezeContext = false;
-			g_lingo->execute();
-		}
-	}
-
 }
 
 void Score::renderFrame(uint16 frameId, RenderMode mode) {
diff --git a/engines/director/window.cpp b/engines/director/window.cpp
index 4d782edc5df..7d20074d92c 100644
--- a/engines/director/window.cpp
+++ b/engines/director/window.cpp
@@ -49,6 +49,7 @@ Window::Window(int id, bool scrollable, bool resizable, bool editable, Graphics:
 	_puppetTransition = nullptr;
 	_soundManager = new DirectorSound(this);
 	_lingoState = new LingoState;
+	_frozenLingoState = nullptr;
 
 	_currentMovie = nullptr;
 	_mainArchive = nullptr;
@@ -428,4 +429,29 @@ Common::String Window::getSharedCastPath() {
 	return Common::String();
 }
 
+void Window::freezeLingoState() {
+	if (_frozenLingoState) {
+		warning("State already frozen! Ditching callstack with %d frames", _frozenLingoState->callstack.size());
+		delete _frozenLingoState;
+	}
+	_frozenLingoState = _lingoState;
+	_lingoState = new LingoState;
+	debugC(kDebugLingoExec, 5, "Freezing Lingo state");
+}
+
+void Window::thawLingoState() {
+	if (!_frozenLingoState) {
+		warning("Tried to thaw when there's no frozen state, ignoring");
+		return;
+	}
+	if (!_lingoState->callstack.empty()) {
+		warning("Can't thaw a Lingo state in mid-execution, ignoring");
+		return;
+	}
+	delete _lingoState;
+	_lingoState = _frozenLingoState;
+	_frozenLingoState = nullptr;
+	debugC(kDebugLingoExec, 5, "Thawing Lingo state");
+}
+
 } // End of namespace Director
diff --git a/engines/director/window.h b/engines/director/window.h
index a15af75c5a2..c03ccecb1a7 100644
--- a/engines/director/window.h
+++ b/engines/director/window.h
@@ -125,6 +125,7 @@ public:
 	void transMultiPass(TransParams &t, Common::Rect &clipRect, Graphics::ManagedSurface *tmpSurface);
 	void transZoom(TransParams &t, Common::Rect &clipRect, Graphics::ManagedSurface *tmpSurface);
 
+	// window.cpp
 	Common::Point getMousePos();
 
 	DirectorEngine *getVM() const { return _vm; }
@@ -140,7 +141,6 @@ public:
 	int getWindowType() const { return _windowType; }
 	void setTitleVisible(bool titleVisible) { _titleVisible = titleVisible; updateBorderType(); };
 	bool isTitleVisible() { return _titleVisible; };
-	LingoState *getLingoState() { return _lingoState; };
 	Datum getStageRect();
 
 	void updateBorderType();
@@ -151,6 +151,11 @@ public:
 
 	Common::String getSharedCastPath();
 
+	LingoState *getLingoState() { return _lingoState; };
+	bool hasFrozenLingoState() { return _frozenLingoState != nullptr; };
+	void freezeLingoState();
+	void thawLingoState();
+
 	// events.cpp
 	bool processEvent(Common::Event &event) override;
 
@@ -199,6 +204,7 @@ private:
 	DirectorEngine *_vm;
 	DirectorSound *_soundManager;
 	LingoState *_lingoState;
+	LingoState *_frozenLingoState;
 	bool _isStage;
 	Archive *_mainArchive;
 	Movie *_currentMovie;


Commit: 30e9d330dfc2fc90828597ba8856e643ce50231d
    https://github.com/scummvm/scummvm/commit/30e9d330dfc2fc90828597ba8856e643ce50231d
Author: Scott Percival (code at moral.net.au)
Date: 2022-12-09T01:10:57+01:00

Commit Message:
DIRECTOR: Implement the paramCount

At first glance it would seem we could use funcSym.nargs for this,
except it is fixed to the number of args in the function definition.
We also can't derive it from the argument list that is sent through,
as it is truncated/padded with VOIDs. To keep things simple, track
paramCount as part of CFrame.

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


diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index a69bdb382f4..26fa88d0ace 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -224,7 +224,7 @@ void Lingo::switchStateFromWindow() {
 	_state = window->getLingoState();
 }
 
-void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRetVal) {
+void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRetVal, int paramCount) {
 	Common::Array<CFrame *> &callstack = _state->callstack;
 
 	debugC(5, kDebugLingoExec, "Pushing frame %d", callstack.size() + 1);
@@ -238,6 +238,7 @@ void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRet
 	fp->sp = funcSym;
 	fp->allowRetVal = allowRetVal;
 	fp->defaultRetVal = defaultRetVal;
+	fp->paramCount = paramCount;
 
 	_state->script = funcSym.u.defn;
 
@@ -1553,6 +1554,7 @@ void LC::call(const Common::String &name, int nargs, bool allowRetVal) {
 }
 
 void LC::call(const Symbol &funcSym, int nargs, bool allowRetVal) {
+	int paramCount = nargs;
 	Datum target = funcSym.target;
 
 	if (funcSym.type == VOIDSYM) {
@@ -1655,7 +1657,7 @@ void LC::call(const Symbol &funcSym, int nargs, bool allowRetVal) {
 		defaultRetVal = funcSym.target; // return me
 	}
 
-	g_lingo->pushContext(funcSym, allowRetVal, defaultRetVal);
+	g_lingo->pushContext(funcSym, allowRetVal, defaultRetVal, paramCount);
 }
 
 void LC::c_procret() {
diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index cf05e01babf..bf8e3505e0a 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -719,6 +719,9 @@ Datum Lingo::getTheEntity(int entity, Datum &id, int field) {
 	case kTheOptionDown:
 		d = (movie->_keyFlags & Common::KBD_ALT) ? 1 : 0;
 		break;
+	case kTheParamCount:
+		d = g_lingo->_state->callstack[g_lingo->_state->callstack.size() - 1]->paramCount;
+		break;
 	case kThePauseState:
 		d = (int) g_director->_playbackPaused;
 		break;
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index f7ad90fd721..a2164251177 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -246,6 +246,7 @@ struct CFrame {	/* proc/func call stack frame */
 	uint			stackSizeBefore;
 	bool			allowRetVal;		/* whether to allow a return value */
 	Datum			defaultRetVal;		/* default return value */
+	int				paramCount;			/* original number of arguments submitted */
 };
 
 struct LingoEvent {
@@ -359,7 +360,7 @@ public:
 	void switchStateFromWindow();
 	void freezeState();
 	bool hasFrozenState();
-	void pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRetVal);
+	void pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRetVal, int paramCount);
 	void popContext(bool aborting = false);
 	void cleanLocalVars();
 	void varAssign(const Datum &var, const Datum &value);


Commit: 225460f87585adfdf1562f0dbad08c691a46eb01
    https://github.com/scummvm/scummvm/commit/225460f87585adfdf1562f0dbad08c691a46eb01
Author: Scott Percival (code at moral.net.au)
Date: 2022-12-09T01:10:57+01:00

Commit Message:
DIRECTOR: LINGO: Refactor SpaceMgr XObj, add LLink support

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


diff --git a/engines/director/lingo/xlibs/spacemgr.cpp b/engines/director/lingo/xlibs/spacemgr.cpp
index aba4c42d19a..8f77883cc67 100644
--- a/engines/director/lingo/xlibs/spacemgr.cpp
+++ b/engines/director/lingo/xlibs/spacemgr.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/system.h"
+#include "common/tokenizer.h"
 
 #include "director/director.h"
 #include "director/lingo/lingo.h"
@@ -228,78 +229,68 @@ void SpaceMgr::m_parseText(int nargs) {
 	Common::String curSpace;
 	Common::String curNode;
 	Common::String curView;
-	int lastIndex = 0;
-	while (lastIndex < (int)result.size()) {
-		size_t index = result.findFirstOf('\r', lastIndex);
-		if (index == Common::String::npos)
-			index = result.size();
-		int pointer = lastIndex;
-
-		while (pointer < (int)index && result[pointer] == ' ')
-			pointer++;
-		if ((int)result.find("SPACECOLLECTION ", pointer) == pointer) {
-			pointer += 16;
-			while (pointer < (int)index && result[pointer] == ' ')
-				pointer++;
-			curSpaceCollection = result.substr(pointer, index - pointer);
+	Common::StringTokenizer instructions = Common::StringTokenizer(result, "\r");
+
+	while (!instructions.empty()) {
+		Common::String instructionBody = instructions.nextToken();
+		Common::StringTokenizer instruction = Common::StringTokenizer(instructionBody, " ");
+		Common::String type = instruction.nextToken();
+		if (type == "SPACECOLLECTION") {
+			curSpaceCollection = instruction.nextToken();
 			curSpace = "";
 			curNode = "";
 			curView = "";
 			if (!(me->_spaceCollections.contains(curSpaceCollection) && me->_checkForDups)) {
 				me->_spaceCollections[curSpaceCollection] = SpaceCollection();
 			}
-		} else if ((int)result.find("SPACE ", pointer) == pointer) {
-			pointer += 6;
-			while (pointer < (int)index && result[pointer] == ' ')
-				pointer++;
-			curSpace = result.substr(pointer, index - pointer);
+		} else if (type == "SPACE") {
+			curSpace = instruction.nextToken();
 			curNode = "";
 			curView = "";
 			SpaceCollection &sc = me->_spaceCollections.getVal(curSpaceCollection);
 			if (!(sc.spaces.contains(curSpaceCollection) && me->_checkForDups)) {
 				sc.spaces[curSpace] = Space();
 			}
-		} else if ((int)result.find("NODE ", pointer) == pointer) {
-			pointer += 5;
-			while (pointer < (int)index && result[pointer] == ' ')
-				pointer++;
-			curNode = result.substr(pointer, index - pointer);
+		} else if (type == "NODE") {
+			curNode = instruction.nextToken();
 			curView = "";
 			SpaceCollection &sc = me->_spaceCollections.getVal(curSpaceCollection);
 			Space &s = sc.spaces.getVal(curSpace);
 			if (!(s.nodes.contains(curNode) && me->_checkForDups)) {
 				s.nodes[curNode] = Node();
 			}
-		} else if ((int)result.find("VIEW ", pointer) == pointer) {
-			pointer += 5;
-			while (pointer < (int)index && result[pointer] == ' ')
-				pointer++;
-			int idEnd = pointer;
-			while (idEnd < (int)index && result[idEnd] != ' ')
-				idEnd++;
-			curView = result.substr(pointer, idEnd - pointer);
-			pointer = idEnd;
-			while (pointer < (int)index && result[pointer] != ' ')
-				pointer++;
+		} else if (type == "VIEW") {
+			curView = instruction.nextToken();
 			SpaceCollection &sc = me->_spaceCollections.getVal(curSpaceCollection);
 			Space &s = sc.spaces.getVal(curSpace);
 			Node &n = s.nodes.getVal(curNode);
 			if (!(n.views.contains(curView) && me->_checkForDups)) {
-				n.views[curView] = result.substr(pointer, index - pointer);
+				n.views[curView] = View();
+				n.views[curView].payload = instruction.nextToken();
+			}
+		} else if (type == "LLINK") {
+			Common::String target = instruction.nextToken();
+			Common::String payload = instruction.nextToken();
+			SpaceCollection &sc = me->_spaceCollections.getVal(curSpaceCollection);
+			Space &s = sc.spaces.getVal(curSpace);
+			Node &n = s.nodes.getVal(curNode);
+			View &v = n.views.getVal(curView);
+			if (!(v.llinks.contains(target) && me->_checkForDups)) {
+				v.llinks[target] = LLink();
+				v.llinks[target].payload = payload;
 			}
 		} else {
-			warning("SpaceMgr::m_parseText: Unhandled instruction %s", result.substr(lastIndex, index).c_str());
+			warning("SpaceMgr::m_parseText: Unhandled instruction %s", instructionBody.c_str());
 		}
-
-		lastIndex = (int)index + 1;
 	}
+
 	if (debugLevelSet(5)) {
 		Common::String format = result;
 		for (int i = 0; i < (int)format.size(); i++) {
 			if (format[i] == '\r')
 				format.replace(i, 1, "\n");
 		}
-		debugC(5, kDebugXObj, "SpaceMgr::m_parseText(): %s", format.c_str());
+		debugC(5, kDebugXObj, "SpaceMgr::m_parseText: %s", format.c_str());
 	}
 
 	g_lingo->push(Datum(0));
@@ -337,14 +328,13 @@ void SpaceMgr::m_getCurData(int nargs) {
 			if (format[i] == '\r')
 				format.replace(i, 1, "\n");
 		}
-		debugC(5, kDebugXObj, "SpaceMgr::m_getCurData(): %s", format.c_str());
+		debugC(5, kDebugXObj, "SpaceMgr::m_getCurData: %s", format.c_str());
 	}
 
 	g_lingo->push(Datum(result));
 }
 
 void SpaceMgr::m_setCurData(int nargs) {
-	g_lingo->printSTUBWithArglist("SpaceMgr::m_setCurData", nargs);
 	if (nargs != 4) {
 		warning("SpaceMgr::m_setCurData: expected 4 arguments");
 		g_lingo->dropStack(nargs);
@@ -352,7 +342,6 @@ void SpaceMgr::m_setCurData(int nargs) {
 		return;
 	}
 	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
-	g_lingo->printSTUBWithArglist("SpaceMgr::m_setCurData", nargs);
 	Datum view = g_lingo->pop();
 	Datum node = g_lingo->pop();
 	Datum space = g_lingo->pop();
@@ -365,10 +354,15 @@ void SpaceMgr::m_setCurData(int nargs) {
 		g_lingo->push(Datum(0));
 		return;
 	}
-	me->_curSpaceCollection = *spaceCollection.u.s;
-	me->_curSpace = *space.u.s;
-	me->_curNode = *node.u.s;
-	me->_curView = *view.u.s;
+	if (!spaceCollection.u.s->empty())
+		me->_curSpaceCollection = *spaceCollection.u.s;
+	if (!space.u.s->empty())
+		me->_curSpace = *space.u.s;
+	if (!node.u.s->empty())
+		me->_curNode = *node.u.s;
+	if (!view.u.s->empty())
+		me->_curView = *view.u.s;
+	debugC(5, kDebugXObj, "SpaceMgr::m_setCurData: %s %s %s %s", me->_curSpaceCollection.c_str(), me->_curSpace.c_str(), me->_curNode.c_str(), me->_curView.c_str());
 	g_lingo->push(Datum(0));
 }
 
@@ -379,8 +373,19 @@ void SpaceMgr::m_addSpaceCollection(int nargs) {
 }
 
 void SpaceMgr::m_removeSpaceCollection(int nargs) {
-	g_lingo->printSTUBWithArglist("SpaceMgr::m_removeSpaceCollection", nargs);
-	g_lingo->dropStack(nargs);
+	if (nargs != 1) {
+		warning("SpaceMgr::m_removeSpaceCollection: expected 1 argument");
+		g_lingo->dropStack(nargs);
+		g_lingo->push(Datum(0));
+		return;
+	}
+	Common::String sc = g_lingo->pop().asString();
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
+	if (me->_spaceCollections.contains(sc)) {
+		me->_spaceCollections.erase(sc);
+	}
+	debugC(5, kDebugXObj, "SpaceMgr::m_removeSpaceCollection: %s", sc.c_str());
+
 	g_lingo->push(Datum(0));
 }
 
@@ -423,9 +428,21 @@ void SpaceMgr::m_getCurSpaceCollection(int nargs) {
 }
 
 void SpaceMgr::m_getSpaceCollection(int nargs) {
-	g_lingo->printSTUBWithArglist("SpaceMgr::m_getSpaceCollection", nargs);
-	g_lingo->dropStack(nargs);
-	g_lingo->push(Datum(""));
+	if (nargs != 1) {
+		warning("SpaceMgr::m_getSpaceCollection: expected 1 argument");
+		g_lingo->dropStack(nargs);
+		g_lingo->push(Datum(""));
+		return;
+	}
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
+	Common::String sc = g_lingo->pop().asString();
+	Common::String result;
+	if (me->_spaceCollections.contains(sc)) {
+		result = "SPACECOLLECTION " + me->_curSpaceCollection;
+	}
+
+	debugC(5, kDebugXObj, "SpaceMgr::m_getSpaceCollection: %s", result.c_str());
+	g_lingo->push(Datum(result));
 }
 
 void SpaceMgr::m_addSpace(int nargs) {
@@ -468,7 +485,7 @@ void SpaceMgr::m_getCurSpace(int nargs) {
 	}
 	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
 	Common::String result;
-	if (!me->_curSpaceCollection.empty()) {
+	if (!me->_curSpace.empty()) {
 		if (me->_spaceCollections.contains(me->_curSpaceCollection)) {
 			SpaceCollection &sc = me->_spaceCollections.getVal(me->_curSpaceCollection);
 			if (sc.spaces.contains(me->_curSpace)) {
@@ -482,9 +499,24 @@ void SpaceMgr::m_getCurSpace(int nargs) {
 }
 
 void SpaceMgr::m_getSpace(int nargs) {
-	g_lingo->printSTUBWithArglist("SpaceMgr::m_getSpace", nargs);
-	g_lingo->dropStack(nargs);
-	g_lingo->push(Datum(""));
+	if (nargs != 1) {
+		warning("SpaceMgr::m_getSpace: expected 1 argument");
+		g_lingo->dropStack(nargs);
+		g_lingo->push(Datum(""));
+		return;
+	}
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
+	Common::String sp = g_lingo->pop().asString();
+	Common::String result;
+	if (me->_spaceCollections.contains(me->_curSpaceCollection)) {
+		SpaceCollection &sc = me->_spaceCollections.getVal(me->_curSpaceCollection);
+		if (sc.spaces.contains(sp)) {
+			result = "SPACE " + sp;
+		}
+	}
+
+	debugC(5, kDebugXObj, "SpaceMgr::m_getSpace: %s", result.c_str());
+	g_lingo->push(Datum(result));
 }
 
 void SpaceMgr::m_addNode(int nargs) {
@@ -527,7 +559,7 @@ void SpaceMgr::m_getCurNode(int nargs) {
 	}
 	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
 	Common::String result;
-	if (!me->_curView.empty()) {
+	if (!me->_curNode.empty()) {
 		if (me->_spaceCollections.contains(me->_curSpaceCollection)) {
 			SpaceCollection &sc = me->_spaceCollections.getVal(me->_curSpaceCollection);
 			if (sc.spaces.contains(me->_curSpace)) {
@@ -538,14 +570,33 @@ void SpaceMgr::m_getCurNode(int nargs) {
 			}
 		}
 	}
+
 	debugC(5, kDebugXObj, "SpaceMgr::m_getCurNode: %s", result.c_str());
 	g_lingo->push(Datum(result));
 }
 
 void SpaceMgr::m_getNode(int nargs) {
-	g_lingo->printSTUBWithArglist("SpaceMgr::m_getNode", nargs);
-	g_lingo->dropStack(nargs);
-	g_lingo->push(Datum(""));
+	if (nargs != 1) {
+		warning("SpaceMgr::m_getNode: expected 1 argument");
+		g_lingo->dropStack(nargs);
+		g_lingo->push(Datum(""));
+		return;
+	}
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
+	Common::String no = g_lingo->pop().asString();
+	Common::String result;
+	if (me->_spaceCollections.contains(me->_curSpaceCollection)) {
+		SpaceCollection &sc = me->_spaceCollections.getVal(me->_curSpaceCollection);
+		if (sc.spaces.contains(me->_curSpace)) {
+			Space &s = sc.spaces.getVal(me->_curSpace);
+			if (s.nodes.contains(no)) {
+				result = "NODE " + no;
+			}
+		}
+	}
+
+	debugC(5, kDebugXObj, "SpaceMgr::m_getNode: %s", result.c_str());
+	g_lingo->push(Datum(result));
 }
 
 void SpaceMgr::m_addView(int nargs) {
@@ -596,7 +647,7 @@ void SpaceMgr::m_getCurView(int nargs) {
 				if (s.nodes.contains(me->_curNode)) {
 					Node &n = s.nodes.getVal(me->_curNode);
 					if (n.views.contains(me->_curView)) {
-						result = "VIEW " + me->_curView + " " + n.views[me->_curView];
+						result = "VIEW " + me->_curView + " " + n.views[me->_curView].payload;
 					}
 				}
 			}
@@ -608,9 +659,30 @@ void SpaceMgr::m_getCurView(int nargs) {
 }
 
 void SpaceMgr::m_getView(int nargs) {
-	g_lingo->printSTUBWithArglist("SpaceMgr::m_getView", nargs);
-	g_lingo->dropStack(nargs);
-	g_lingo->push(Datum(""));
+	if (nargs != 1) {
+		warning("SpaceMgr::m_getView: expected 1 argument");
+		g_lingo->dropStack(nargs);
+		g_lingo->push(Datum(""));
+		return;
+	}
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
+	Common::String vi = g_lingo->pop().asString();
+	Common::String result;
+	if (me->_spaceCollections.contains(me->_curSpaceCollection)) {
+		SpaceCollection &sc = me->_spaceCollections.getVal(me->_curSpaceCollection);
+		if (sc.spaces.contains(me->_curSpace)) {
+			Space &s = sc.spaces.getVal(me->_curSpace);
+			if (s.nodes.contains(me->_curNode)) {
+				Node &n = s.nodes.getVal(me->_curNode);
+				if (n.views.contains(vi)) {
+					result = "VIEW " + vi + " " + n.views[vi].payload;
+				}
+			}
+		}
+	}
+
+	debugC(5, kDebugXObj, "SpaceMgr::m_getView: %s", result.c_str());
+	g_lingo->push(Datum(result));
 }
 
 void SpaceMgr::m_addLocalLink(int nargs) {
@@ -632,9 +704,33 @@ void SpaceMgr::m_removeLocalLinks(int nargs) {
 }
 
 void SpaceMgr::m_getLocalLink(int nargs) {
-	g_lingo->printSTUBWithArglist("SpaceMgr::m_getLocalLink", nargs);
-	g_lingo->dropStack(nargs);
-	g_lingo->push(Datum(""));
+	if (nargs != 1) {
+		warning("SpaceMgr::m_getLocalLink: expected 1 argument");
+		g_lingo->dropStack(nargs);
+		g_lingo->push(Datum(""));
+		return;
+	}
+	SpaceMgrXObject *me = static_cast<SpaceMgrXObject *>(g_lingo->_state->me.u.obj);
+	Common::String ll = g_lingo->pop().asString();
+	Common::String result;
+	if (me->_spaceCollections.contains(me->_curSpaceCollection)) {
+		SpaceCollection &sc = me->_spaceCollections.getVal(me->_curSpaceCollection);
+		if (sc.spaces.contains(me->_curSpace)) {
+			Space &s = sc.spaces.getVal(me->_curSpace);
+			if (s.nodes.contains(me->_curNode)) {
+				Node &n = s.nodes.getVal(me->_curNode);
+				if (n.views.contains(me->_curView)) {
+					View &v = n.views.getVal(me->_curView);
+					if (v.llinks.contains(ll)) {
+						result = "LLINK " + ll + " " + v.llinks[ll].payload;
+					}
+				}
+			}
+		}
+	}
+
+	debugC(5, kDebugXObj, "SpaceMgr::m_getLocalLink: %s", result.c_str());
+	g_lingo->push(Datum(result));
 }
 
 void SpaceMgr::m_getLocalLinks(int nargs) {
diff --git a/engines/director/lingo/xlibs/spacemgr.h b/engines/director/lingo/xlibs/spacemgr.h
index 51744a15388..5ec850fc0e1 100644
--- a/engines/director/lingo/xlibs/spacemgr.h
+++ b/engines/director/lingo/xlibs/spacemgr.h
@@ -26,8 +26,17 @@ namespace Director {
 
 namespace SpaceMgr {
 
+struct LLink {
+	Common::String payload;
+};
+
+struct View {
+	Common::String payload;
+	Common::HashMap<Common::String, LLink> llinks;
+};
+
 struct Node {
-	Common::HashMap<Common::String, Common::String> views;
+	Common::HashMap<Common::String, View> views;
 };
 
 struct Space {


Commit: 7c975eff436ba879a3143f54fb7e04247473d9c2
    https://github.com/scummvm/scummvm/commit/7c975eff436ba879a3143f54fb7e04247473d9c2
Author: Scott Percival (code at moral.net.au)
Date: 2022-12-09T01:10:57+01:00

Commit Message:
DIRECTOR: LINGO: Adjust freezing/thawing behaviour

Changed paths:
    engines/director/lingo/lingo-code.cpp
    engines/director/lingo/lingo-funcs.cpp
    engines/director/lingo/lingo.cpp
    engines/director/lingo/lingo.h
    engines/director/score.cpp
    engines/director/window.cpp
    engines/director/window.h


diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 26fa88d0ace..51e6b53c417 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -362,11 +362,6 @@ void Lingo::popContext(bool aborting) {
 	g_debugger->popContextHook();
 }
 
-bool Lingo::hasFrozenState() {
-	Window *window = _vm->getCurrentWindow();
-	return window->hasFrozenLingoState();
-}
-
 void Lingo::freezeState() {
 	Window *window = _vm->getCurrentWindow();
 	window->freezeLingoState();
diff --git a/engines/director/lingo/lingo-funcs.cpp b/engines/director/lingo/lingo-funcs.cpp
index c80eca31ed3..15ff9b4a2f9 100644
--- a/engines/director/lingo/lingo-funcs.cpp
+++ b/engines/director/lingo/lingo-funcs.cpp
@@ -197,9 +197,7 @@ void Lingo::func_goto(Datum &frame, Datum &movie) {
 
 	// If there isn't already frozen Lingo (e.g. from a previous func_goto we haven't yet unfrozen),
 	// freeze this script context. We'll return to it after entering the next frame.
-	if (!g_lingo->hasFrozenState()) {
-		g_lingo->_freezeState = true;
-	}
+	g_lingo->_freezeState = true;
 
 	if (movie.type != VOID) {
 		Common::String movieFilenameRaw = movie.asString();
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index b45ccc171f7..9bbf57de4c0 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -568,9 +568,9 @@ void Lingo::execute() {
 				debug("me: %s", _state->me.asString(true).c_str());
 		}
 
-		if (debugChannelSet(3, kDebugLingoExec)) {
+		if (debugChannelSet(4, kDebugLingoExec)) {
 			Common::String instr = decodeInstruction(_state->script, _state->pc);
-			debugC(3, kDebugLingoExec, "[%5d]: %s", current, instr.c_str());
+			debugC(4, kDebugLingoExec, "[%5d]: %s", current, instr.c_str());
 		}
 
 		g_debugger->stepHook();
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index a2164251177..665faccf969 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -359,7 +359,6 @@ public:
 	void execute();
 	void switchStateFromWindow();
 	void freezeState();
-	bool hasFrozenState();
 	void pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRetVal, int paramCount);
 	void popContext(bool aborting = false);
 	void cleanLocalVars();
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index bb2edac8a8e..bb77312f017 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -471,12 +471,6 @@ void Score::update() {
 	debugC(1, kDebugLoading, "******************************  Current frame: %d, time: %d", _currentFrame, g_system->getMillis(false));
 	g_debugger->frameHook();
 
-	if (_window->hasFrozenLingoState()) {
-		_window->thawLingoState();
-		g_lingo->switchStateFromWindow();
-		g_lingo->execute();
-	}
-
 	_lingo->executeImmediateScripts(_frames[_currentFrame]);
 
 	if (_vm->getVersion() >= 600) {
@@ -492,7 +486,26 @@ void Score::update() {
 
 	// Enter and exit from previous frame
 	if (!_vm->_playbackPaused) {
-		_movie->processEvent(kEventEnterFrame); // Triggers the frame script in D2-3, explicit enterFrame handlers in D4+
+		uint32 count = _window->frozenLingoStateCount();
+		// Triggers the frame script in D2-3, explicit enterFrame handlers in D4+
+		_movie->processEvent(kEventEnterFrame);
+		// If another frozen state gets triggered, wait another update() before thawing
+		if (_window->frozenLingoStateCount() > count)
+			return;
+	}
+
+	// Attempt to thaw and continue any frozen execution after startMovie and enterFrame
+	while (uint32 count = _window->frozenLingoStateCount()) {
+		_window->thawLingoState();
+		g_lingo->switchStateFromWindow();
+		g_lingo->execute();
+		if (_window->frozenLingoStateCount() >= count) {
+			debugC(3, kDebugLingoExec, "State froze again mid-thaw, interrupting");
+			return;
+		}
+	}
+
+	if (!_vm->_playbackPaused) {
 		if ((_vm->getVersion() >= 300 && _vm->getVersion() < 400) || _movie->_allowOutdatedLingo) {
 			// Movie version of enterFrame, for D3 only. The D3 Interactivity Manual claims
 			// "This handler executes before anything else when the playback head moves."
@@ -502,6 +515,7 @@ void Score::update() {
 		if (_movie->_timeOutPlay)
 			_movie->_lastTimeOut = _vm->getMacTicks();
 	}
+
 	// TODO Director 6 - another order
 
 	// TODO: Figure out when exactly timeout events are processed
diff --git a/engines/director/window.cpp b/engines/director/window.cpp
index 7d20074d92c..f046129eb1a 100644
--- a/engines/director/window.cpp
+++ b/engines/director/window.cpp
@@ -49,7 +49,6 @@ Window::Window(int id, bool scrollable, bool resizable, bool editable, Graphics:
 	_puppetTransition = nullptr;
 	_soundManager = new DirectorSound(this);
 	_lingoState = new LingoState;
-	_frozenLingoState = nullptr;
 
 	_currentMovie = nullptr;
 	_mainArchive = nullptr;
@@ -430,17 +429,13 @@ Common::String Window::getSharedCastPath() {
 }
 
 void Window::freezeLingoState() {
-	if (_frozenLingoState) {
-		warning("State already frozen! Ditching callstack with %d frames", _frozenLingoState->callstack.size());
-		delete _frozenLingoState;
-	}
-	_frozenLingoState = _lingoState;
+	_frozenLingoStates.push_back(_lingoState);
 	_lingoState = new LingoState;
-	debugC(kDebugLingoExec, 5, "Freezing Lingo state");
+	debugC(kDebugLingoExec, 3, "Freezing Lingo state, depth %d", _frozenLingoStates.size());
 }
 
 void Window::thawLingoState() {
-	if (!_frozenLingoState) {
+	if (_frozenLingoStates.empty()) {
 		warning("Tried to thaw when there's no frozen state, ignoring");
 		return;
 	}
@@ -449,9 +444,9 @@ void Window::thawLingoState() {
 		return;
 	}
 	delete _lingoState;
-	_lingoState = _frozenLingoState;
-	_frozenLingoState = nullptr;
-	debugC(kDebugLingoExec, 5, "Thawing Lingo state");
+	debugC(kDebugLingoExec, 3, "Thawing Lingo state, depth %d", _frozenLingoStates.size());
+	_lingoState = _frozenLingoStates.back();
+	_frozenLingoStates.pop_back();
 }
 
 } // End of namespace Director
diff --git a/engines/director/window.h b/engines/director/window.h
index c03ccecb1a7..9ee9fc746ba 100644
--- a/engines/director/window.h
+++ b/engines/director/window.h
@@ -152,7 +152,7 @@ public:
 	Common::String getSharedCastPath();
 
 	LingoState *getLingoState() { return _lingoState; };
-	bool hasFrozenLingoState() { return _frozenLingoState != nullptr; };
+	uint32 frozenLingoStateCount() { return _frozenLingoStates.size(); };
 	void freezeLingoState();
 	void thawLingoState();
 
@@ -204,7 +204,7 @@ private:
 	DirectorEngine *_vm;
 	DirectorSound *_soundManager;
 	LingoState *_lingoState;
-	LingoState *_frozenLingoState;
+	Common::Array<LingoState *> _frozenLingoStates;
 	bool _isStage;
 	Archive *_mainArchive;
 	Movie *_currentMovie;


Commit: 7a8d4bfeb97d4d673329b68345c69f70da64103e
    https://github.com/scummvm/scummvm/commit/7a8d4bfeb97d4d673329b68345c69f70da64103e
Author: Scott Percival (code at moral.net.au)
Date: 2022-12-09T01:10:57+01:00

Commit Message:
DIRECTOR: LINGO: Add func_goto* logs to kDebugLingoExec

Changed paths:
    engines/director/lingo/lingo-code.cpp
    engines/director/lingo/lingo-funcs.cpp


diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 51e6b53c417..d85b09afd9c 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -851,7 +851,7 @@ Datum LC::negateData(Datum &d) {
 	} else if (res.type == FLOAT) {
 		res.u.f = -res.u.f;
 	} else {
-		warning("LC::negateData(): not supported for type %s", res.type2str());
+		warning("LC::negateData(): not supported for type %s", d.type2str());
 	}
 
 	return res;
diff --git a/engines/director/lingo/lingo-funcs.cpp b/engines/director/lingo/lingo-funcs.cpp
index 15ff9b4a2f9..2f5d8c64a50 100644
--- a/engines/director/lingo/lingo-funcs.cpp
+++ b/engines/director/lingo/lingo-funcs.cpp
@@ -216,9 +216,13 @@ void Lingo::func_goto(Datum &frame, Datum &movie) {
 		stage->_nextMovie.frameI = -1;
 
 		if (frame.type == STRING) {
+			debugC(3, kDebugLingoExec, "Lingo::func_goto(): going to movie \"%s\", frame \"%s\"", movieFilenameRaw.c_str(), frame.u.s->c_str());
 			stage->_nextMovie.frameS = *frame.u.s;
 		} else if (frame.type != VOID) {
+			debugC(3, kDebugLingoExec, "Lingo::func_goto(): going to movie \"%s\", frame %d", movieFilenameRaw.c_str(), frame.asInt());
 			stage->_nextMovie.frameI = frame.asInt();
+		} else {
+			debugC(3, kDebugLingoExec, "Lingo::func_goto(): going to start of movie \"%s\"", movieFilenameRaw.c_str());
 		}
 
 		// Set cursor to watch.
@@ -229,8 +233,10 @@ void Lingo::func_goto(Datum &frame, Datum &movie) {
 	}
 
 	if (frame.type == STRING) {
+		debugC(3, kDebugLingoExec, "Lingo::func_goto(): going to frame \"%s\"", frame.u.s->c_str());
 		score->setStartToLabel(*frame.u.s);
 	} else {
+		debugC(3, kDebugLingoExec, "Lingo::func_goto(): going to frame %d", frame.asInt());
 		score->setCurrentFrame(frame.asInt());
 	}
 }
@@ -238,8 +244,10 @@ void Lingo::func_goto(Datum &frame, Datum &movie) {
 void Lingo::func_gotoloop() {
 	if (!_vm->getCurrentMovie())
 		return;
+	Score *score = _vm->getCurrentMovie()->getScore();
+	debugC(3, kDebugLingoExec, "Lingo::func_gotoloop(): looping frame %d", score->getCurrentFrame());
 
-	_vm->getCurrentMovie()->getScore()->gotoLoop();
+	score->gotoLoop();
 
 	_vm->_skipFrameAdvance = true;
 }
@@ -248,7 +256,9 @@ void Lingo::func_gotonext() {
 	if (!_vm->getCurrentMovie())
 		return;
 
-	_vm->getCurrentMovie()->getScore()->gotoNext();
+	Score *score = _vm->getCurrentMovie()->getScore();
+	score->gotoNext();
+	debugC(3, kDebugLingoExec, "Lingo::func_gotonext(): going to next frame %d", score->getNextFrame());
 
 	_vm->_skipFrameAdvance = true;
 }
@@ -257,7 +267,9 @@ void Lingo::func_gotoprevious() {
 	if (!_vm->getCurrentMovie())
 		return;
 
-	_vm->getCurrentMovie()->getScore()->gotoPrevious();
+	Score *score = _vm->getCurrentMovie()->getScore();
+	score->gotoPrevious();
+	debugC(3, kDebugLingoExec, "Lingo::func_gotoprevious(): going to previous frame %d", score->getNextFrame());
 
 	_vm->_skipFrameAdvance = true;
 }


Commit: 65b341b5c1e15099bbc180e8107a1f596ab99532
    https://github.com/scummvm/scummvm/commit/65b341b5c1e15099bbc180e8107a1f596ab99532
Author: Scott Percival (code at moral.net.au)
Date: 2022-12-09T01:10:57+01:00

Commit Message:
DIRECTOR: LINGO: Fix LC::negateData to work on a copy

Fixes scene switching in DEVO Presents: Adventures of the Smart Patrol.

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


diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index d85b09afd9c..1fde73d05f0 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -845,11 +845,11 @@ Datum LC::negateData(Datum &d) {
 		return res;
 	}
 
-	Datum res = d;
-	if (res.type == INT) {
-		res.u.i = -res.u.i;
-	} else if (res.type == FLOAT) {
-		res.u.f = -res.u.f;
+	Datum res;
+	if (d.type == INT) {
+		res = Datum(-d.asInt());
+	} else if (d.type == FLOAT) {
+		res = Datum(-d.asFloat());
 	} else {
 		warning("LC::negateData(): not supported for type %s", d.type2str());
 	}


Commit: f84d643e950b16729b52ee59ad89db48110630fe
    https://github.com/scummvm/scummvm/commit/f84d643e950b16729b52ee59ad89db48110630fe
Author: Scott Percival (code at moral.net.au)
Date: 2022-12-09T01:10:57+01:00

Commit Message:
DIRECTOR: LINGO: Freeze state when score stops

If the play state for the score stops for whatever reason, Lingo will
attempt to bail early so the movie can switch over. Freeze the state so
that we can pick up execution where we left off.

Fixes the location/time announcements in DEVO Presents: Adventures of
the Smart Patrol.

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


diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index 9bbf57de4c0..32cb8dea1be 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -552,8 +552,10 @@ void Lingo::execute() {
 		if (localCounter > 0 && localCounter % 100 == 0) {
 			_vm->processEvents();
 			g_system->updateScreen();
-			if (_vm->getCurrentMovie()->getScore()->_playState == kPlayStopped)
+			if (_vm->getCurrentMovie()->getScore()->_playState == kPlayStopped) {
+				_freezeState = true;
 				break;
+			}
 		}
 
 		uint current = _state->pc;


Commit: 93373b0795aa4757975bf73090bddeb768064cfd
    https://github.com/scummvm/scummvm/commit/93373b0795aa4757975bf73090bddeb768064cfd
Author: Scott Percival (code at moral.net.au)
Date: 2022-12-09T01:10:57+01:00

Commit Message:
DIRECTOR: Allow Lingo::pushContext to set me handle to null

It's possible to call a method designed for a me object with any
nonsense in the first argument. By design, this should look up the
clean function handler in the script context instead of the one in the
offspring object. However the "me" handle needs to be nulled out,
or else accessing properties will leak through from the previous
object.

Fixes saving games in DEVO Presents: Adventures of the Smart Patrol.

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


diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 1fde73d05f0..28e4ddf14c9 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -242,8 +242,7 @@ void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRet
 
 	_state->script = funcSym.u.defn;
 
-	if (funcSym.target)
-		_state->me = funcSym.target;
+	_state->me = funcSym.target;
 
 	if (funcSym.ctx) {
 		_state->context = funcSym.ctx;


Commit: 94c064e84b60b13e49354369d9f5c27cf9dcc9a8
    https://github.com/scummvm/scummvm/commit/94c064e84b60b13e49354369d9f5c27cf9dcc9a8
Author: Scott Percival (code at moral.net.au)
Date: 2022-12-09T01:10:57+01:00

Commit Message:
DIRECTOR: LINGO: Add b_deleteOne

This function is available undocumented in Director 4. It is properly
documented in Director 5.

Used by DEVO Presents: Adventures of the Smart Patrol

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


diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index f4bd63132b3..c4ce3ed0e33 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -82,6 +82,7 @@ static BuiltinProto builtins[] = {
 	{ "append",			LB::b_append,		2, 2, 400, HBLTIN },	//			D4 h
 	{ "count",			LB::b_count,		1, 1, 400, FBLTIN },	//			D4 f
 	{ "deleteAt",		LB::b_deleteAt,		2, 2, 400, HBLTIN },	//			D4 h
+	{ "deleteOne",		LB::b_deleteOne,	2, 2, 400, HBLTIN },	//			D4 h, undocumented?
 	{ "deleteProp",		LB::b_deleteProp,	2, 2, 400, HBLTIN },	//			D4 h
 	{ "findPos",		LB::b_findPos,		2, 2, 400, FBLTIN },	//			D4 f
 	{ "findPosNear",	LB::b_findPosNear,	2, 2, 400, FBLTIN },	//			D4 f
@@ -672,6 +673,37 @@ void LB::b_deleteAt(int nargs) {
 	}
 }
 
+void LB::b_deleteOne(int nargs) {
+	Datum indexD = g_lingo->pop();
+	Datum list = g_lingo->pop();
+	TYPECHECK3(indexD, INT, FLOAT, SYMBOL);
+	TYPECHECK2(list, ARRAY, PARRAY);
+
+	switch (list.type) {
+	case ARRAY: {
+		g_lingo->push(list);
+		g_lingo->push(indexD);
+		b_getPos(nargs);
+		int index = g_lingo->pop().asInt();
+		if (index > 0) {
+			list.u.farr->arr.remove_at(index - 1);
+		}
+		break;
+	}
+	case PARRAY: {
+		Datum d;
+		int index = LC::compareArrays(LC::eqData, list, indexD, true, true).u.i;
+		if (index > 0) {
+			list.u.parr->arr.remove_at(index - 1);
+		}
+		break;
+	}
+	default:
+		TYPECHECK2(list, ARRAY, PARRAY);
+	}
+}
+
+
 void LB::b_deleteProp(int nargs) {
 	Datum prop = g_lingo->pop();
 	Datum list = g_lingo->pop();
diff --git a/engines/director/lingo/lingo-builtins.h b/engines/director/lingo/lingo-builtins.h
index 5694b9bbc48..53f344ceb3d 100644
--- a/engines/director/lingo/lingo-builtins.h
+++ b/engines/director/lingo/lingo-builtins.h
@@ -54,6 +54,7 @@ void b_addProp(int nargs);
 void b_append(int nargs);
 void b_count(int nargs);
 void b_deleteAt(int nargs);
+void b_deleteOne(int nargs);
 void b_deleteProp(int nargs);
 void b_findPos(int nargs);
 void b_findPosNear(int nargs);


Commit: 7714e09382285ea4f12a0682444b30abedbba521
    https://github.com/scummvm/scummvm/commit/7714e09382285ea4f12a0682444b30abedbba521
Author: Scott Percival (code at moral.net.au)
Date: 2022-12-09T01:10:57+01:00

Commit Message:
DIRECTOR: LINGO: Remove duplicate insert in b_addProp

Previously, all property list inserts would happen twice, causing
widespread mayhem. Property lists are allowed to have duplicate keys,
which may have disguised the impact of this bug.

Fixes lots of voice clips playing twice in DEVO Presents: Adventures
of the Smart Patrol.

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 c4ce3ed0e33..d9ea34d27ea 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -591,7 +591,6 @@ void LB::b_addProp(int nargs) {
 	TYPECHECK(list, PARRAY);
 
 	PCell cell = PCell(prop, value);
-	list.u.parr->arr.push_back(cell);
 
 	if (list.u.parr->_sorted) {
 		if (list.u.parr->arr.empty())


Commit: 2674b628c49e8e52958ddfcd0bfbd9cb320a4cd3
    https://github.com/scummvm/scummvm/commit/2674b628c49e8e52958ddfcd0bfbd9cb320a4cd3
Author: Scott Percival (code at moral.net.au)
Date: 2022-12-09T01:10:57+01:00

Commit Message:
DIRECTOR: Free frozen lingo states in window destructor

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


diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 28e4ddf14c9..5810c17ff5d 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -249,10 +249,12 @@ void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRet
 		*_state->context->_refCount += 1;
 	}
 
-	DatumHash *localvars = _state->localVars;
-	if (!funcSym.anonymous) {
+	DatumHash *localvars = new DatumHash;
+	if (funcSym.anonymous && _state->localVars) {
 		// Execute anonymous functions within the current var frame.
-		localvars = new DatumHash;
+		for (auto it = _state->localVars->begin(); it != _state->localVars->end(); ++it) {
+			localvars->setVal(it->_key, it->_value);
+		}
 	}
 
 	if (funcSym.argNames) {
@@ -346,11 +348,14 @@ void Lingo::popContext(bool aborting) {
 	_state->pc = fp->retPC;
 	_state->me = fp->retMe;
 
-	// Restore local variables
-	if (!fp->sp.anonymous) {
-		cleanLocalVars();
-		_state->localVars = fp->retLocalVars;
+	// For anonymous functions, copy the local var state back to the parent
+	if (fp->sp.anonymous && fp->retLocalVars) {
+		for (auto it = _state->localVars->begin(); it != _state->localVars->end(); ++it) {
+			fp->retLocalVars->setVal(it->_key, it->_value);
+		}
 	}
+	cleanLocalVars();
+	_state->localVars = fp->retLocalVars;
 
 	if (debugChannelSet(2, kDebugLingoExec)) {
 		printCallStack(_state->pc);
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index 32cb8dea1be..1d435e6d792 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -149,6 +149,27 @@ MenuReference::MenuReference() {
 	menuItemIdStr = nullptr;
 }
 
+LingoState::~LingoState() {
+	for (uint i = 0; i < callstack.size(); i++) {
+		if (callstack[i]->retLocalVars)
+			delete callstack[i]->retLocalVars;
+		if (callstack[i]->retContext) {
+			*callstack[i]->retContext->_refCount -= 1;
+			if (*callstack[i]->retContext->_refCount == 0)
+				delete callstack[i]->retContext;
+		}
+		delete callstack[i];
+	}
+	if (localVars)
+		delete localVars;
+	if (context) {
+		*context->_refCount -= 1;
+		if (*context->_refCount == 0)
+			delete context;
+	}
+
+}
+
 Lingo::Lingo(DirectorEngine *vm) : _vm(vm) {
 	g_lingo = this;
 
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index 665faccf969..631cef1ebb7 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -298,6 +298,8 @@ struct LingoState {
 	ScriptContext *context = nullptr;
 	DatumHash *localVars = nullptr;
 	Datum me;
+
+	~LingoState();
 };
 
 class Lingo {
diff --git a/engines/director/window.cpp b/engines/director/window.cpp
index f046129eb1a..5aa2f75f956 100644
--- a/engines/director/window.cpp
+++ b/engines/director/window.cpp
@@ -67,6 +67,8 @@ Window::~Window() {
 	delete _lingoState;
 	delete _soundManager;
 	delete _currentMovie;
+	for (uint i = 0; i < _frozenLingoStates.size(); i++)
+		delete _frozenLingoStates[i];
 	if (_puppetTransition)
 		delete _puppetTransition;
 }


Commit: 689d08a39609870d960afb092e9ab5e8187c2517
    https://github.com/scummvm/scummvm/commit/689d08a39609870d960afb092e9ab5e8187c2517
Author: Scott Percival (code at moral.net.au)
Date: 2022-12-09T01:10:57+01:00

Commit Message:
DIRECTOR: Add changes from code review

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


diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index d9ea34d27ea..e3316edd83e 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -673,15 +673,15 @@ void LB::b_deleteAt(int nargs) {
 }
 
 void LB::b_deleteOne(int nargs) {
-	Datum indexD = g_lingo->pop();
+	Datum val = g_lingo->pop();
 	Datum list = g_lingo->pop();
-	TYPECHECK3(indexD, INT, FLOAT, SYMBOL);
+	TYPECHECK3(val, INT, FLOAT, SYMBOL);
 	TYPECHECK2(list, ARRAY, PARRAY);
 
 	switch (list.type) {
 	case ARRAY: {
 		g_lingo->push(list);
-		g_lingo->push(indexD);
+		g_lingo->push(val);
 		b_getPos(nargs);
 		int index = g_lingo->pop().asInt();
 		if (index > 0) {
@@ -691,7 +691,7 @@ void LB::b_deleteOne(int nargs) {
 	}
 	case PARRAY: {
 		Datum d;
-		int index = LC::compareArrays(LC::eqData, list, indexD, true, true).u.i;
+		int index = LC::compareArrays(LC::eqData, list, val, true, true).u.i;
 		if (index > 0) {
 			list.u.parr->arr.remove_at(index - 1);
 		}
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index 631cef1ebb7..1696527d1f5 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -292,12 +292,17 @@ struct LingoArchive {
 };
 
 struct LingoState {
-	Common::Array<CFrame *> callstack;
-	uint pc = 0;
-	ScriptData *script = nullptr;
-	ScriptContext *context = nullptr;
-	DatumHash *localVars = nullptr;
-	Datum me;
+	// Execution state for a Lingo process, created every time
+	// a top-level handler is called (e.g. on mouseDown).
+	// Can be swapped out when another script gets called with priority.
+	// Call frames are pushed and popped from the callstack with
+	// pushContext and popContext.
+	Common::Array<CFrame *> callstack;		// call stack
+	uint pc = 0;							// current program counter
+	ScriptData *script = nullptr;			// current Lingo script
+	ScriptContext *context = nullptr;		// current Lingo script context
+	DatumHash *localVars = nullptr;			// current local variables
+	Datum me;								// current me object
 
 	~LingoState();
 };




More information about the Scummvm-git-logs mailing list