[Scummvm-git-logs] scummvm master -> 947dd6898d8277ffdd2131bf7ee0a80f5a6a4f56

bluegr noreply at scummvm.org
Tue Aug 4 22:58:09 UTC 2026


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

Summary:
3b3d5c5ca9 SCI32: Move control code parsing out of drawText()
947dd6898d SCI32: Fix right-to-left text with inline style control codes


Commit: 3b3d5c5ca99e52e134448ba43005de8b9da33a50
    https://github.com/scummvm/scummvm/commit/3b3d5c5ca99e52e134448ba43005de8b9da33a50
Author: Zvika Haramaty (haramaty.zvika at gmail.com)
Date: 2026-08-05T01:58:05+03:00

Commit Message:
SCI32: Move control code parsing out of drawText()

`readStyleControl()` is the control code block of `drawText()`, moved
into a function unchanged. No functional change.

This is preparation for the right-to-left drawing path, which has to
read the same codes and must not grow a second parser of the same
syntax.

Assisted-by: Claude:claude-opus-5

Changed paths:
    engines/sci/graphics/text32.cpp


diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp
index c1d56002893..8e85e52d46c 100644
--- a/engines/sci/graphics/text32.cpp
+++ b/engines/sci/graphics/text32.cpp
@@ -378,6 +378,49 @@ void GfxText32::drawTextBox(const Common::String &text) {
 	drawTextBox();
 }
 
