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

sev- noreply at scummvm.org
Wed Jun 3 21:12:28 UTC 2026


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

Summary:
922426f320 DIRECTOR: DT: Cast window now loads shared cast
0ea5066c06 DIRECTOR: DT: fix crash and add script viewing from cast details
d33776efd2 DIRECTOR: DT: fix null dereferences, OOB accesses, and memory leaks


Commit: 922426f320ab585c04a890dd0869757cb3bc9818
    https://github.com/scummvm/scummvm/commit/922426f320ab585c04a890dd0869757cb3bc9818
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-03T23:12:22+02:00

Commit Message:
DIRECTOR: DT: Cast window now loads shared cast

Changed paths:
    engines/director/debugger/dt-cast.cpp


diff --git a/engines/director/debugger/dt-cast.cpp b/engines/director/debugger/dt-cast.cpp
index 9994701e4cd..029bb37b033 100644
--- a/engines/director/debugger/dt-cast.cpp
+++ b/engines/director/debugger/dt-cast.cpp
@@ -109,6 +109,93 @@ Common::String getDisplayName(CastMember *castMember) {
 	return Common::String::format("%u", castMember->getID());
 }
 
+void drawCastRow(Cast* cast) {
+	assert(cast);
+	for (auto castMember : *cast->_loadedCast) {
+		castMember._value->load();
+
+		Common::String name(getDisplayName(castMember._value));
+		if (!_state->_cast._nameFilter.PassFilter(name.c_str()))
+			continue;
+		if ((castMember._value->_type != kCastTypeAny) &&
+				!(_state->_cast._typeFilter & (1 << (int)castMember._value->_type)))
+			continue;
+
+		ImGui::TableNextRow();
+
+		// Make the entire row selectable/clickable
+		ImGui::TableSetColumnIndex(0);
+		if (ImGui::Selectable(
+			Common::String::format("##row%d", castMember._key).c_str(),
+			false,
+			ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap,
+			ImVec2(0, 32.f) // match row height
+		)) {
+			castMember._value->load();
+			_state->_castDetails._castMember = castMember._value;
+			_state->_w.castDetails = true;
+		}
+		ImGui::SameLine();
+
+		ImGui::Text("%s %s", toIcon(castMember._value->_type), name.c_str());
+
+		ImGui::TableNextColumn();
+		ImGui::Text("%d", castMember._key);
+
+		ImGui::TableNextColumn();
+		if (castMember._value->_type == CastType::kCastLingoScript) {
+			ScriptCastMember *scriptMember = (ScriptCastMember *)castMember._value;
+			ImGui::Text("%s", toString(scriptMember->_scriptType));
+		}
+		ImGui::TableNextColumn();
+		ImGui::Text("%s", toString(castMember._value->_type));
+
+		ImGui::TableNextColumn();
+		float columnWidth = ImGui::GetColumnWidth();
+
+		ImGuiImage imgID = {};
+		switch (castMember._value->_type) {
+		case kCastBitmap:
+			{
+				imgID = getImageID(castMember._value);
+				if (imgID.id) {
+					float offsetX = (columnWidth - 32.f) * 0.5f;
+					ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
+					showImage(imgID, name.c_str(), 32.f);
+				}
+			}
+			break;
+
+		case kCastText:
+		case kCastRichText:
+		case kCastButton:
+			{
+				imgID = getTextID(castMember._value);
+				if (imgID.id) {
+					float offsetX = (columnWidth - 32.f) * 0.5f;
+					ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
+					showImage(imgID, name.c_str(), 32.f);
+				}
+			}
+			break;
+
+		case kCastShape:
+			{
+				imgID = getShapeID(castMember._value);
+				if (imgID.id) {
+					float offsetX = (columnWidth - 32.f) * 0.5f;
+					ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
+					showImage(imgID, name.c_str(), 32.f);
+				}
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+}
+
 void showCast() {
 	if (!_state->_w.cast)
 		return;
@@ -164,89 +251,13 @@ void showCast() {
 					Cast *cast = it._value;
 					if (!cast->_loadedCast)
 						continue;
+					drawCastRow(cast);
+				}
 
-					for (auto castMember : *cast->_loadedCast) {
-						castMember._value->load();
-
-						Common::String name(getDisplayName(castMember._value));
-						if (!_state->_cast._nameFilter.PassFilter(name.c_str()))
-							continue;
-						if ((castMember._value->_type != kCastTypeAny) && !(_state->_cast._typeFilter & (1 << (int)castMember._value->_type)))
-							continue;
-
-						ImGui::TableNextRow();
-
-						// Make the entire row selectable/clickable
-						ImGui::TableSetColumnIndex(0);
-						if (ImGui::Selectable(
-							Common::String::format("##row%d", castMember._key).c_str(),
-							false,
-							ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap,
-							ImVec2(0, 32.f) // match row height
-						)) {
-							castMember._value->load();
-							_state->_castDetails._castMember = castMember._value;
-							_state->_w.castDetails = true;
-						}
-						ImGui::SameLine();
-
-						ImGui::Text("%s %s", toIcon(castMember._value->_type), name.c_str());
-
-						ImGui::TableNextColumn();
-						ImGui::Text("%d", castMember._key);
-
-						ImGui::TableNextColumn();
-						if (castMember._value->_type == CastType::kCastLingoScript) {
-							ScriptCastMember *scriptMember = (ScriptCastMember *)castMember._value;
-							ImGui::Text("%s", toString(scriptMember->_scriptType));
-						}
-						ImGui::TableNextColumn();
-						ImGui::Text("%s", toString(castMember._value->_type));
-
-						ImGui::TableNextColumn();
-						float columnWidth = ImGui::GetColumnWidth();
-
-						ImGuiImage imgID = {};
-						switch (castMember._value->_type) {
-						case kCastBitmap:
-							{
-								imgID = getImageID(castMember._value);
-								if (imgID.id) {
-									float offsetX = (columnWidth - 32.f) * 0.5f;
-									ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
-									showImage(imgID, name.c_str(), 32.f);
-								}
-							}
-							break;
-
-						case kCastText:
-						case kCastRichText:
-						case kCastButton:
-							{
-								imgID = getTextID(castMember._value);
-								if (imgID.id) {
-									float offsetX = (columnWidth - 32.f) * 0.5f;
-									ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
-									showImage(imgID, name.c_str(), 32.f);
-								}
-							}
-							break;
+				Cast *sharedCast = movie->getSharedCast();
+				if(sharedCast && sharedCast->_loadedCast)
+					drawCastRow(sharedCast);
 
-						case kCastShape:
-							{
-								imgID = getShapeID(castMember._value);
-								if (imgID.id) {
-									float offsetX = (columnWidth - 32.f) * 0.5f;
-									ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
-									showImage(imgID, name.c_str(), 32.f);
-								}
-							}
-							break;
-						default:
-							break;
-						}
-					}
-				}
 				ImGui::EndTable();
 			}
 		} else {


Commit: 0ea5066c062a20b52f3f3882eb42d680502548b2
    https://github.com/scummvm/scummvm/commit/0ea5066c062a20b52f3f3882eb42d680502548b2
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-03T23:12:22+02:00

Commit Message:
DIRECTOR: DT: fix crash and add script viewing from cast details

Changed paths:
    engines/director/debugger/debugtools.cpp
    engines/director/debugger/dt-castdetails.cpp


diff --git a/engines/director/debugger/debugtools.cpp b/engines/director/debugger/debugtools.cpp
index 733d3c04d9f..f9bb8840a3d 100644
--- a/engines/director/debugger/debugtools.cpp
+++ b/engines/director/debugger/debugtools.cpp
@@ -127,7 +127,7 @@ ImGuiScript toImGuiScript(ScriptType scriptType, CastMemberID id, const Common::
 
 ScriptContext *getScriptContext(CastMemberID id) {
 	const Director::Movie *movie = g_director->getCurrentMovie();;
-	const Cast *cast = movie->getCasts()->getVal(id.castLib);
+	const Cast *cast;
 
 	if (id.castLib == SHARED_CAST_LIB)
 		cast = movie->getSharedCast();
diff --git a/engines/director/debugger/dt-castdetails.cpp b/engines/director/debugger/dt-castdetails.cpp
index d275b5e460c..bd0939442bc 100644
--- a/engines/director/debugger/dt-castdetails.cpp
+++ b/engines/director/debugger/dt-castdetails.cpp
@@ -38,6 +38,7 @@
 #include "director/score.h"
 #include "director/sound.h"
 
+#include "director/movie.h"
 #include "director/types.h"
 
 namespace Director {
@@ -544,10 +545,16 @@ void drawBaseCMprops(CastMember *member) {
 				ImGui::Text("scriptText");
 				ImGui::TableSetColumnIndex(1);
 				if (info && !info->script.empty()) {
-					ImGui::TextColored(ImVec4(0.7f, 0.7f, 1.0f, 1.0f), "...");
+					CastMemberID scriptCastId(member->getID(), cast->_castLibID);
+					bool hasCastScript = g_director->getCurrentMovie()->getScriptContext(kCastScript, scriptCastId) != nullptr;
+					ImGui::TextColored(hasCastScript ? ImVec4(0.4f, 0.8f, 0.4f, 1.0f) : ImVec4(0.7f, 0.7f, 1.0f, 1.0f), "...");
 					if (ImGui::IsItemHovered()) {
-						ImGui::SetTooltip("%s", info->script.c_str());
+						ImGui::SetTooltip(hasCastScript ? "%s\n\n(click to open script)" : "%s", info->script.c_str());
+						if (hasCastScript)
+							ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
 					}
+					if (hasCastScript && ImGui::IsItemClicked(0))
+						displayScriptRef(scriptCastId);
 				} else {
 					ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "...");
 				}


Commit: d33776efd2c1c24223211398d1dcc11cbc27d926
    https://github.com/scummvm/scummvm/commit/d33776efd2c1c24223211398d1dcc11cbc27d926
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-03T23:12:22+02:00

Commit Message:
DIRECTOR: DT: fix null dereferences, OOB accesses, and memory leaks

Changed paths:
    engines/director/debugger/debugtools.cpp
    engines/director/debugger/dt-castdetails.cpp
    engines/director/debugger/dt-controlpanel.cpp
    engines/director/debugger/dt-internal.h
    engines/director/debugger/dt-save-state.cpp
    engines/director/debugger/dt-score.cpp
    engines/director/debugger/dt-script-d2.cpp
    engines/director/debugger/dt-script-d4.cpp
    engines/director/debugger/dt-scripts.cpp


diff --git a/engines/director/debugger/debugtools.cpp b/engines/director/debugger/debugtools.cpp
index f9bb8840a3d..6512c3e3eaf 100644
--- a/engines/director/debugger/debugtools.cpp
+++ b/engines/director/debugger/debugtools.cpp
@@ -82,13 +82,16 @@ const LingoDec::Handler *getHandler(CastMemberID id, const Common::String &handl
 	if (id.castLib == SHARED_CAST_LIB)
 		return getHandler(movie->getSharedCast(), id, handlerId);
 
-	const Cast *cast = movie->getCasts()->getVal(id.castLib);
+	const Cast *cast = movie->getCasts()->getValOrDefault(id.castLib, nullptr);
 
 	const LingoDec::Handler *handler = getHandler(cast, id, handlerId);
 	if (handler)
 		return handler;
 
-	return getHandler(movie->getSharedCast(), id, handlerId);
+	Cast *sharedCast = movie->getSharedCast();
+	if (!sharedCast)
+		return nullptr;
+	return getHandler(sharedCast, id, handlerId);
 }
 
 ImGuiScript toImGuiScript(ScriptType scriptType, CastMemberID id, const Common::String &handlerId) {
@@ -101,7 +104,11 @@ ImGuiScript toImGuiScript(ScriptType scriptType, CastMemberID id, const Common::
 	if (!handler) {
 		const ScriptContext *ctx;
 		if (id.castLib == SHARED_CAST_LIB) {
-			ctx = g_director->getCurrentMovie()->getSharedCast()->_lingoArchive->getScriptContext(scriptType, id.member);
+			// null guard
+			Cast *sharedCast = g_director->getCurrentMovie()->getSharedCast();
+			if (!sharedCast)
+				return result;
+			ctx = sharedCast->_lingoArchive->getScriptContext(scriptType, id.member);
 		} else {
 			ctx = g_director->getCurrentMovie()->getScriptContext(scriptType, id);
 		}
@@ -144,10 +151,17 @@ ScriptContext *getScriptContext(CastMemberID id) {
 
 ScriptContext *getScriptContext(uint32 nameIndex, CastMemberID id, Common::String handlerName) {
 	Movie *movie = g_director->getCurrentMovie();
-	Cast *cast = movie->getCasts()->getVal(id.castLib);
+	Cast *cast;
+	if (id.castLib == SHARED_CAST_LIB)
+		cast = movie->getSharedCast();
+	else
+		cast = movie->getCasts()->getValOrDefault(id.castLib, nullptr);
+
+	if (!cast)
+		return nullptr;
 
 	// If the name at nameIndex is not the same as handler name, means its a local script (in the same Lscr resource)
-	if (cast && cast->_lingoArchive->names[nameIndex] != handlerName) {
+	if (cast->_lingoArchive->names[nameIndex] != handlerName) {
 		return cast->_lingoArchive->findScriptContext(id.member);
 	}
 
@@ -299,6 +313,8 @@ static void setToolTipImage(const ImGuiImage &image, const char *name) {
 }
 
 void showImage(const ImGuiImage &image, const char *name, float thumbnailSize) {
+	if (!image.width || !image.height)
+		return;
 	ImVec2 size;
 	if (image.width > image.height) {
 		size = {thumbnailSize - 2, (thumbnailSize - 2) * image.height / image.width};
@@ -320,6 +336,8 @@ void showImage(const ImGuiImage &image, const char *name, float thumbnailSize) {
 }
 
 void showImageWrappedBorder(const ImGuiImage &image, const char *name, float imageSize) {
+	if (!image.width || !image.height)
+		return;
 	ImVec2 size;
 	if (image.width > image.height) {
 		size = {imageSize, imageSize * image.height / image.width};
@@ -424,8 +442,11 @@ ImGuiImage getTextID(CastMember *castMember) {
 	Graphics::MacWidget *widget = castMember->createWidget(bbox, channel, kTextSprite);
 	Graphics::Surface surface;
 
-	if (!widget || !widget->getSurface() || !widget->getSurface()->getPixels())
-      return {};
+	if (!widget || !widget->getSurface() || !widget->getSurface()->getPixels()) {
+		delete channel;
+		delete sprite;
+		return {};
+	}
 
 	surface.copyFrom(*widget->getSurface());
 
diff --git a/engines/director/debugger/dt-castdetails.cpp b/engines/director/debugger/dt-castdetails.cpp
index bd0939442bc..1999dddddfa 100644
--- a/engines/director/debugger/dt-castdetails.cpp
+++ b/engines/director/debugger/dt-castdetails.cpp
@@ -300,7 +300,7 @@ void showImageViewer() {
 			}
 
 			// text tab
-			if (!state->cachedNormalized.empty() && ImGui::BeginTabItem("Text")) {
+			if (!state->cachedNormalized.empty() && state->buffer && ImGui::BeginTabItem("Text")) {
 
 				ImGui::BeginChild("##textPanel", ImGui::GetContentRegionAvail(), false,
 					ImGuiWindowFlags_HorizontalScrollbar);
diff --git a/engines/director/debugger/dt-controlpanel.cpp b/engines/director/debugger/dt-controlpanel.cpp
index d0fd862aec2..094dc8d97bc 100644
--- a/engines/director/debugger/dt-controlpanel.cpp
+++ b/engines/director/debugger/dt-controlpanel.cpp
@@ -34,7 +34,7 @@ static uint32 getLineFromPC() {
 	ScriptData *scriptData = &_state->_functions._windowScriptData.getOrCreateVal(g_director->getCurrentWindow());
 
 	const uint pc = g_lingo->_state->pc;
-	if (scriptData->_scripts.empty())
+	if (scriptData->_scripts.empty() || scriptData->_current >= scriptData->_scripts.size())
 		return 0;
 	const Common::Array<uint> &offsets = scriptData->_scripts[scriptData->_current].startOffsets;
 	for (uint i = 0; i < offsets.size(); i++) {
@@ -128,7 +128,12 @@ void showControlPanel() {
 	ImGui::SetNextWindowSize(ImVec2(200, 103), ImGuiCond_FirstUseEver);
 
 	if (ImGui::Begin("Control Panel", &_state->_w.controlPanel, ImGuiWindowFlags_NoDocking)) {
+		// null guard
 		Movie *movie = g_director->getCurrentMovie();
+		if (!movie) {
+			ImGui::End();
+			return;
+		}
 		Score *score = movie->getScore();
 		ImDrawList *dl = ImGui::GetWindowDrawList();
 
diff --git a/engines/director/debugger/dt-internal.h b/engines/director/debugger/dt-internal.h
index cf5e1d0c981..998f4795286 100644
--- a/engines/director/debugger/dt-internal.h
+++ b/engines/director/debugger/dt-internal.h
@@ -224,7 +224,7 @@ typedef struct ImGuiState {
 		Common::HashMap<Window *, ScriptData> _windowScriptData;
 	} _functions;
 	struct {
-		CastMember *_castMember;
+		CastMember *_castMember = nullptr;
 		Common::HashMap<CastMember *, int> _filmLoopCurrentFrame;
 	} _castDetails;
 
diff --git a/engines/director/debugger/dt-save-state.cpp b/engines/director/debugger/dt-save-state.cpp
index 0180968d6ee..e8088fc94e0 100644
--- a/engines/director/debugger/dt-save-state.cpp
+++ b/engines/director/debugger/dt-save-state.cpp
@@ -140,6 +140,16 @@ void loadSavedState() {
 	debugC(7, kDebugImGui, "ImGui::loaded state: %s", saved->stringify(true).c_str());
 
 	// Load open/closed window flags
+	if (!saved->asObject()["Windows"] || !saved->asObject()["Window Settings"] ||
+			!saved->asObject()["Log"] || !saved->asObject()["ScoreWindow"] ||
+			!saved->asObject()["ChannelsWindow"] || !saved->asObject()["CastWindow"] ||
+			!saved->asObject()["IgnoreMouse"] || !saved->asObject()["EnableMultiViewport"]) {
+		warning("ImGui::loadSavedState(): save file is missing required fields, ignoring");
+		free(data);
+		delete saved;
+		delete savedState;
+		return;
+	}
 	int64 openFlags = saved->asObject()["Windows"]->asIntegerNumber();
 	Common::Array<WindowFlag> windows = getWindowFlags();
 
@@ -157,8 +167,8 @@ void loadSavedState() {
 	}
 
 	// Load window settings
-	const char *windowSettings = saved->asObject()["Window Settings"]->asString().c_str();
-	ImGui::LoadIniSettingsFromMemory(windowSettings);
+	Common::String windowSettingsStr = saved->asObject()["Window Settings"]->asString();
+	ImGui::LoadIniSettingsFromMemory(windowSettingsStr.c_str());
 
 	// Load the log
 	Common::JSONArray log = saved->asObject()["Log"]->asArray();
diff --git a/engines/director/debugger/dt-score.cpp b/engines/director/debugger/dt-score.cpp
index ae11f63cbc9..fcc05364f08 100644
--- a/engines/director/debugger/dt-score.cpp
+++ b/engines/director/debugger/dt-score.cpp
@@ -150,6 +150,9 @@ static void buildContinuationData(Window *window) {
 	Score *score = window->getCurrentMovie()->getScore();
 	uint numFrames = score->_scoreCache.size();
 
+	if (numFrames == 0)
+		return;
+
 	uint numChannels = score->_scoreCache[0]->_sprites.size();
 	_state->_continuationData.resize(numChannels);
 
@@ -391,7 +394,9 @@ static void drawSpriteInspector(Score *score, Cast *cast, uint numFrames) {
 		CastMember *castMember = nullptr;
 		bool shape = false;
 
-		if (_state->_selectedScoreCast.frame != -1 && !_state->_selectedScoreCast.isMainChannel)
+		if (_state->_selectedScoreCast.frame != -1 && !_state->_selectedScoreCast.isMainChannel
+				&& _state->_selectedScoreCast.frame < (int)score->_scoreCache.size()
+				&& _state->_selectedScoreCast.channel < (int)score->_scoreCache[_state->_selectedScoreCast.frame]->_sprites.size())
 			sprite = score->_scoreCache[_state->_selectedScoreCast.frame]->_sprites[_state->_selectedScoreCast.channel];
 
 		if (sprite) {
@@ -975,6 +980,8 @@ static void drawMainChannelGrid(ImDrawList *dl, ImVec2 startPos, Score *score) {
 				case kChScript: // open script in script editor
 					if (mc.actionId.member) {
 						ScriptContext *ctx = getScriptContext(mc.actionId);
+						if (!ctx)
+							break;
 						for (auto &handler : ctx->_functionHandlers) {
 							ImGuiScript script = toImGuiScript(kScoreScript, mc.actionId, handler._key);
 							script.byteOffsets = ctx->_functionByteOffsets[script.handlerId];
@@ -1138,6 +1145,11 @@ void showScore() {
 
 		buildContinuationData(selectedWindow);
 
+		if (!selectedWindow->getCurrentMovie()) {
+			ImGui::Text("No movie loaded");
+			ImGui::End();
+			return;
+		}
 		Score *score = selectedWindow->getCurrentMovie()->getScore();
 		uint numFrames = score->_scoreCache.size();
 		Cast *cast = selectedWindow->getCurrentMovie()->getCast();
@@ -1191,6 +1203,11 @@ void showChannels() {
 	if (ImGui::Begin("Channels", &_state->_w.channels)) {
 		Window *selectedWindow = windowListCombo(&_state->_scoreWindow);
 
+		if (!selectedWindow->getCurrentMovie()) {
+			ImGui::Text("No movie loaded");
+			ImGui::End();
+			return;
+		}
 		Score *score = selectedWindow->getCurrentMovie()->getScore();
 		const Frame &frame = *score->_currentFrame;
 
@@ -1410,8 +1427,10 @@ void showChannels() {
 						if (sprite._spriteListIdx) {
 							Common::MemoryReadStreamEndian *stream = score->getSpriteDetailsStream(sprite._spriteListIdx + 2);
 							Common::String name;
-							if (stream)
+							if (stream) {
 								name = stream->readPascalString();
+								delete stream;
+							}
 							ImGui::Text("%s", name.c_str());
 						} else {
 							ImGui::Text(" ");
diff --git a/engines/director/debugger/dt-script-d2.cpp b/engines/director/debugger/dt-script-d2.cpp
index 2e07fb5c042..6e732985fde 100644
--- a/engines/director/debugger/dt-script-d2.cpp
+++ b/engines/director/debugger/dt-script-d2.cpp
@@ -48,7 +48,8 @@ public:
 		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);
+			if (head->sp.ctx)
+				_isScriptInDebug = (head->sp.ctx->_id == script.id.member) && (*head->sp.name == script.handlerId);
 		}
 		_script.startOffsets.clear();
 	}
diff --git a/engines/director/debugger/dt-script-d4.cpp b/engines/director/debugger/dt-script-d4.cpp
index 5d7cc2d1627..e47661be2a5 100644
--- a/engines/director/debugger/dt-script-d4.cpp
+++ b/engines/director/debugger/dt-script-d4.cpp
@@ -41,7 +41,8 @@ public:
 		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);
+			if (head->sp.ctx)
+				_isScriptInDebug = (head->sp.ctx->_id == script.id.member) && (*head->sp.name == script.handlerId);
 		}
 		_script.startOffsets.clear();
 	}
@@ -1128,6 +1129,8 @@ private:
 
 	void renderLine(uint p) {
 		bool showCurrentStatement = false;
+		if (_script.byteOffsets.empty())
+			return;
 		p = MIN(p, _script.byteOffsets.size() - 1);
 		uint pc = _script.byteOffsets[p];
 		_script.startOffsets.push_back(pc);
diff --git a/engines/director/debugger/dt-scripts.cpp b/engines/director/debugger/dt-scripts.cpp
index 1690e10f034..402f96376cf 100644
--- a/engines/director/debugger/dt-scripts.cpp
+++ b/engines/director/debugger/dt-scripts.cpp
@@ -130,7 +130,8 @@ static void renderCallStack(uint pc) {
 
 		if (frame->sp.type != VOIDSYM) {
 			stackFrame = Common::String::format("#%d ", i);
-			stackFrame += Common::String::format("%d ", frame->sp.ctx->_scriptId);
+			if (frame->sp.ctx)
+				stackFrame += Common::String::format("%d ", frame->sp.ctx->_scriptId);
 
 			if (frame->sp.ctx && frame->sp.ctx->_id != -1) {
 				stackFrame += Common::String::format("(%d): ", frame->sp.ctx->_id);
@@ -155,6 +156,8 @@ static void renderCallStack(uint pc) {
 		if (ImGui::Selectable(stackFrame.c_str())) {
 			CFrame *head = callstack[callstack.size() - i - 1];
 			ScriptContext *scriptContext = head->sp.ctx;
+			if (!scriptContext || !movie->getCast())
+				continue;
 			int castLibID = movie->getCast()->_castLibID;
 			int castId = head->sp.ctx->_id;
 			bool childScript = false;
@@ -183,13 +186,13 @@ static bool showScriptCast(CastMemberID &id) {
 	bool closed = true;
 
 	if (ImGui::Begin(wName.c_str(), &closed)) {
-		Cast *cast = g_director->getCurrentMovie()->getCasts()->getVal(id.castLib);
+		Cast *cast = g_director->getCurrentMovie()->getCasts()->getValOrDefault(id.castLib, nullptr);
 		ScriptContext *ctx = g_director->getCurrentMovie()->getScriptContext(kScoreScript, id);
 
 		if (ctx) {
 			for (auto &handler : ctx->_functionHandlers)
 				renderCastScript(handler._value);
-		} else if (cast->_lingoArchive->factoryContexts.contains(id.member)) {
+		} else if (cast && cast->_lingoArchive->factoryContexts.contains(id.member)) {
 			for (auto &it : *cast->_lingoArchive->factoryContexts.getVal(id.member)) {
 				for (auto &handler : it._value->_functionHandlers)
 					renderCastScript(handler._value);
@@ -272,11 +275,13 @@ void showHandlers() {
 		return;
 	}
 
+	Common::Array<uint32> toClose;
 	for (auto handler : _state->_openHandlers) {
-		if (!showHandler(handler._value)) {
-			_state->_openHandlers.erase(handler._value.id.member);
-		}
+		if (!showHandler(handler._value))
+			toClose.push_back(handler._value.id.member);
 	}
+	for (uint32 key : toClose)
+		_state->_openHandlers.erase(key);
 }
 
 static void updateCurrentScript() {
@@ -291,6 +296,8 @@ static void updateCurrentScript() {
 	CFrame *head = callstack[callstack.size() - 1];
 	const Director::Movie *movie = g_director->getCurrentMovie();
 	ScriptContext *scriptContext = head->sp.ctx;
+	if (!scriptContext || !movie->getCast())
+		return;
 	int castLibID = movie->getCast()->_castLibID;
 	int castId = head->sp.ctx->_id;
 	bool childScript = false;
@@ -573,7 +580,7 @@ void showExecutionContext() {
 		ScriptData *scriptData = &_state->_functions._windowScriptData.getOrCreateVal(stage);
 		updateCurrentScript();
 
-		if (scriptData->_showScript) {
+		if (scriptData->_showScript && !scriptData->_scripts.empty() && scriptData->_current < scriptData->_scripts.size()) {
 			// disable highlighting while rendering scripts in Execution Context
 			bool oldSuppress = _state->_dbg._suppressHighlight;
 			_state->_dbg._suppressHighlight = true;
@@ -693,7 +700,7 @@ void showExecutionContext() {
 			scriptData = &_state->_functions._windowScriptData.getOrCreateVal(window);
 			updateCurrentScript();
 
-			if (scriptData->_showScript) {
+			if (scriptData->_showScript && !scriptData->_scripts.empty() && scriptData->_current < scriptData->_scripts.size()) {
 				bool oldSuppress = _state->_dbg._suppressHighlight;
 				_state->_dbg._suppressHighlight = true;
 				ImGuiScript &current = scriptData->_scripts[scriptData->_current];




More information about the Scummvm-git-logs mailing list