[Scummvm-git-logs] scummvm master -> 63a3542684e859c9af94de22db5092ae8294e12e

scemino noreply at scummvm.org
Tue May 28 18:46:54 UTC 2024


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

Summary:
ee77c261aa DIRECTOR: Fix script offsets in imgui
63a3542684 DIRECTOR: Add stepping at lingo byte codes level


Commit: ee77c261aad271e8dcd801ac4c417403c03081c1
    https://github.com/scummvm/scummvm/commit/ee77c261aad271e8dcd801ac4c417403c03081c1
Author: scemino (scemino74 at gmail.com)
Date: 2024-05-28T20:45:35+02:00

Commit Message:
DIRECTOR: Fix script offsets in imgui

Changed paths:
    engines/director/debugtools.cpp
    engines/director/lingo/lingo-bytecode.cpp
    engines/director/lingo/lingo-object.h


diff --git a/engines/director/debugtools.cpp b/engines/director/debugtools.cpp
index 1b00f1e175f..ed0d45c430f 100644
--- a/engines/director/debugtools.cpp
+++ b/engines/director/debugtools.cpp
@@ -80,6 +80,7 @@ typedef struct ImGuiScript {
 	Common::String handlerId;
 	Common::String handlerName;
 	Common::String moviePath;
+	Common::Array<uint32> byteOffsets;
 
 	bool isMethod = false;
 	bool isGenericEvent = false;
@@ -468,10 +469,10 @@ ImGuiScript toImGuiScript(CastMemberID id, const Common::String &handlerId) {
 
 static void setScriptToDisplay(const ImGuiScript &script);
 
-static Director::Breakpoint *getBreakpoint(const Common::String &handlerName, int pc) {
+static Director::Breakpoint *getBreakpoint(const Common::String &handlerName, uint16 scriptId, uint pc) {
 	auto &bps = g_lingo->getBreakpoints();
 	for (uint i = 0; i < bps.size(); i++) {
-		if (bps[i].type == kBreakpointFunction && bps[i].funcName == handlerName && (int)bps[i].funcOffset == pc) {
+		if (bps[i].type == kBreakpointFunction && bps[i].scriptId == scriptId && bps[i].funcName == handlerName && bps[i].funcOffset == pc) {
 			return &bps[i];
 		}
 	}
@@ -1537,14 +1538,15 @@ private:
 		ImGui::Text("%s", (s + code).c_str());
 	}
 
-	void renderLine(uint pc) const {
+	void renderLine(uint p) const {
+		uint pc = _script.byteOffsets[p];
 		ImDrawList *dl = ImGui::GetWindowDrawList();
 		ImVec2 pos = ImGui::GetCursorScreenPos();
 		const ImVec2 mid(pos.x + 7, pos.y + 7);
 
 		ImVec4 color = _state->_colors._bp_color_disabled;
 
-		Director::Breakpoint *bp = getBreakpoint(_script.handlerName, pc);
+		Director::Breakpoint *bp = getBreakpoint(_script.handlerId, _script.id.member, pc);
 		if (bp)
 			color = _state->_colors._bp_color_enabled;
 
@@ -1556,7 +1558,8 @@ private:
 			} else {
 				Director::Breakpoint newBp;
 				newBp.type = kBreakpointFunction;
-				newBp.funcName = _script.handlerName;
+				newBp.scriptId = _script.id.member;
+				newBp.funcName = _script.handlerId;
 				newBp.funcOffset = pc;
 				g_lingo->addBreakpoint(newBp);
 				color = _state->_colors._bp_color_enabled;
@@ -2423,7 +2426,7 @@ static void renderCastScript(Symbol &sym) {
 
 		color = _state->_colors._bp_color_disabled;
 
-		Director::Breakpoint *bp = getBreakpoint(handlerName, pc);
+		Director::Breakpoint *bp = getBreakpoint(handlerName, sym.ctx->_id, pc);
 		if (bp)
 			color = _state->_colors._bp_color_enabled;
 
@@ -2646,6 +2649,7 @@ static void showFuncList() {
 							if (ImGui::Selectable(function.c_str())) {
 								CastMemberID memberID(scriptContext._key, cast._key);
 								ImGuiScript script = toImGuiScript(memberID, functionHandler._key);
+								script.byteOffsets = scriptContext._value->_functionByteOffsets[script.handlerId];
 								script.moviePath = movie->getArchive()->getPathName().toString();
 								script.handlerName = getHandlerName(functionHandler._value);
 								setScriptToDisplay(script);
@@ -2683,6 +2687,7 @@ static void showFuncList() {
 							if (ImGui::Selectable(function.c_str())) {
 								CastMemberID memberID(scriptContext._key, SHARED_CAST_LIB);
 								ImGuiScript script = toImGuiScript(memberID, functionHandler._key);
+								script.byteOffsets = scriptContext._value->_functionByteOffsets[script.handlerId];
 								script.moviePath = movie->getArchive()->getPathName().toString();
 								script.handlerName = getHandlerName(functionHandler._value);
 								setScriptToDisplay(script);
diff --git a/engines/director/lingo/lingo-bytecode.cpp b/engines/director/lingo/lingo-bytecode.cpp
index 452aef9d4fb..afc5ff4e168 100644
--- a/engines/director/lingo/lingo-bytecode.cpp
+++ b/engines/director/lingo/lingo-bytecode.cpp
@@ -1399,6 +1399,7 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
 		uint32 pointer = startOffset - codeStoreOffset;
 		Common::Array<uint32> offsetList;
 		Common::Array<uint32> jumpList;
+		Common::Array<uint32> byteOffsets;
 
 		// Size of an entry in the consts index.
 		int constEntrySize = 0;
@@ -1418,6 +1419,7 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
 				// Opcode for pushing a value from the constants table.
 				// Rewrite these to inline the constant into our bytecode.
 				offsetList.push_back(_currentAssembly->size());
+				byteOffsets.push_back(_currentAssembly->size());
 				int arg = 0;
 				if (opcode == 0x84) {
 					arg = (uint16)READ_BE_UINT16(&codeStore[pointer]);
@@ -1450,8 +1452,11 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
 				if (opcode == 0x84) {
 					offsetList.push_back(_currentAssembly->size());
 					offsetList.push_back(_currentAssembly->size());
+					byteOffsets.push_back(_currentAssembly->size());
+					byteOffsets.push_back(_currentAssembly->size());
 				} else {
 					offsetList.push_back(_currentAssembly->size());
+					byteOffsets.push_back(_currentAssembly->size());
 				}
 				switch (constant.type) {
 				case INT:
@@ -1469,6 +1474,7 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
 				}
 			} else if (g_lingo->_lingoV4.contains(opcode)) {
 				offsetList.push_back(_currentAssembly->size());
+				byteOffsets.push_back(_currentAssembly->size());
 				code1(g_lingo->_lingoV4[opcode]->func);
 
 				size_t argc = strlen(g_lingo->_lingoV4[opcode]->proto);
@@ -1480,12 +1486,14 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
 						case 'b':
 							// read one uint8 as an argument
 							offsetList.push_back(_currentAssembly->size());
+							byteOffsets.push_back(_currentAssembly->size());
 							arg = (uint8)codeStore[pointer];
 							pointer += 1;
 							break;
 						case 'B':
 							// read one int8 as an argument
 							offsetList.push_back(_currentAssembly->size());
+							byteOffsets.push_back(_currentAssembly->size());
 							arg = (int8)codeStore[pointer];
 							pointer += 1;
 							break;
@@ -1493,6 +1501,8 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
 							// read one uint16 as an argument
 							offsetList.push_back(_currentAssembly->size());
 							offsetList.push_back(_currentAssembly->size());
+							byteOffsets.push_back(_currentAssembly->size());
+							byteOffsets.push_back(_currentAssembly->size());
 							arg = (uint16)READ_BE_UINT16(&codeStore[pointer]);
 							pointer += 2;
 							break;
@@ -1500,6 +1510,8 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
 							// read one int16 as an argument
 							offsetList.push_back(_currentAssembly->size());
 							offsetList.push_back(_currentAssembly->size());
+							byteOffsets.push_back(_currentAssembly->size());
+							byteOffsets.push_back(_currentAssembly->size());
 							arg = (int16)READ_BE_INT16(&codeStore[pointer]);
 							pointer += 2;
 							break;
@@ -1555,24 +1567,30 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
 				if (opcode < 0x40) { // 1 byte instruction
 					debugC(5, kDebugCompile, "Unimplemented opcode: 0x%02x", opcode);
 					offsetList.push_back(_currentAssembly->size());
+					byteOffsets.push_back(_currentAssembly->size());
 					code1(LC::cb_unk);
 					codeInt(opcode);
 				} else if (opcode < 0x80) { // 2 byte instruction
 					debugC(5, kDebugCompile, "Unimplemented opcode: 0x%02x (%d)", opcode, (uint)codeStore[pointer]);
 					offsetList.push_back(_currentAssembly->size());
+					byteOffsets.push_back(_currentAssembly->size());
 					code1(LC::cb_unk1);
 					codeInt(opcode);
 					offsetList.push_back(_currentAssembly->size());
+					byteOffsets.push_back(_currentAssembly->size());
 					codeInt((uint)codeStore[pointer]);
 					pointer += 1;
 				} else { // 3 byte instruction
 					debugC(5, kDebugCompile, "Unimplemented opcode: 0x%02x (%d, %d)", opcode, (uint)codeStore[pointer], (uint)codeStore[pointer+1]);
 					offsetList.push_back(_currentAssembly->size());
+					byteOffsets.push_back(_currentAssembly->size());
 					code1(LC::cb_unk2);
 					codeInt(opcode);
 					offsetList.push_back(_currentAssembly->size());
+					byteOffsets.push_back(_currentAssembly->size());
 					codeInt((uint)codeStore[pointer]);
 					offsetList.push_back(_currentAssembly->size());
+					byteOffsets.push_back(_currentAssembly->size());
 					codeInt((uint)codeStore[pointer+1]);
 					pointer += 2;
 				}
@@ -1622,6 +1640,8 @@ ScriptContext *LingoCompiler::compileLingoV4(Common::SeekableReadStreamEndian &s
 			sym.varNames = varNames;
 		}
 
+		_assemblyContext->_functionByteOffsets[functionName] = byteOffsets;
+
 		if (!skipdump && ConfMan.getBool("dump_scripts")) {
 			out.writeString(g_lingo->formatFunctionBody(sym));
 		}
diff --git a/engines/director/lingo/lingo-object.h b/engines/director/lingo/lingo-object.h
index 53f60a1da0d..f0c7547b0d3 100644
--- a/engines/director/lingo/lingo-object.h
+++ b/engines/director/lingo/lingo-object.h
@@ -210,6 +210,7 @@ public:
 	ScriptType _scriptType;
 	int _id;
 	Common::Array<Common::String> _functionNames; // used by cb_localcall
+	Common::HashMap<Common::String, Common::Array<uint32>> _functionByteOffsets;
 	SymbolHash _functionHandlers;
 	Common::HashMap<uint32, Symbol> _eventHandlers;
 	Common::Array<Datum> _constants;


Commit: 63a3542684e859c9af94de22db5092ae8294e12e
    https://github.com/scummvm/scummvm/commit/63a3542684e859c9af94de22db5092ae8294e12e
Author: scemino (scemino74 at gmail.com)
Date: 2024-05-28T20:45:35+02:00

Commit Message:
DIRECTOR: Add stepping at lingo byte codes level

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


diff --git a/engines/director/debugtools.cpp b/engines/director/debugtools.cpp
index ed0d45c430f..ad9c41d9642 100644
--- a/engines/director/debugtools.cpp
+++ b/engines/director/debugtools.cpp
@@ -88,6 +88,7 @@ typedef struct ImGuiScript {
 	Common::StringArray propertyNames;
 	Common::StringArray globalNames;
 	Common::SharedPtr<LingoDec::HandlerNode> root;
+	Common::Array<LingoDec::Bytecode> bytecodeArray;
 
 	bool operator==(const ImGuiScript &c) const {
 		return moviePath == c.moviePath && score == c.score && id == c.id && handlerId == c.handlerId;
@@ -377,6 +378,7 @@ typedef struct ImGuiState {
 		ImVec4 _bp_color_disabled = ImVec4(0.9f, 0.08f, 0.0f, 0.0f);
 		ImVec4 _bp_color_enabled = ImVec4(0.9f, 0.08f, 0.0f, 1.0f);
 		ImVec4 _bp_color_hover = ImVec4(0.42f, 0.17f, 0.13f, 1.0f);
+		ImVec4 _current_statement = ImColor(IM_COL32(0xFF, 0xFF, 0x00, 0xFF));
 		ImVec4 _line_color = ImVec4(0.44f, 0.44f, 0.44f, 1.0f);
 		ImVec4 _call_color = ImColor(IM_COL32(0xFF, 0xC5, 0x5C, 0xFF));
 		ImVec4 _builtin_color = ImColor(IM_COL32(0x60, 0x7C, 0xFF, 0xFF));
@@ -428,17 +430,23 @@ typedef struct ImGuiState {
 ImGuiState *_state = nullptr;
 
 const LingoDec::Handler *getHandler(CastMemberID id, const Common::String &handlerId) {
-	Director::Movie *movie = g_director->getCurrentMovie();
-	const Director::Cast *cast = movie->getCast(id);
-	if (!cast->_lingodec)
-		return nullptr;
-
-	Common::SharedPtr<LingoDec::Node> node;
-	for (auto it : cast->_lingodec->scripts) {
-		for (const LingoDec::Handler &h : it.second->handlers) {
-			if (h.name != handlerId)
+	const Director::Movie *movie = g_director->getCurrentMovie();
+	const Cast *targets[2] = {movie->getCast(), movie->getSharedCast()};
+	for (int i = 0; i < 2; i++) {
+		const Cast *cast = targets[i];
+		if (!cast)
+			continue;
+		const ScriptContext *ctx = cast->_lingoArchive->findScriptContext(id.member);
+		if (!ctx || !ctx->_functionHandlers.contains(handlerId))
+			continue;
+		for (auto p : cast->_lingodec->scripts) {
+			if (p.second->castID != id.member)
 				continue;
-			return &h;
+			for (const LingoDec::Handler &handler : p.second->handlers) {
+				if (handler.name == handlerId) {
+					return &handler;
+				}
+			}
 		}
 	}
 	return nullptr;
@@ -453,6 +461,7 @@ ImGuiScript toImGuiScript(CastMemberID id, const Common::String &handlerId) {
 	if (!handler)
 		return result;
 
+	result.bytecodeArray = handler->bytecodeArray;
 	result.root = handler->ast.root;
 	result.isGenericEvent = handler->isGenericEvent;
 	result.argumentNames = handler->argumentNames;
@@ -481,11 +490,15 @@ static Director::Breakpoint *getBreakpoint(const Common::String &handlerName, ui
 
 class RenderScriptVisitor : public LingoDec::NodeVisitor {
 public:
-	explicit RenderScriptVisitor(ImGuiScript &script, bool showByteCode) : _script(script), _showByteCode(showByteCode) {}
+	explicit RenderScriptVisitor(ImGuiScript &script, bool showByteCode) : _script(script), _showByteCode(showByteCode) {
+		Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;
+		if (!callstack.empty()) {
+			CFrame *head = callstack[callstack.size() - 1];
+			_isScriptInDebug = (head->sp.ctx->_id == script.id.member) && (*head->sp.name == script.handlerId);
+		}
+	}
 
 	virtual void visit(const LingoDec::HandlerNode &node) override {
-		_handler = node.handler;
-
 		if (_showByteCode) {
 			byteCode(node);
 			return;
@@ -537,9 +550,9 @@ public:
 
 	virtual void visit(const LingoDec::CallNode &node) override {
 		int32 obj = 0;
-		for (uint i = 0; i < _handler->bytecodeArray.size(); i++) {
-			if (node._startOffset == _handler->bytecodeArray[i].pos) {
-				obj = _handler->bytecodeArray[i].obj;
+		for (uint i = 0; i < _script.bytecodeArray.size(); i++) {
+			if (node._startOffset == _script.bytecodeArray[i].pos) {
+				obj = _script.bytecodeArray[i].obj;
 				break;
 			}
 		}
@@ -1452,7 +1465,7 @@ private:
 		}
 	}
 
-	void byteCode(const LingoDec::HandlerNode &node) const {
+	void byteCode(const LingoDec::HandlerNode &node) {
 		LingoDec::Handler *handler = node.handler;
 		bool isMethod = handler->script->isFactory();
 
@@ -1523,13 +1536,13 @@ private:
 		}
 	}
 
-	void write(uint32 offset, const Common::String &code, ImVec4 color = ImVec4(1, 1, 1, 1)) const {
+	void write(uint32 offset, const Common::String &code, ImVec4 color = ImVec4(1, 1, 1, 1)) {
 		renderLine(offset);
 		renderIndentation();
 		ImGui::TextColored(color, "%s", code.c_str());
 	}
 
-	void writeByteCode(uint32 offset, const Common::String &code) const {
+	void writeByteCode(uint32 offset, const Common::String &code) {
 		renderLine(offset);
 		Common::String s;
 		for (int i = 0; i < _indent; i++) {
@@ -1538,19 +1551,34 @@ private:
 		ImGui::Text("%s", (s + code).c_str());
 	}
 
-	void renderLine(uint p) const {
+	void renderLine(uint p) {
+		bool showCurrentStatement = false;
+		p = MIN(p, _script.byteOffsets.size() - 1);
 		uint pc = _script.byteOffsets[p];
+
+		if (_isScriptInDebug && g_lingo->_exec._state == kPause) {
+			// check current statement
+			if (!_currentStatementDisplayed) {
+				if (g_lingo->_state->pc <= pc) {
+					showCurrentStatement = true;
+					_currentStatementDisplayed = true;
+				}
+			}
+		}
+
 		ImDrawList *dl = ImGui::GetWindowDrawList();
-		ImVec2 pos = ImGui::GetCursorScreenPos();
+		const ImVec2 pos = ImGui::GetCursorScreenPos();
+		const float width = ImGui::GetContentRegionAvail().x;
 		const ImVec2 mid(pos.x + 7, pos.y + 7);
 
 		ImVec4 color = _state->_colors._bp_color_disabled;
-
-		Director::Breakpoint *bp = getBreakpoint(_script.handlerId, _script.id.member, pc);
+		const Director::Breakpoint *bp = getBreakpoint(_script.handlerId, _script.id.member, pc);
 		if (bp)
 			color = _state->_colors._bp_color_enabled;
 
 		ImGui::InvisibleButton("Line", ImVec2(16, ImGui::GetFontSize()));
+
+		// click on breakpoint column?
 		if (ImGui::IsItemClicked(0)) {
 			if (color == _state->_colors._bp_color_enabled) {
 				g_lingo->delBreakpoint(bp->id);
@@ -1570,15 +1598,28 @@ private:
 			color = _state->_colors._bp_color_hover;
 		}
 
+		// draw breakpoint
 		if (!bp || bp->enabled)
 			dl->AddCircleFilled(mid, 4.0f, ImColor(color));
 		else
 			dl->AddCircle(mid, 4.0f, ImColor(_state->_colors._line_color));
+
+		// draw current statement
+		if (showCurrentStatement) {
+			dl->AddQuadFilled(ImVec2(pos.x, pos.y + 4.f), ImVec2(pos.x + 9.f, pos.y + 4.f), ImVec2(pos.x + 9.f, pos.y + 10.f), ImVec2(pos.x, pos.y + 10.f), ImColor(_state->_colors._current_statement));
+			dl->AddTriangleFilled(ImVec2(pos.x + 8.f, pos.y), ImVec2(pos.x + 14.f, pos.y + 7.f), ImVec2(pos.x + 8.f, pos.y + 14.f), ImColor(_state->_colors._current_statement));
+			if (!ImGui::IsItemVisible()) {
+				ImGui::SetScrollHereY(0.5f);
+			}
+			dl->AddRectFilled(ImVec2(pos.x + 16.f, pos.y), ImVec2(pos.x + width, pos.y + 16.f), ImColor(IM_COL32(0xFF, 0xFF, 0x00, 0x20)), 0.4f);
+		}
+		// draw separator
 		dl->AddLine(ImVec2(pos.x + 16.0f, pos.y), ImVec2(pos.x + 16.0f, pos.y + 17), ImColor(_state->_colors._line_color));
 
 		ImGui::SetItemTooltip("Click to add a breakpoint");
-
 		ImGui::SameLine();
+
+		// draw offset
 		ImGui::Text("[%5d] ", pc);
 		ImGui::SameLine();
 	}
@@ -1612,7 +1653,8 @@ private:
 	bool _showByteCode = false;
 	bool _dot = false;
 	int _indent = 0;
-	LingoDec::Handler *_handler = nullptr;
+	bool _currentStatementDisplayed = false;
+	bool _isScriptInDebug = false;
 };
 
 static void showControlPanel() {
@@ -1686,8 +1728,10 @@ static void showControlPanel() {
 			p = ImGui::GetCursorScreenPos();
 			ImGui::InvisibleButton("Stop", buttonSize);
 
-			if (ImGui::IsItemClicked(0))
+			if (ImGui::IsItemClicked(0)) {
 				score->_playState = kPlayPaused;
+				g_lingo->_exec._state = kPause;
+			}
 
 			if (ImGui::IsItemHovered())
 				dl->AddRectFilled(ImVec2(p.x + bgX1, p.y + bgX1), ImVec2(p.x + bgX2, p.y + bgX2), bgcolor, 3.0f, ImDrawFlags_RoundCornersAll);
@@ -1724,8 +1768,11 @@ static void showControlPanel() {
 			p = ImGui::GetCursorScreenPos();
 			ImGui::InvisibleButton("Play", buttonSize);
 
-			if (ImGui::IsItemClicked(0))
+			if (ImGui::IsItemClicked(0)) {
 				score->_playState = kPlayStarted;
+				g_lingo->_exec._step = -1;
+				g_lingo->_exec._state = kRunning;
+			}
 
 			if (ImGui::IsItemHovered())
 				dl->AddRectFilled(ImVec2(p.x + bgX1, p.y + bgX1), ImVec2(p.x + bgX2, p.y + bgX2), bgcolor, 3.0f, ImDrawFlags_RoundCornersAll);
@@ -1758,8 +1805,13 @@ static void showControlPanel() {
 			p = ImGui::GetCursorScreenPos();
 			ImGui::InvisibleButton("Step Over", buttonSize);
 
-			if (ImGui::IsItemClicked(0))
+			if (ImGui::IsItemClicked(0)) {
 				score->_playState = kPlayStarted;
+				g_lingo->_exec._state = kRunning;
+				g_lingo->_exec._step = -1;
+				g_lingo->_exec._next._enabled = true;
+				g_lingo->_exec._next._stackSize = g_lingo->_state->callstack.size();
+			}
 
 			if (ImGui::IsItemHovered())
 				dl->AddRectFilled(ImVec2(p.x + bgX1, p.y + bgX1), ImVec2(p.x + bgX2, p.y + bgX2), bgcolor, 3.0f, ImDrawFlags_RoundCornersAll);
@@ -1778,8 +1830,11 @@ static void showControlPanel() {
 			p = ImGui::GetCursorScreenPos();
 			ImGui::InvisibleButton("Step Into", buttonSize);
 
-			if (ImGui::IsItemClicked(0))
+			if (ImGui::IsItemClicked(0)) {
 				score->_playState = kPlayStarted;
+				g_lingo->_exec._step = 1;
+				g_lingo->_exec._state = kRunning;
+			}
 
 			if (ImGui::IsItemHovered())
 				dl->AddRectFilled(ImVec2(p.x + bgX1, p.y + bgX1), ImVec2(p.x + bgX2, p.y + bgX2), bgcolor, 3.0f, ImDrawFlags_RoundCornersAll);
@@ -1797,8 +1852,13 @@ static void showControlPanel() {
 			p = ImGui::GetCursorScreenPos();
 			ImGui::InvisibleButton("Step Out", buttonSize);
 
-			if (ImGui::IsItemClicked(0))
+			if (ImGui::IsItemClicked(0)) {
 				score->_playState = kPlayStarted;
+				g_lingo->_exec._state = kRunning;
+				g_lingo->_exec._step = -1;
+				g_lingo->_exec._next._enabled = true;
+				g_lingo->_exec._next._stackSize = g_lingo->_state->callstack.size() - 1;
+			}
 
 			if (ImGui::IsItemHovered())
 				dl->AddRectFilled(ImVec2(p.x + bgX1, p.y + bgX1), ImVec2(p.x + bgX2, p.y + bgX2), bgcolor, 3.0f, ImDrawFlags_RoundCornersAll);
@@ -2257,6 +2317,10 @@ static void addScriptCastToDisplay(CastMemberID &id) {
 
 static void setScriptToDisplay(const ImGuiScript &script) {
 	uint index = _state->_functions._scripts.size();
+	if (index && _state->_functions._scripts[index - 1] == script) {
+		_state->_functions._showScript = true;
+		return;
+	}
 	_state->_functions._scripts.push_back(script);
 	_state->_functions._current = index;
 	_state->_functions._showScript = true;
@@ -2516,7 +2580,29 @@ static void displayScriptCasts() {
 	}
 }
 
+static void updateCurrentScript() {
+	if (g_lingo->_exec._state != kPause)
+		return;
+
+	Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;
+	if (callstack.empty())
+		return;
+
+	// show current script of the current stack frame
+	CFrame *head = callstack[callstack.size() - 1];
+	Director::Movie *movie = g_director->getCurrentMovie();
+	ScriptContext *scriptContext = head->sp.ctx;
+	int castLibID = movie->getCast()->_castLibID;
+	ImGuiScript script = toImGuiScript(CastMemberID(head->sp.ctx->_id, castLibID), *head->sp.name);
+	script.byteOffsets = scriptContext->_functionByteOffsets[script.handlerId];
+	script.moviePath = movie->getArchive()->getPathName().toString();
+	script.handlerName = head->sp.ctx->_id ? Common::String::format("%d:%s", head->sp.ctx->_id, script.handlerId.c_str()) : script.handlerId;
+	setScriptToDisplay(script);
+}
+
 static void displayScripts() {
+	updateCurrentScript();
+
 	if (!_state->_functions._showScript)
 		return;
 
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index 8a17269e925..fc71f98d71c 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -617,6 +617,24 @@ bool Lingo::execute() {
 	uint localCounter = 0;
 
 	while (!_abort && !_freezeState && _state->script && (*_state->script)[_state->pc] != STOP) {
+		if(_exec._next._enabled && _state->callstack.size() == _exec._next._stackSize) {
+			// we reach the next statement -> pause the execution
+			_exec._state = kPause;
+			_exec._next._enabled = false;
+		}
+		if(!_exec._step || _exec._state == kPause) {
+			// if execution is in pause -> poll event + update screen
+			_exec._state = kPause;
+			Common::EventManager *eventMan = g_system->getEventManager();
+			while (_exec._state == kPause && !eventMan->shouldQuit() && (!g_engine || !eventMan->shouldReturnToLauncher())) {
+				Common::Event event;
+				while (eventMan->pollEvent(event)) {
+				}
+				g_system->delayMillis(10);
+				g_system->updateScreen();
+			}
+		}
+
 		if (_globalCounter > 1000 && debugChannelSet(-1, kDebugFewFramesOnly)) {
 			warning("Lingo::execute(): Stopping due to debug few frames only");
 			_vm->getCurrentMovie()->getScore()->_playState = kPlayStopped;
@@ -656,6 +674,7 @@ bool Lingo::execute() {
 		}
 
 		g_debugger->stepHook();
+		if (_exec._step > 0) _exec._step--;
 
 		_state->pc++;
 		(*((*_state->script)[_state->pc - 1]))();
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index f1dd15b3217..663e498c8fb 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -337,6 +337,11 @@ struct LingoState {
 	~LingoState();
 };
 
+enum LingoExecState {
+	kRunning,
+	kPause,
+};
+
 class Lingo {
 
 public:
@@ -551,6 +556,15 @@ public:
 	Datum _windowList;
 	Symbol _currentInputEvent;
 
+	struct {
+		LingoExecState _state = kRunning;
+		int _step = -1;
+		struct {
+			uint _stackSize = 0;
+			bool _enabled = false;
+		} _next;
+	} _exec;
+
 public:
 	void executeImmediateScripts(Frame *frame);
 	void executePerFrameHook(int frame, int subframe);




More information about the Scummvm-git-logs mailing list