+// This internal function gets called as soon as a '|' is found in a text. It
+// will read the encountered code and its value, and forward the text past the
+// whole control code.
+// Returns false when the control code runs off the end of the text, in which
+// case the caller stops reading it.
+static bool readStyleControl(const char *&text, uint &length, char &controlChar, uint16 &value) {
+	controlChar = *text++;
+	--length;
+
+	if (length == 0) {
+		return false;
+	}
+
+	value = 0;
+	if (controlChar == 'a' || controlChar == 'c' || controlChar == 'f') {
+		while (length > 0) {
+			const char valueChar = *text;
+			if (valueChar < '0' || valueChar > '9') {
+				break;
+			}
+
+			++text;
+			--length;
+			value = 10 * value + (valueChar - '0');
+		}
+
+		if (length == 0) {
+			return false;
+		}
+	}
+
+	while (length > 0 && *text != '|') {
+		++text;
+		--length;
+	}
+	if (length > 0) {
+		++text;
+		--length;
+	}
+
+	return true;
+}
+
 void GfxText32::drawText(const uint index, uint length) {
 	assert(index + length <= _text.size());
 
@@ -403,47 +446,18 @@ void GfxText32::drawText(const uint index, uint length) {
 		}
 
 		if (currentChar == '|') {
-			const char controlChar = *text++;
-			--length;
-
-			if (length == 0) {
+			char controlChar;
+			uint16 value;
+			if (!readStyleControl(text, length, controlChar, value)) {
 				return;
 			}
 
-			if (controlChar == 'a' || controlChar == 'c' || controlChar == 'f') {
-				uint16 value = 0;
-
-				while (length > 0) {
-					const char valueChar = *text;
-					if (valueChar < '0' || valueChar > '9') {
-						break;
-					}
-
-					++text;
-					--length;
-					value = 10 * value + (valueChar - '0');
-				}
-
-				if (length == 0) {
-					return;
-				}
-
-				if (controlChar == 'a') {
-					_alignment = (TextAlign)value;
-				} else if (controlChar == 'c') {
-					_foreColor = value;
-				} else if (controlChar == 'f') {
-					setFont(value);
-				}
-			}
-
-			while (length > 0 && *text != '|') {
-				++text;
-				--length;
-			}
-			if (length > 0) {
-				++text;
-				--length;
+			if (controlChar == 'a') {
+				_alignment = (TextAlign)value;
+			} else if (controlChar == 'c') {
+				_foreColor = value;
+			} else if (controlChar == 'f') {
+				setFont(value);
 			}
 		} else {
 			drawChar(currentChar);


Commit: 947dd6898d8277ffdd2131bf7ee0a80f5a6a4f56
    https://github.com/scummvm/scummvm/commit/947dd6898d8277ffdd2131bf7ee0a80f5a6a4f56
Author: Zvika Haramaty (haramaty.zvika at gmail.com)
Date: 2026-08-05T01:58:05+03:00

Commit Message:
SCI32: Fix right-to-left text with inline style control codes

SCI32 text interleaves printable characters with pipe-delimited style
control codes such as `|f70|`. The right-to-left path handed the whole
line, control codes included, to `Common::convertBiDiString()`, which
reorders them along with the text. The draw loop then read the moved
codes as ordinary characters, painting them as glyphs and running the
draw position off the end of the bitmap. In the Hebrew fan translation
of SQ6 this produced "font.70 glyph 102 drawn out of bounds", glyph 102
being the letter 'f' of the control code itself.

Only the printable characters may take part in the reordering, so
`drawTextRTL()` separates them from the control codes and resolves the
codes into the font and colour that each character is drawn in. After
reordering, every character is still drawn in the style that its logical
position selected.

Assisted-by: Claude:claude-opus-5

Changed paths:
    engines/sci/graphics/text32.cpp
    engines/sci/graphics/text32.h


diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp
index 8e85e52d46c..d1e131a7e81 100644
--- a/engines/sci/graphics/text32.cpp
+++ b/engines/sci/graphics/text32.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/array.h"
 #include "common/util.h"
 #include "common/stack.h"
 #include "common/unicode-bidi.h"
@@ -428,15 +429,10 @@ void GfxText32::drawText(const uint index, uint length) {
 	// implementation in SSCI, but is accurate. Primarily the changes revolve
 	// around eliminating some extra temporaries and fixing the logic to match.
 
-	Common::String textString;
-	const char *text;
-	if (!g_sci->isLanguageRTL()) {
-		text = _text.c_str() + index;
-	} else {
-		const char *textOrig = _text.c_str() + index;
-		Common::String textLogical = Common::String(textOrig, (uint32)length);
-		textString = Common::convertBiDiString(textLogical, g_sci->getLanguage(), Common::BiDiParagraph::BIDI_PAR_RTL);
-		text = textString.c_str();
+	const char *text = _text.c_str() + index;
+	if (g_sci->isLanguageRTL()) {
+		drawTextRTL(text, length);
+		return;
 	}
 
 	while (length-- > 0) {
@@ -465,6 +461,70 @@ void GfxText32::drawText(const uint index, uint length) {
 	}
 }
 
+// The styles that the inline control codes give to a character
+struct TextStyle {
+	GuiResourceId fontId;
+	uint8 foreColor;
+};
+
+void GfxText32::drawTextRTL(const char *text, uint length) {
+	Common::String textLogical;
+	// The style each printable character is drawn in
+	Common::Array<TextStyle> styles;
+
+	TextStyle currentStyle;
+	currentStyle.fontId = _fontId;
+	currentStyle.foreColor = _foreColor;
+	// Alignment positions the line as a whole, so only its final value is used
+	TextAlign alignment = _alignment;
+
+	while (length-- > 0) {
+		const char currentChar = *text++;
+
+		if (currentChar == '|') {
+			char controlChar;
+			uint16 value;
+			// Leaves the loop rather than the function, so that the characters
+			// collected so far are still drawn below
+			if (!readStyleControl(text, length, controlChar, value)) {
+				break;
+			}
+
+			if (controlChar == 'a') {
+				alignment = (TextAlign)value;
+			} else if (controlChar == 'c') {
+				currentStyle.foreColor = value;
+			} else if (controlChar == 'f') {
+				currentStyle.fontId = value;
+			}
+		} else {
+			textLogical += currentChar;
+			styles.push_back(currentStyle);
+		}
+	}
+
+	// The only right-to-left language in SCI is Hebrew, see isLanguageRTL
+	assert(g_sci->getLanguage() == Common::HE_ISR);
+	const Common::CodePage codePage = Common::kWindows1255;
+	const Common::UnicodeBiDiText bidi(textLogical.decode(codePage), Common::BIDI_PAR_RTL);
+	const Common::String textVisual = bidi.visual.encode(codePage);
+
+	// Reordering separates the characters from the controls that styled them,
+	// so each one is drawn in the style its logical position gave it
+	for (uint i = 0; i < textVisual.size(); ++i) {
+		const TextStyle &style = styles[bidi.getLogicalPosition(i)];
+		setFont(style.fontId);
+		_foreColor = style.foreColor;
+		drawChar((byte)textVisual[i]);
+	}
+
+	// Leave behind the styles that were in effect at the logical end of the
+	// line, because that is where the next line carries on from
+	setFont(currentStyle.fontId);
+	_foreColor = currentStyle.foreColor;
+	_alignment = alignment;
+}
+
 void GfxText32::invertRect(const reg_t bitmapId, int16 bitmapStride, const Common::Rect &rect, const uint8 foreColor, const uint8 backColor, const bool doScaling) {
 	Common::Rect targetRect = rect;
 	if (doScaling) {
diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h
index 06a96622b8b..49dc733c4be 100644
--- a/engines/sci/graphics/text32.h
+++ b/engines/sci/graphics/text32.h
@@ -121,6 +121,13 @@ private:
 	void drawChar(const uint16 charIndex);
 	void drawText(const uint index, uint length);
 
+	/**
+	 * Draws text in a right-to-left language. Only the printable characters
+	 * take part in the bidirectional reordering; each is then drawn in the
+	 * style that its logical position gave it.
+	 */
+	void drawTextRTL(const char *text, uint length);
+
 	/**
 	 * Gets the length of the longest run of text available within the currently
 	 * loaded text, starting from the given `charIndex` and running for up to




More information about the Scummvm-git-logs mailing list