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

scemino noreply at scummvm.org
Mon May 20 09:48:13 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:
76bf0bd8c7 DIRECTOR: Start syntax highlighting
b9b7788aeb DIRECTOR: Add breakpoint list window


Commit: 76bf0bd8c7f3f0c8af4f2dab634a106207945327
    https://github.com/scummvm/scummvm/commit/76bf0bd8c7f3f0c8af4f2dab634a106207945327
Author: scemino (scemino74 at gmail.com)
Date: 2024-05-20T11:47:38+02:00

Commit Message:
DIRECTOR: Start syntax highlighting

Changed paths:
    engines/director/debugtools.cpp


diff --git a/engines/director/debugtools.cpp b/engines/director/debugtools.cpp
index ac66d688726..2c055b1affc 100644
--- a/engines/director/debugtools.cpp
+++ b/engines/director/debugtools.cpp
@@ -72,38 +72,70 @@ typedef struct ImGuiScript {
 	Common::String handlerId;
 	Common::String handlerName;
 	Common::String moviePath;
-	Common::Array<ImGuiScriptCodeLine> lingoCode;
-	Common::Array<ImGuiScriptCodeLine> byteCode;
 
 	bool operator==(const ImGuiScript &c) const {
-		return moviePath == c.moviePath && score == c.score && id == c.id && type == c.type && handlerId == c.handlerId;
+		return moviePath == c.moviePath && score == c.score && id == c.id && handlerId == c.handlerId;
 	}
 	bool operator!=(const ImGuiScript &c) const {
 		return !(*this == c);
 	}
 } ImGuiScript;
 
-class ImGuiNodeVisitor : public LingoDec::NodeVisitor {
-public:
-	explicit ImGuiNodeVisitor(ImGuiScript &script) : _script(script) {}
+typedef struct ImGuiState {
+	struct {
+		Common::HashMap<Graphics::Surface *, ImGuiImage> _textures;
+		bool _listView = true;
+		int _thumbnailSize = 64;
+		ImGuiTextFilter _nameFilter;
+		int _typeFilter = 0x7FFF;
+	} _cast;
+	struct {
+		ImGuiScript _script;
+		ImGuiTextFilter _nameFilter;
+		bool _showScript = false;
+		bool _showByteCode = false;
+	} _functions;
+	bool _showControlPanel = true;
+	bool _showCallStack = false;
+	bool _showVars = false;
+	bool _showChannels = false;
+	bool _showCast = false;
+	bool _showFuncList = false;
+	bool _showScore = false;
+	Common::List<CastMemberID> _scriptCasts;
+	Common::HashMap<Common::String, bool, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _breakpoints;
+	Common::HashMap<Common::String, bool, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _variables;
+	int _prevFrame = -1;
+	struct {
+		int frame = -1;
+		int channel = -1;
+	} _selectedScoreCast;
+} ImGuiState;
 
-	virtual void visit(const LingoDec::BlockNode &node) override {
-		_indent++;
-		for (const auto &child : node.children) {
-			child->accept(*this);
-		}
-		_indent--;
-	}
+ImGuiState *_state = nullptr;
+
+static void setScriptToDisplay(const ImGuiScript &script);
+
+class RenderScriptVisitor : public LingoDec::NodeVisitor {
+public:
+	explicit RenderScriptVisitor(ImGuiScript &script, bool showByteCode) : _script(script), _showByteCode(showByteCode) {}
 
 	virtual void visit(const LingoDec::HandlerNode &node) override {
-		byteCode(node);
+		_handler = node.handler;
+		LingoDec::Script *script = node.handler->script;
+		if (!script)
+			return;
+
+		if (_showByteCode) {
+			byteCode(node);
+			return;
+		}
 
 		if (node.handler->isGenericEvent) {
 			node.block->accept(*this);
 			return;
 		}
 
-		LingoDec::Script *script = node.handler->script;
 		bool isMethod = script->isFactory();
 		{
 			Common::String code;
@@ -125,41 +157,116 @@ public:
 			write(node._startOffset, code);
 		}
 
-		{
-			Common::String code;
-			if (isMethod && node.handler->script->propertyNames.size() > 0 && node.handler == &node.handler->script->handlers[0]) {
-				code += "instance ";
-				for (size_t i = 0; i < node.handler->script->propertyNames.size(); i++) {
-					if (i > 0)
-						code += ", ";
-					code += node.handler->script->propertyNames[i];
-				}
-				write(node._startOffset, code);
+		if (isMethod && node.handler->script->propertyNames.size() > 0 && node.handler == &node.handler->script->handlers[0]) {
+			write(node._startOffset, "instance");
+			ImGui::SameLine();
+			for (size_t i = 0; i < node.handler->script->propertyNames.size(); i++) {
+				if (i > 0)
+					ImGui::Text(", ");
+				ImGui::SameLine();
+				ImGui::TextColored((ImVec4)ImColor(var_color), "%s", node.handler->script->propertyNames[i].c_str());
+				ImGui::SameLine();
 			}
+			ImGui::NewLine();
 		}
 
-		{
-			if (node.handler->globalNames.size() > 0) {
-				Common::String code;
-				code += "global ";
-				for (size_t i = 0; i < node.handler->globalNames.size(); i++) {
-					if (i > 0)
-						code += ", ";
-					code += node.handler->globalNames[i];
-				}
-				write(node._startOffset, code);
+		if (node.handler->globalNames.size() > 0) {
+			write(node._startOffset, "global ");
+			ImGui::SameLine();
+			for (size_t i = 0; i < node.handler->globalNames.size(); i++) {
+				if (i > 0)
+					ImGui::Text(", ");
+				ImGui::SameLine();
+				ImGui::TextColored((ImVec4)ImColor(var_color), "%s", node.handler->globalNames[i].c_str());
+				ImGui::SameLine();
 			}
+			ImGui::NewLine();
 		}
 
 		node.block->accept(*this);
 
-		{
-			Common::String code;
-			if (!isMethod) {
-				code += "end";
+		if (!isMethod) {
+			write(node.block->_endOffset, "end");
+		}
+	}
+
+	virtual void visit(const LingoDec::LiteralNode &node) override {
+		LingoDec::CodeWriterVisitor code(_dot, false);
+		node.accept(code);
+		ImGui::TextColored(literal_color, "%s", code._str.c_str());
+		ImGui::SameLine();
+	}
+
+	virtual void visit(const LingoDec::ObjCallV4Node &node) override {
+		if (node.isStatement) {
+			renderLine(node._startOffset);
+			renderIndentation();
+		}
+
+		node.obj->accept(*this);
+		ImGui::SameLine();
+		ImGui::Text("(");
+		ImGui::SameLine();
+		node.argList->accept(*this);
+		ImGui::SameLine();
+		ImGui::Text(")");
+		if (!node.isStatement) {
+			ImGui::SameLine();
+		}
+	}
+
+	virtual void visit(const LingoDec::CallNode &node) override {
+		int32 obj = 0;
+		for (int i = 0; i < _handler->bytecodeArray.size(); i++) {
+			if (node._startOffset == _handler->bytecodeArray[i].pos) {
+				obj = _handler->bytecodeArray[i].obj;
+				break;
 			}
-			write(node.block->_endOffset, code);
 		}
+
+		// new line only if it's a statement
+		if (node.isStatement) {
+			renderLine(node._startOffset);
+			renderIndentation();
+		}
+
+		const ImVec4 color = (ImVec4)ImColor(g_lingo->_builtinCmds.contains(node.name) ? builtin_color : call_color);
+		ImGui::TextColored(color, "%s", node.name.c_str());
+		if (!g_lingo->_builtinCmds.contains(node.name) && ImGui::IsItemHovered() && ImGui::BeginTooltip()) {
+			ImGui::Text("Go to definition");
+			ImGui::EndTooltip();
+		}
+		if (!g_lingo->_builtinCmds.contains(node.name) && ImGui::IsItemClicked()) {
+			ImGuiScript script;
+			script.moviePath = _script.moviePath;
+			script.handlerId = node.name;
+			script.id = CastMemberID(obj, _script.id.castLib);
+			script.handlerName = node.name;
+			setScriptToDisplay(script);
+		}
+		ImGui::SameLine();
+
+		LingoDec::CodeWriterVisitor code(_dot, false);
+		if (node.noParens()) {
+			node.argList->accept(code);
+		} else {
+			code.write("(");
+			node.argList->accept(code);
+			code.write(")");
+		}
+
+		ImGui::Text("%s", code._str.c_str());
+		if (!node.isStatement) {
+			ImGui::SameLine();
+		}
+	}
+
+	virtual void visit(const LingoDec::BlockNode &node) override {
+		_indent++;
+		for (const auto &child : node.children) {
+			child->accept(*this);
+		}
+		_indent--;
 	}
 
 	virtual void visit(const LingoDec::RepeatWhileStmtNode &node) override {
@@ -203,11 +310,13 @@ public:
 
 	virtual void visit(const LingoDec::IfStmtNode &node) override {
 		{
-			LingoDec::CodeWriterVisitor code(_dot, false);
-			code.write("if ");
-			node.condition->accept(code);
-			code.write(" then");
-			write(node._startOffset, code._str);
+			renderLine(node._startOffset);
+			renderIndentation();
+			ImGui::Text("if ");
+			ImGui::SameLine();
+			node.condition->accept(*this);
+			ImGui::SameLine();
+			ImGui::Text(" then");
 		}
 		node.block1->accept(*this);
 		if (node.hasElse) {
@@ -229,11 +338,15 @@ public:
 	virtual void defaultVisit(const LingoDec::Node &node) override {
 		LingoDec::CodeWriterVisitor code(_dot, false);
 		node.accept(code);
-		write(node._startOffset, code._str);
+		if (node.isStatement) {
+			renderLine(node._startOffset);
+			renderIndentation();
+		}
+		ImGui::Text("%s", code._str.c_str());
 	}
 
 private:
-	void byteCode(const LingoDec::HandlerNode &node) {
+	void byteCode(const LingoDec::HandlerNode &node) const {
 		LingoDec::Handler *handler = node.handler;
 		bool isMethod = handler->script->isFactory();
 
@@ -304,65 +417,88 @@ private:
 		}
 	}
 
-	void write(uint32 offset, const Common::String &code) {
+	void write(uint32 offset, const Common::String &code) const {
+		renderLine(offset);
+		renderIndentation();
+		ImGui::Text("%s", code.c_str());
+	}
+
+	void writeByteCode(uint32 offset, const Common::String &code) const {
+		renderLine(offset);
 		Common::String s;
 		for (int i = 0; i < _indent; i++) {
 			s += "  ";
 		}
-		_script.lingoCode.push_back({offset, s + code});
+		ImGui::Text("%s", (s + code).c_str());
 	}
 
-	void writeByteCode(uint32 offset, const Common::String &code) {
-		Common::String s;
-		for (int i = 0; i < _indent; i++) {
-			s += "  ";
+	void renderLine(uint pc) const {
+		ImDrawList *dl = ImGui::GetWindowDrawList();
+		ImVec2 pos = ImGui::GetCursorScreenPos();
+		const ImVec2 mid(pos.x + 7, pos.y + 7);
+		Common::String bpName = Common::String::format("%s-%d", _script.handlerId.c_str(), pc);
+
+		ImU32 color = bp_color_disabled;
+
+		if (_state->_breakpoints.contains(bpName))
+			color = bp_color_enabled;
+
+		ImGui::InvisibleButton("Line", ImVec2(16, ImGui::GetFontSize()));
+		if (ImGui::IsItemClicked(0)) {
+			if (color == bp_color_enabled) {
+				_state->_breakpoints.erase(bpName);
+				color = bp_color_disabled;
+			} else {
+				_state->_breakpoints[bpName] = true;
+				color = bp_color_enabled;
+			}
 		}
-		_script.byteCode.push_back({offset, s + code});
+
+		if (color == bp_color_disabled && ImGui::IsItemHovered()) {
+			color = bp_color_hover;
+		}
+
+		dl->AddCircleFilled(mid, 4.0f, color);
+		dl->AddLine(ImVec2(pos.x + 16.0f, pos.y), ImVec2(pos.x + 16.0f, pos.y + 17), line_color);
+
+		ImGui::SetItemTooltip("Click to add a breakpoint");
+
+		ImGui::SameLine();
+		ImGui::Text("[%5d] ", pc);
+		ImGui::SameLine();
+	}
+
+	void renderIndentation(int indent) const {
+		for (int i = 0; i < indent; i++) {
+			ImGui::Text("  ");
+			ImGui::SameLine();
+		}
+	}
+
+	void renderIndentation() const {
+		renderIndentation(_indent);
 	}
 
-	Common::String posToString(int32 pos) {
+	Common::String posToString(int32 pos) const {
 		return Common::String::format("[%3d]", pos);
 	}
 
 private:
 	ImGuiScript &_script;
+	bool _showByteCode = false;
 	bool _dot = false;
 	int _indent = 0;
-};
+	LingoDec::Handler *_handler = nullptr;
 
-typedef struct ImGuiState {
-	struct {
-		Common::HashMap<Graphics::Surface *, ImGuiImage> _textures;
-		bool _listView = true;
-		int _thumbnailSize = 64;
-		ImGuiTextFilter _nameFilter;
-		int _typeFilter = 0x7FFF;
-	} _cast;
-	struct {
-		ImGuiScript _script;
-		ImGuiTextFilter _nameFilter;
-		bool _showScript = false;
-		bool _showByteCode = false;
-	} _functions;
-	bool _showControlPanel = true;
-	bool _showCallStack = false;
-	bool _showVars = false;
-	bool _showChannels = false;
-	bool _showCast = false;
-	bool _showFuncList = false;
-	bool _showScore = false;
-	Common::List<CastMemberID> _scriptCasts;
-	Common::HashMap<Common::String, bool, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _breakpoints;
-	Common::HashMap<Common::String, bool, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _variables;
-	int _prevFrame = -1;
-
-	struct {
-		int frame = -1;
-		int channel = -1;
-	} _selectedScoreCast;
-} ImGuiState;
-
-ImGuiState *_state = nullptr;
+	const ImU32 bp_color_disabled = ImGui::GetColorU32(ImVec4(0.9f, 0.08f, 0.0f, 0.0f));
+	const ImU32 bp_color_enabled = ImGui::GetColorU32(ImVec4(0.9f, 0.08f, 0.0f, 1.0f));
+	const ImU32 bp_color_hover = ImGui::GetColorU32(ImVec4(0.42f, 0.17f, 0.13f, 1.0f));
+	const ImU32 line_color = ImGui::GetColorU32(ImVec4(0.44f, 0.44f, 0.44f, 1.0f));
+	const ImVec4 call_color = ImVec4(0.44f, 0.44f, 0.88f, 1.0f);
+	const ImVec4 builtin_color = ImColor(IM_COL32_WHITE);
+	const ImVec4 var_color = ImColor(IM_COL32_WHITE);
+	const ImVec4 literal_color = ImColor(IM_COL32_WHITE);
+};
 
 static void showControlPanel() {
 	if (!_state->_showControlPanel)
@@ -1183,47 +1319,27 @@ static void renderCastScript(Symbol &sym) {
 }
 
 static void renderScript(ImGuiScript &script, bool showByteCode) {
-	ImDrawList *dl = ImGui::GetWindowDrawList();
-
-	const ImU32 bp_color_disabled = ImGui::GetColorU32(ImVec4(0.9f, 0.08f, 0.0f, 0.0f));
-	const ImU32 bp_color_enabled = ImGui::GetColorU32(ImVec4(0.9f, 0.08f, 0.0f, 1.0f));
-	const ImU32 bp_color_hover = ImGui::GetColorU32(ImVec4(0.42f, 0.17f, 0.13f, 1.0f));
-	const ImU32 line_color = ImGui::GetColorU32(ImVec4(0.44f, 0.44f, 0.44f, 1.0f));
-	ImU32 color;
-
-	const Common::Array<ImGuiScriptCodeLine> &code = showByteCode ? script.byteCode : script.lingoCode;
-	for (const auto& line : code) {
-		ImVec2 pos = ImGui::GetCursorScreenPos();
-		const ImVec2 mid(pos.x + 7, pos.y + 7);
-		Common::String bpName = Common::String::format("%s-%d", script.handlerId.c_str(), line.pc);
-
-		color = bp_color_disabled;
-
-		if (_state->_breakpoints.contains(bpName))
-			color = bp_color_enabled;
+	Director::Movie *movie = g_director->getCurrentMovie();
+	const Common::String &moviePath = movie->getArchive()->getPathName().toString();
+	if (moviePath != script.moviePath)
+		return;
 
-		ImGui::InvisibleButton("Line", ImVec2(16, ImGui::GetFontSize()));
-		if (ImGui::IsItemClicked(0)) {
-			if (color == bp_color_enabled) {
-				_state->_breakpoints.erase(bpName);
-				color = bp_color_disabled;
-			} else {
-				_state->_breakpoints[bpName] = true;
-				color = bp_color_enabled;
-			}
-		}
+	const Director::Cast *cast = movie->getCast(script.id);
+	if (!cast->_lingodec)
+		return;
 
-		if (color == bp_color_disabled && ImGui::IsItemHovered()) {
-			color = bp_color_hover;
+	Common::SharedPtr<LingoDec::Node> node;
+	for (auto it : cast->_lingodec->scripts) {
+		for (const LingoDec::Handler &h : it.second->handlers) {
+			if (h.name != script.handlerId)
+				continue;
+			node = h.ast.root;
 		}
+	}
 
-		dl->AddCircleFilled(mid, 4.0f, color);
-		dl->AddLine(ImVec2(pos.x + 16.0f, pos.y), ImVec2(pos.x + 16.0f, pos.y + 17), line_color);
-
-		ImGui::SetItemTooltip("Click to add a breakpoint");
-
-		ImGui::SameLine();
-		ImGui::Text("[%5d] %s", line.pc, line.codeLine.c_str());
+	if (node) {
+		RenderScriptVisitor visitor(script, showByteCode);
+		node->accept(visitor);
 	}
 }
 
@@ -1344,17 +1460,8 @@ static void showFuncList() {
 						ImGuiScript script;
 						script.moviePath = movie->getArchive()->getPathName().toString();
 						script.score = true;
-						script.type = csc->_scriptType;
 						script.handlerId = functionHandler._key;
 						script.handlerName = getHandlerName(functionHandler._value);
-						uint32 scriptId = movie->getCast()->getCastMemberInfo(csc->_id)->scriptId;
-						const LingoDec::Script *s = movie->getCast()->_lingodec->scripts[scriptId];
-						ImGuiNodeVisitor visitor(script);
-						for (auto &h : s->handlers) {
-							if (h.name != functionHandler._key)
-								continue;
-							h.ast.root->accept(visitor);
-						}
 						setScriptToDisplay(script);
 					}
 					ImGui::TableNextColumn();
@@ -1389,17 +1496,8 @@ static void showFuncList() {
 								ImGuiScript script;
 								script.moviePath = movie->getArchive()->getPathName().toString();
 								script.id = memberID;
-								script.type = (ScriptType)i;
 								script.handlerId = functionHandler._key;
 								script.handlerName = getHandlerName(functionHandler._value);
-								uint32 scriptId = movie->getCastMemberInfo(memberID)->scriptId;
-								const LingoDec::Script *s = cast._value->_lingodec->scripts[scriptId];
-								ImGuiNodeVisitor visitor(script);
-								for (auto &h : s->handlers) {
-									if (h.name != functionHandler._key)
-										continue;
-									h.ast.root->accept(visitor);
-								}
 								setScriptToDisplay(script);
 							}
 							ImGui::TableNextColumn();
@@ -1437,17 +1535,8 @@ static void showFuncList() {
 								ImGuiScript script;
 								script.moviePath = movie->getArchive()->getPathName().toString();
 								script.id = memberID;
-								script.type = (ScriptType)i;
 								script.handlerId = functionHandler._key;
 								script.handlerName = getHandlerName(functionHandler._value);
-								uint32 scriptId = sharedCast->getCastMemberInfo(scriptContext._key)->scriptId;
-								const LingoDec::Script *s = sharedCast->_lingodec->scripts[scriptId];
-								ImGuiNodeVisitor visitor(script);
-								for (auto &h : s->handlers) {
-									if (h.name != functionHandler._key)
-										continue;
-									h.ast.root->accept(visitor);
-								}
 								setScriptToDisplay(script);
 							}
 							ImGui::TableNextColumn();


Commit: b9b7788aeb2abc8b38611099ff096bf9c2478a83
    https://github.com/scummvm/scummvm/commit/b9b7788aeb2abc8b38611099ff096bf9c2478a83
Author: scemino (scemino74 at gmail.com)
Date: 2024-05-20T11:47:38+02:00

Commit Message:
DIRECTOR: Add breakpoint list window

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


diff --git a/engines/director/debugger.cpp b/engines/director/debugger.cpp
index f04c3bd6976..5ef1d5ddc4b 100644
--- a/engines/director/debugger.cpp
+++ b/engines/director/debugger.cpp
@@ -121,18 +121,6 @@ Debugger::Debugger(): GUI::Debugger() {
 	_nextCounter = 0;
 	_lingoEval = false;
 	_lingoReplMode = false;
-	_bpNextId = 1;
-	_bpCheckFunc = false;
-	_bpCheckMoviePath = false;
-	_bpNextMovieMatch = false;
-	_bpMatchScriptId = 0;
-	_bpCheckPropRead = false;
-	_bpCheckPropWrite = false;
-	_bpCheckVarRead = false;
-	_bpCheckVarWrite = false;
-	_bpCheckEntityRead = false;
-	_bpCheckEntityWrite = false;
-	_bpCheckEvent = false;
 }
 
 Debugger::~Debugger() {
@@ -207,7 +195,7 @@ bool Debugger::cmdHelp(int argc, const char **argv) {
 	return true;
 }
 
-Common::String Breakpoint::format() {
+Common::String Breakpoint::format() const {
 	Common::String result = Common::String::format("Breakpoint %d, ", id);
 	switch (type) {
 	case kBreakpointFunction:
@@ -660,7 +648,6 @@ bool Debugger::cmdFinish(int argc, const char **argv) {
 
 bool Debugger::cmdBpSet(int argc, const char **argv) {
 	Breakpoint bp;
-	bp.id = _bpNextId;
 	bp.type = kBreakpointFunction;
 	if (argc == 1) {
 		Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;
@@ -720,21 +707,18 @@ bool Debugger::cmdBpSet(int argc, const char **argv) {
 		debugPrintf("Too many arguments.\n");
 		return true;
 	}
-	_breakpoints.push_back(bp);
+	g_lingo->addBreakpoint(bp);
 	bpUpdateState();
 	debugPrintf("Added %s\n", bp.format().c_str());
-	_bpNextId++;
 	return true;
 }
 
 bool Debugger::cmdBpMovie(int argc, const char **argv) {
 	if (argc == 2) {
 		Breakpoint bp;
-		bp.id = _bpNextId;
-		_bpNextId++;
 		bp.type = kBreakpointMovie;
 		bp.moviePath = argv[1];
-		_breakpoints.push_back(bp);
+		g_lingo->addBreakpoint(bp);
 		bpUpdateState();
 		debugPrintf("Added %s\n", bp.format().c_str());
 	} else {
@@ -780,9 +764,7 @@ bool Debugger::cmdBpEntity(int argc, const char **argv) {
 			bp.varRead = true;
 			bp.varWrite = true;
 		}
-		bp.id = _bpNextId;
-		_bpNextId++;
-		_breakpoints.push_back(bp);
+		g_lingo->addBreakpoint(bp);
 		bpUpdateState();
 		debugPrintf("Added %s\n", bp.format().c_str());
 	} else {
@@ -808,9 +790,7 @@ bool Debugger::cmdBpProp(int argc, const char **argv) {
 			bp.varRead = true;
 			bp.varWrite = true;
 		}
-		bp.id = _bpNextId;
-		_bpNextId++;
-		_breakpoints.push_back(bp);
+		g_lingo->addBreakpoint(bp);
 		bpUpdateState();
 		debugPrintf("Added %s\n", bp.format().c_str());
 	} else {
@@ -836,9 +816,7 @@ bool Debugger::cmdBpVar(int argc, const char **argv) {
 			bp.varRead = true;
 			bp.varWrite = true;
 		}
-		bp.id = _bpNextId;
-		_bpNextId++;
-		_breakpoints.push_back(bp);
+		g_lingo->addBreakpoint(bp);
 		bpUpdateState();
 		debugPrintf("Added %s\n", bp.format().c_str());
 	} else {
@@ -861,9 +839,7 @@ bool Debugger::cmdBpEvent(int argc, const char **argv) {
 			debugPrintf("Event %s not found.\n", argv[1]);
 			return true;
 		}
-		bp.id = _bpNextId;
-		_bpNextId++;
-		_breakpoints.push_back(bp);
+		g_lingo->addBreakpoint(bp);
 		bpUpdateState();
 		debugPrintf("Added %s\n", bp.format().c_str());
 	} else {
@@ -880,8 +856,6 @@ bool Debugger::cmdBpFrame(int argc, const char **argv) {
 	Movie *movie = g_director->getCurrentMovie();
 	if (argc == 2 || argc == 3) {
 		Breakpoint bp;
-		bp.id = _bpNextId;
-		_bpNextId++;
 		bp.type = kBreakpointMovieFrame;
 		Common::String target(argv[1]);
 		if (argc == 3) {
@@ -895,7 +869,7 @@ bool Debugger::cmdBpFrame(int argc, const char **argv) {
 			debugPrintf("Must specify a valid frame ID.\n");
 			return true;
 		}
-		_breakpoints.push_back(bp);
+		g_lingo->addBreakpoint(bp);
 		bpUpdateState();
 		debugPrintf("Added %s\n", bp.format().c_str());
 	} else {
@@ -906,18 +880,12 @@ bool Debugger::cmdBpFrame(int argc, const char **argv) {
 
 bool Debugger::cmdBpDel(int argc, const char **argv) {
 	if (argc == 2 && atoi(argv[1]) > 0) {
-		bool found = false;
-		for (auto it = _breakpoints.begin(); it != _breakpoints.end(); ++it) {
-			if (it->id == atoi(argv[1])) {
-				it = _breakpoints.erase(it);
-				found = true;
-				bpUpdateState();
-				debugPrintf("Deleted breakpoint %s.\n", argv[1]);
-				break;
-			}
-		}
-		if (!found)
+		if (g_lingo->delBreakpoint(atoi(argv[1]))) {
+			debugPrintf("Deleted breakpoint %s.\n", argv[1]);
+		} else {
 			debugPrintf("No breakpoint with ID %s.\n", argv[1]);
+		}
+		bpUpdateState();
 	} else {
 		debugPrintf("Must specify a breakpoint ID.\n");
 	}
@@ -926,17 +894,12 @@ bool Debugger::cmdBpDel(int argc, const char **argv) {
 
 bool Debugger::cmdBpEnable(int argc, const char **argv) {
 	if (argc == 2 && atoi(argv[1]) > 0) {
-		bool found = false;
-		for (auto &it : _breakpoints) {
-			if (it.id == atoi(argv[1])) {
-				it.enabled = true;
-				found = true;
-				bpUpdateState();
-				debugPrintf("Enabled breakpoint %s.\n", argv[1]);
-				break;
-			}
-		}
-		if (!found)
+		Breakpoint *bp = g_lingo->getBreakpoint(atoi(argv[1]));
+		if (bp) {
+			bp->enabled = true;
+			bpUpdateState();
+			debugPrintf("Enabled breakpoint %s.\n", argv[1]);
+		} else
 			debugPrintf("No breakpoint with ID %s.\n", argv[1]);
 	} else {
 		debugPrintf("Must specify a breakpoint ID.\n");
@@ -946,17 +909,12 @@ bool Debugger::cmdBpEnable(int argc, const char **argv) {
 
 bool Debugger::cmdBpDisable(int argc, const char **argv) {
 	if (argc == 2 && atoi(argv[1]) > 0) {
-		bool found = false;
-		for (auto &it : _breakpoints) {
-			if (it.id == atoi(argv[1])) {
-				it.enabled = false;
-				found = true;
-				bpUpdateState();
-				debugPrintf("Disabled breakpoint %s.\n", argv[1]);
-				break;
-			}
-		}
-		if (!found)
+		Breakpoint *bp = g_lingo->getBreakpoint(atoi(argv[1]));
+		if (bp) {
+			bp->enabled = false;
+			bpUpdateState();
+			debugPrintf("Disabled breakpoint %s.\n", argv[1]);
+		} else
 			debugPrintf("No breakpoint with ID %s.\n", argv[1]);
 	} else {
 		debugPrintf("Must specify a breakpoint ID.\n");
@@ -965,8 +923,9 @@ bool Debugger::cmdBpDisable(int argc, const char **argv) {
 }
 
 bool Debugger::cmdBpList(int argc, const char **argv) {
-	if (_breakpoints.size()) {
-		for (auto &it : _breakpoints) {
+	const Common::Array<Breakpoint> &bps = g_lingo->getBreakpoints();
+	if (bps.size()) {
+		for (auto &it : bps) {
 			debugPrintf("%s (%s)\n", it.format().c_str(), it.enabled ? "enabled" : "disabled");
 		}
 	} else {
@@ -1057,7 +1016,7 @@ void Debugger::bpUpdateState() {
 	_bpCheckEvent = false;
 	Movie *movie = g_director->getCurrentMovie();
 	Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;
-	for (auto &it : _breakpoints) {
+	for (auto &it : g_lingo->getBreakpoints()) {
 		if (!it.enabled)
 			continue;
 		if (it.type == kBreakpointFunction) {
@@ -1129,7 +1088,7 @@ void Debugger::bpTest(bool forceCheck) {
 	// Print the breakpoints that matched
 	if (stop) {
 		debugPrintf("Hit a breakpoint:\n");
-		for (auto &it : _breakpoints) {
+		for (auto &it : g_lingo->getBreakpoints()) {
 			if (!it.enabled)
 				continue;
 			if (it.type == kBreakpointFunction) {
@@ -1237,7 +1196,7 @@ void Debugger::movieHook() {
 
 void Debugger::eventHook(LEvent eventId) {
 	if (_bpCheckEvent) {
-		for (auto &it : _breakpoints) {
+		for (auto &it : g_lingo->getBreakpoints()) {
 			if (it.type == kBreakpointEvent && eventId == it.eventId) {
 				debugPrintf("Hit a breakpoint:\n");
 				debugPrintf("%s\n", it.format().c_str());
@@ -1272,7 +1231,7 @@ void Debugger::builtinHook(const Symbol &funcSym) {
 	bpUpdateState();
 	bool builtinMatch = false;
 	if (_bpCheckFunc) {
-		for (auto &it : _breakpoints) {
+		for (auto &it : g_lingo->getBreakpoints()) {
 			if (it.type != kBreakpointFunction)
 				continue;
 			if (it.funcName.equalsIgnoreCase(*funcSym.name)) {
@@ -1288,7 +1247,7 @@ void Debugger::propReadHook(const Common::String &name) {
 	if (name.empty())
 		return;
 	if (_bpCheckPropRead) {
-		for (auto &it : _breakpoints) {
+		for (auto &it : g_lingo->getBreakpoints()) {
 			if (it.type == kBreakpointProperty && it.varRead && it.varName.equalsIgnoreCase(name)) {
 				debugPrintf("Hit a breakpoint:\n");
 				debugPrintf("%s\n", it.format().c_str());
@@ -1305,7 +1264,7 @@ void Debugger::propWriteHook(const Common::String &name) {
 	if (name.empty())
 		return;
 	if (_bpCheckPropWrite) {
-		for (auto &it : _breakpoints) {
+		for (auto &it : g_lingo->getBreakpoints()) {
 			if (it.type == kBreakpointProperty && it.varWrite && it.varName.equalsIgnoreCase(name)) {
 				debugPrintf("Hit a breakpoint:\n");
 				debugPrintf("%s\n", it.format().c_str());
@@ -1322,7 +1281,7 @@ void Debugger::varReadHook(const Common::String &name) {
 	if (name.empty())
 		return;
 	if (_bpCheckVarRead) {
-		for (auto &it : _breakpoints) {
+		for (auto &it : g_lingo->getBreakpoints()) {
 			if (it.type == kBreakpointVariable && it.varRead && it.varName.equalsIgnoreCase(name)) {
 				debugPrintf("Hit a breakpoint:\n");
 				debugPrintf("%s\n", it.format().c_str());
@@ -1339,7 +1298,7 @@ void Debugger::varWriteHook(const Common::String &name) {
 	if (name.empty())
 		return;
 	if (_bpCheckVarWrite) {
-		for (auto &it : _breakpoints) {
+		for (auto &it : g_lingo->getBreakpoints()) {
 			if (it.type == kBreakpointVariable && it.varWrite && it.varName.equalsIgnoreCase(name)) {
 				debugPrintf("Hit a breakpoint:\n");
 				debugPrintf("%s\n", it.format().c_str());
@@ -1354,7 +1313,7 @@ void Debugger::varWriteHook(const Common::String &name) {
 
 void Debugger::entityReadHook(int entity, int field) {
 	if (_bpCheckEntityRead) {
-		for (auto &it : _breakpoints) {
+		for (auto &it : g_lingo->getBreakpoints()) {
 			if (it.type == kBreakpointEntity && it.varRead && it.entity == entity && it.field == field) {
 				debugPrintf("Hit a breakpoint:\n");
 				debugPrintf("%s\n", it.format().c_str());
@@ -1369,7 +1328,7 @@ void Debugger::entityReadHook(int entity, int field) {
 
 void Debugger::entityWriteHook(int entity, int field) {
 	if (_bpCheckEntityWrite) {
-		for (auto &it : _breakpoints) {
+		for (auto &it : g_lingo->getBreakpoints()) {
 			if (it.type == kBreakpointEntity && it.varWrite && it.entity == entity && it.field == field) {
 				debugPrintf("Hit a breakpoint:\n");
 				debugPrintf("%s\n", it.format().c_str());
diff --git a/engines/director/debugger.h b/engines/director/debugger.h
index 1d0771075fe..4b8dd42b1fb 100644
--- a/engines/director/debugger.h
+++ b/engines/director/debugger.h
@@ -59,7 +59,7 @@ struct Breakpoint {
 	bool varRead = false;
 	bool varWrite = false;
 
-	Common::String format();
+	Common::String format() const;
 };
 
 
@@ -144,23 +144,21 @@ private:
 	bool _lingoEval;
 	bool _lingoReplMode;
 
-	Common::Array<Breakpoint> _breakpoints;
-	int _bpNextId;
-	bool _bpCheckFunc;
-	bool _bpCheckMoviePath;
-	bool _bpNextMovieMatch;
+	bool _bpCheckFunc = false;
+	bool _bpCheckMoviePath = false;
+	bool _bpNextMovieMatch = false;
 	Common::String _bpMatchFuncName;
-	uint _bpMatchScriptId;
+	uint _bpMatchScriptId = 0;
 	Common::String _bpMatchMoviePath;
 	Common::HashMap<uint, void *> _bpMatchFuncOffsets;
 	Common::HashMap<uint, void *> _bpMatchFrameOffsets;
-	bool _bpCheckPropRead;
-	bool _bpCheckPropWrite;
-	bool _bpCheckVarRead;
-	bool _bpCheckVarWrite;
-	bool _bpCheckEntityRead;
-	bool _bpCheckEntityWrite;
-	bool _bpCheckEvent;
+	bool _bpCheckPropRead = false;
+	bool _bpCheckPropWrite = false;
+	bool _bpCheckVarRead = false;
+	bool _bpCheckVarWrite = false;
+	bool _bpCheckEntityRead = false;
+	bool _bpCheckEntityWrite = false;
+	bool _bpCheckEvent = false;
 };
 
 
diff --git a/engines/director/debugtools.cpp b/engines/director/debugtools.cpp
index 2c055b1affc..ea46486df52 100644
--- a/engines/director/debugtools.cpp
+++ b/engines/director/debugtools.cpp
@@ -45,6 +45,7 @@
 #include "director/castmember/script.h"
 #include "director/channel.h"
 #include "director/debugtools.h"
+#include "director/debugger.h"
 #include "director/frame.h"
 #include "director/movie.h"
 #include "director/picture.h"
@@ -102,8 +103,8 @@ typedef struct ImGuiState {
 	bool _showCast = false;
 	bool _showFuncList = false;
 	bool _showScore = false;
+	bool _showBpList = false;
 	Common::List<CastMemberID> _scriptCasts;
-	Common::HashMap<Common::String, bool, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _breakpoints;
 	Common::HashMap<Common::String, bool, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _variables;
 	int _prevFrame = -1;
 	struct {
@@ -116,6 +117,16 @@ ImGuiState *_state = nullptr;
 
 static void setScriptToDisplay(const ImGuiScript &script);
 
+static Director::Breakpoint *getBreakpoint(const Common::String &handlerName, int pc) {
+	auto &bps = g_lingo->getBreakpoints();
+	for (uint i = 0; i < bps.size(); i++) {
+		if (bps[i].type == kBreakpointFunction && bps[i].funcName == handlerName && bps[i].funcOffset == pc) {
+			return &bps[i];
+		}
+	}
+	return nullptr;
+}
+
 class RenderScriptVisitor : public LingoDec::NodeVisitor {
 public:
 	explicit RenderScriptVisitor(ImGuiScript &script, bool showByteCode) : _script(script), _showByteCode(showByteCode) {}
@@ -436,20 +447,24 @@ private:
 		ImDrawList *dl = ImGui::GetWindowDrawList();
 		ImVec2 pos = ImGui::GetCursorScreenPos();
 		const ImVec2 mid(pos.x + 7, pos.y + 7);
-		Common::String bpName = Common::String::format("%s-%d", _script.handlerId.c_str(), pc);
 
 		ImU32 color = bp_color_disabled;
 
-		if (_state->_breakpoints.contains(bpName))
+		Director::Breakpoint *bp = getBreakpoint(_script.handlerName, pc);
+		if (bp)
 			color = bp_color_enabled;
 
 		ImGui::InvisibleButton("Line", ImVec2(16, ImGui::GetFontSize()));
 		if (ImGui::IsItemClicked(0)) {
 			if (color == bp_color_enabled) {
-				_state->_breakpoints.erase(bpName);
+				g_lingo->delBreakpoint(bp->id);
 				color = bp_color_disabled;
 			} else {
-				_state->_breakpoints[bpName] = true;
+				Director::Breakpoint newBp;
+				newBp.type = kBreakpointFunction;
+				newBp.funcName = _script.handlerName;
+				newBp.funcOffset = pc;
+				g_lingo->addBreakpoint(newBp);
 				color = bp_color_enabled;
 			}
 		}
@@ -458,7 +473,10 @@ private:
 			color = bp_color_hover;
 		}
 
-		dl->AddCircleFilled(mid, 4.0f, color);
+		if(!bp || bp->enabled)
+			dl->AddCircleFilled(mid, 4.0f, color);
+		else
+			dl->AddCircle(mid, 4.0f, line_color);
 		dl->AddLine(ImVec2(pos.x + 16.0f, pos.y), ImVec2(pos.x + 16.0f, pos.y + 17), line_color);
 
 		ImGui::SetItemTooltip("Click to add a breakpoint");
@@ -1288,16 +1306,21 @@ static void renderCastScript(Symbol &sym) {
 
 		color = bp_color_disabled;
 
-		if (_state->_breakpoints.contains(bpName))
+		Director::Breakpoint *bp = getBreakpoint(handlerName, pc);
+		if (bp)
 			color = bp_color_enabled;
 
 		ImGui::InvisibleButton("Line", ImVec2(16, ImGui::GetFontSize()));
 		if (ImGui::IsItemClicked(0)) {
-			if (color == bp_color_enabled) {
-				_state->_breakpoints.erase(bpName);
+			if (bp) {
+				g_lingo->delBreakpoint(bp->id);
 				color = bp_color_disabled;
 			} else {
-				_state->_breakpoints[bpName] = true;
+				Director::Breakpoint newBp;
+				newBp.type = kBreakpointFunction;
+				newBp.funcName = handlerName;
+				newBp.funcOffset = pc;
+				g_lingo->addBreakpoint(newBp);
 				color = bp_color_enabled;
 			}
 		}
@@ -1556,6 +1579,112 @@ static void showFuncList() {
 	ImGui::End();
 }
 
+// Make the UI compact because there are so many fields
+static void PushStyleCompact()
+{
+    ImGuiStyle& style = ImGui::GetStyle();
+    ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(style.FramePadding.x, (float)(int)(style.FramePadding.y * 0.60f)));
+    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x, (float)(int)(style.ItemSpacing.y * 0.60f)));
+}
+
+static void PopStyleCompact()
+{
+    ImGui::PopStyleVar(2);
+}
+
+static void showBreakpointList() {
+	if (!_state->_showBpList)
+		return;
+
+	const ImU32 bp_color_disabled = ImGui::GetColorU32(ImVec4(0.9f, 0.08f, 0.0f, 0.0f));
+	const ImU32 bp_color_enabled = ImGui::GetColorU32(ImVec4(0.9f, 0.08f, 0.0f, 1.0f));
+	const ImU32 bp_color_hover = ImGui::GetColorU32(ImVec4(0.42f, 0.17f, 0.13f, 1.0f));
+	const ImU32 line_color = ImGui::GetColorU32(ImVec4(0.44f, 0.44f, 0.44f, 1.0f));
+
+	ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver);
+	ImGui::SetNextWindowSize(ImVec2(480, 240), ImGuiCond_FirstUseEver);
+	if (ImGui::Begin("Breakpoints", &_state->_showBpList)) {
+		auto &bps = g_lingo->getBreakpoints();
+		if (ImGui::BeginTable("BreakpointsTable", 5, ImGuiTableFlags_SizingFixedFit)) {
+			for (uint i = 0; i < bps.size(); i++) {
+				ImGui::TableNextRow();
+				ImGui::TableNextColumn();
+
+				ImDrawList *dl = ImGui::GetWindowDrawList();
+				ImVec2 pos = ImGui::GetCursorScreenPos();
+				const ImVec2 mid(pos.x + 7, pos.y + 7);
+
+				ImU32 color = bp_color_disabled;
+
+				if (bps[i].enabled)
+					color = bp_color_enabled;
+
+				ImGui::InvisibleButton("Line", ImVec2(16, ImGui::GetFontSize()));
+				if (ImGui::IsItemClicked(0)) {
+					if (bps[i].enabled) {
+						bps[i].enabled = false;
+						color = bp_color_disabled;
+					} else {
+						bps[i].enabled = true;
+						color = bp_color_enabled;
+					}
+				}
+
+				if (color == bp_color_disabled && ImGui::IsItemHovered()) {
+					color = bp_color_hover;
+				}
+
+				if (bps[i].enabled)
+					dl->AddCircleFilled(mid, 4.0f, color);
+				else
+					dl->AddCircle(mid, 4.0f, line_color);
+
+				// enabled column
+				ImGui::TableNextColumn();
+				PushStyleCompact();
+				ImGui::PushID(i);
+				ImGui::Checkbox("", &bps[i].enabled);
+				PopStyleCompact();
+
+				// description
+				ImGui::TableNextColumn();
+				Common::String desc;
+				if (bps[i].scriptId)
+					desc = Common::String::format("%d: %s", bps[i].scriptId, bps[i].funcName.c_str());
+				else
+					desc = bps[i].funcName;
+				ImGui::Text("%s", desc.c_str());
+
+				// remove bp
+				ImGui::TableNextColumn();
+				pos = ImGui::GetCursorScreenPos();
+				const bool del = ImGui::InvisibleButton("DelBp", ImVec2(16, ImGui::GetFontSize()));
+				const bool hovered = ImGui::IsItemHovered();
+				const float fontSize = ImGui::GetFontSize();
+				const float cross_extent = ImGui::GetFontSize() * 0.5f * 0.7071f - 1.0f;
+				const ImU32 cross_col = ImGui::GetColorU32(ImGuiCol_Text);
+				const ImVec2 center = pos + ImVec2(0.5f + fontSize * 0.5f, 1.0f + fontSize * 0.5f);
+				if (hovered)
+        			dl->AddCircleFilled(center, MAX(2.0f, fontSize * 0.5f + 1.0f), ImGui::GetColorU32(ImGuiCol_ButtonActive));
+				dl->AddLine(center + ImVec2(+cross_extent, +cross_extent), center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f);
+				dl->AddLine(center + ImVec2(+cross_extent, -cross_extent), center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f);
+
+				// offset
+				ImGui::TableNextColumn();
+				ImGui::Text("%5d", bps[i].funcOffset);
+				ImGui::PopID();
+
+				if(del) {
+					g_lingo->delBreakpoint(bps[i].id);
+					break;
+				}
+			}
+			ImGui::EndTable();
+		}
+	}
+	ImGui::End();
+}
+
 static void showScore() {
 	if (!_state->_showScore)
 		return;
@@ -1812,6 +1941,7 @@ void onImGuiRender() {
 			ImGui::MenuItem("Channels", NULL, &_state->_showChannels);
 			ImGui::MenuItem("Cast", NULL, &_state->_showCast);
 			ImGui::MenuItem("Functions", NULL, &_state->_showFuncList);
+			ImGui::MenuItem("Breakpoints", NULL, &_state->_showBpList);
 			ImGui::MenuItem("Score", NULL, &_state->_showScore);
 			ImGui::EndMenu();
 		}
@@ -1828,6 +1958,7 @@ void onImGuiRender() {
 	showCast();
 	showFuncList();
 	showScore();
+	showBreakpointList();
 }
 
 void onImGuiCleanup() {
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index dc1ec563c00..ef9d027ace0 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -1864,6 +1864,31 @@ void Lingo::exposeXObject(const char *name, Datum obj) {
 	_globalvars[name].ignoreGlobal = true;
 }
 
+void Lingo::addBreakpoint(Breakpoint &bp) {
+	bp.id = _bpNextId;
+	_breakpoints.push_back(bp);
+	_bpNextId++;
+}
+
+bool Lingo::delBreakpoint(int id) {
+	for (auto it = _breakpoints.begin(); it != _breakpoints.end(); ++it) {
+		if (it->id == id) {
+			it = _breakpoints.erase(it);
+			return true;
+		}
+	}
+	return false;
+}
+
+Breakpoint *Lingo::getBreakpoint(int id) {
+	for (auto &it : _breakpoints) {
+		if (it.id == id) {
+			return ⁢
+		}
+	}
+	return nullptr;
+}
+
 PictureReference::~PictureReference() {
 	delete _picture;
 }
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index ffe25efc3b3..f1dd15b3217 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -47,6 +47,7 @@ class ScriptContext;
 class DirectorEngine;
 class Frame;
 class LingoCompiler;
+struct Breakpoint;
 
 typedef void (*inst)(void);
 #define	STOP (inst)0
@@ -561,6 +562,18 @@ private:
 
 public:
 	Common::String normalizeString(const Common::String &str);
+
+public:
+	void addBreakpoint(Breakpoint &bp);
+	bool delBreakpoint(int id);
+	Breakpoint *getBreakpoint(int id);
+
+	const Common::Array<Breakpoint> &getBreakpoints() const { return _breakpoints; }
+	Common::Array<Breakpoint> &getBreakpoints() { return _breakpoints; }
+
+private:
+	int _bpNextId = 1;
+	Common::Array<Breakpoint> _breakpoints;
 };
 
 extern Lingo *g_lingo;




More information about the Scummvm-git-logs mailing list