[Scummvm-git-logs] scummvm master -> 123e6d10b63d12f26082abd329485f3d824560f9

athrxx noreply at scummvm.org
Fri Apr 8 17:54:05 UTC 2022


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

Summary:
359a9718e7 SCUMM: COMI: Fix string wrapping when wrap flag is active
51394cc7bb SCUMM: COMI: Clean up code and separate SCUMM v7-8 code from v6
eb276dcf48 SCUMM: Fix typo
addf810b9d SCUMM: Use getStringHeight() in v7-8 CHARSET_1() for pixel-perfect wrapping
d1764b52d0 SCUMM: COMI: Fix an oversight in enqueueText()
05980ff294 SCUMM: remove unneeded function
7fe7b30459 SCUMM: (SCUMM5/FM-Towns) - minor cleanup
1f56132725 SCUMM: (SCUMM7/8) - reorganize font rendering - first part
0256e92c25 SCUMM: (SCUMM7/8) - reorganize font rendering - second part
37e58e8420 SCUMM: (SCUMM7/8) - minor fix to string escape code handling
f5f49d1b3f SCUMM: (SCUMM7/8) - improve ingame text wrapping and positioning for DIG and COMI
78835f0318 SCUMM: (SCUMM7/8) - start cleaning up text verb drawing
997e2c4b05 SCUMM: (SCUMM7/8) - cleanup
de4513cdff SCUMM: (SCUMM7/8) - implement original text formatting flags for more flexibility
d9ca128863 SCUMM: (SCUMM7/8) - implement right alignment flag for text
acb665c109 SCUMM: (SCUMM7/8) - cleanup (text) verb drawing and Hebrew right-to-left drawing
2e249f1d00 SCUMM: (SCUMM7/8) - reduce deprecated code
dcae67025e SCUMM: fix build
ba0cbd2dc8 SCUMM: add support for FT with the new text handling
5963241222 SCUMM: FT: Fix text bugs
ede23b4a5e SCUMM: DIG (demo): Fix crash caused by unhandled escape codes
69022c5060 SCUMM: DIG (demo): Fix escape code 3 handling
31fca3be6d SCUMM: FT: Fix crash related to text handling
9f2e0f6d45 SCUMM: Clean up test assertions
9468d24958 SCUMM: FT/DIG: Fix line spacing accuracy
bd21f18d0c SCUMM: DIG (demo): Improve text accuracy
c312cf8e7e SCUMM: FT (demo): Handle escape sequence 3 properly
d3f756eedc SCUMM: Remove whitespace in nut_renderer.cpp
47270487b6 SCUMM: Implement correct behavior for old style text overflow
1408d6f7e3 SCUMM: FT: Fix missing _talkDelay setup
6fd84288a2 SCUMM: Fix typo in string_v7.h
bab5ff89b4 SCUMM: Remove empty line
b15ae51f8b SCUMM: Fix missing virtual dtor warning
755ba94486 SCUMM: Remove no-op statements
123e6d10b6 SCUMM: (COMI/CJK) - fix verb drawing regression


Commit: 359a9718e70c5687cd2e87c29af386ed2046a409
    https://github.com/scummvm/scummvm/commit/359a9718e70c5687cd2e87c29af386ed2046a409
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: COMI: Fix string wrapping when wrap flag is active

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/charset.h
    engines/scumm/string.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index bfbe3ba94dd..3e9ca8691f3 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -532,6 +532,33 @@ int CharsetRenderer::getStringWidth(int arg, const byte *text, uint strLenMax) {
 	return width;
 }
 
+int CharsetRenderer::getStringHeight(const char *str, uint numBytesMax) {
+	assert(str);
+	int totalHeight = 0;
+	int lineHeight = 0;
+
+	if (!numBytesMax)
+		return 0;
+
+	while (*str && numBytesMax) {
+		// No treatment of the ^ commands here, since they're handled/removed in handleTextResource().
+		if (*str == '\n') {
+			totalHeight += (lineHeight ? lineHeight : getFontHeight()) + 1;
+			lineHeight = 0;
+		} else if (*str != '\r' && *str != _vm->_newLineCharacter) {
+			lineHeight = MAX<int>(lineHeight, getCharHeight(*str));
+			if (is2ByteCharacter(_vm->_language, *str)) {
+				++str;
+				--numBytesMax;
+			}
+		}
+		++str;
+		--numBytesMax;
+	}
+
+	return totalHeight + (lineHeight ? lineHeight : getFontHeight()) + 1;
+}
+
 void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
 	int lastKoreanLineBreak = -1;
 	int origPos = pos;
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index d4116bc834c..0a19a2ddba3 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -96,7 +96,8 @@ public:
 	virtual void printChar(int chr, bool ignoreCharsetMask) = 0;
 	virtual void drawChar(int chr, Graphics::Surface &s, int x, int y) {}
 
-	virtual int getStringWidth(int arg, const byte *text, uint strLenMax = 100000);
+	virtual int getStringWidth(int arg, const byte *text uint strLenMax = 100000);
+	int getStringHeight(const char *str, uint strLenMax = 100000);
 	void addLinebreaks(int a, byte *str, int pos, int maxwidth);
 	void translateColor();
 
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 84c98944ba1..9da48c6d919 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -179,6 +179,62 @@ void ScummEngine_v6::enqueueText(const byte *text, int x, int y, byte color, byt
 
 		clipHeight = clipHeight + clipHeight / 2;
 		y = MIN<int>(y, 470 - clipHeight);
+
+		// HACK: Wrap the text when it's too long.
+		// The original does this by drawing word by word and adjusting x and y for each step of the pipeline.
+		// This, instead, is a mixture of code from SmushFont::drawStringWrap() and CHARSET_1(), which is just 
+		// enough to split the full line into more smaller lines, which are treated as different blastText in 
+		// the queue. Anyway this shouldn't generate more than 2 lines total but it's generalized just in case.
+		_charset->addLinebreaks(0, (byte *) bt.text, 0, _screenWidth - 20);
+		int16 substrPos[200];
+		memset(substrPos, 0, sizeof(substrPos));
+		int16 substrLen[200];
+		memset(substrLen, 0, sizeof(substrLen));
+
+		int len = (int) strlen((char *) bt.text);
+		int curPos = -1;
+		int numLines = 1;
+
+		// Save splitting information (position of the substring and its length)
+		while (curPos <= len) {
+			if (bt.text[curPos] == '\r') {
+				substrPos[numLines] = curPos + 1;
+				substrLen[numLines - 1] -= 1;
+				numLines++;
+			} else {
+				substrLen[numLines - 1] += 1;
+			}
+			curPos++;
+		}
+
+		// If we have found more than one line, then we split the lines and
+		// draw them from top to bottom
+		if (numLines > 1) {
+			int lastLineY = 0;
+			for (int i = 1; i < numLines; i++) {
+				BlastText &bt_secondary = _blastTextQueue[_blastTextQueuePos++];
+				strncpy((char*)bt_secondary.text, (char*)(bt.text + substrPos[i]), substrLen[i]);
+				bt_secondary.xpos = x;
+				bt_secondary.ypos = lastLineY = y + (numLines - i) * _charset->getStringHeight((char*)bt_secondary.text);
+				bt_secondary.color = color;
+				bt_secondary.charset = charset;
+				bt_secondary.center = center;
+			}
+
+			// Truncate the first substring accordingly
+			bt.text[substrLen[0]] = '\0';
+
+			// Now correct the vertical placement for the last line, and compensate bottom-up
+			int diffY = lastLineY - MIN<int>(lastLineY, 470 - clipHeight);
+
+			for (int i = 1; i < numLines; i++) {
+				BlastText &bt_compensation = _blastTextQueue[_blastTextQueuePos - i];
+				bt_compensation.ypos -= diffY;
+			}
+
+			// Also adjust the first line
+			y -= diffY;
+		}
 	}
 
 	bt.xpos = x;


Commit: 51394cc7bb4b8f9514da58915be766e65c7cb07a
    https://github.com/scummvm/scummvm/commit/51394cc7bb4b8f9514da58915be766e65c7cb07a
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: COMI: Clean up code and separate SCUMM v7-8 code from v6

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/scumm_v6.h
    engines/scumm/scumm_v7.h
    engines/scumm/string.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 3e9ca8691f3..652ca4a9878 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -541,7 +541,23 @@ int CharsetRenderer::getStringHeight(const char *str, uint numBytesMax) {
 		return 0;
 
 	while (*str && numBytesMax) {
-		// No treatment of the ^ commands here, since they're handled/removed in handleTextResource().
+		while (str[0] == '^') {
+			switch (str[1]) {
+			case 'f':
+				// We should change the font on the fly at this point
+				// which would result in a different width result.
+				// This has never been observed in the game though, and
+				// as such, we don't handle it.
+				str += 4;
+				break;
+			case 'c':
+				str += 5;
+				break;
+			default:
+				error("CharsetRenderer::getStringHeight(): Invalid escape code in text string");
+			}
+		}
+
 		if (*str == '\n') {
 			totalHeight += (lineHeight ? lineHeight : getFontHeight()) + 1;
 			lineHeight = 0;
diff --git a/engines/scumm/scumm_v6.h b/engines/scumm/scumm_v6.h
index 92b12b07a12..b05a01f6d51 100644
--- a/engines/scumm/scumm_v6.h
+++ b/engines/scumm/scumm_v6.h
@@ -158,7 +158,7 @@ protected:
 	void useBompCursor(const byte *im, int w, int h);
 	void grabCursor(int x, int y, int w, int h);
 
-	void enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped = false);
+	virtual void enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped = false);
 	void drawBlastTexts();
 	void removeBlastTexts();
 
diff --git a/engines/scumm/scumm_v7.h b/engines/scumm/scumm_v7.h
index cae583e54d8..9cfe3cda1f7 100644
--- a/engines/scumm/scumm_v7.h
+++ b/engines/scumm/scumm_v7.h
@@ -125,6 +125,7 @@ protected:
 
 	int getObjectIdFromOBIM(const byte *obim) override;
 
+	virtual void enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped = false) override;
 	void actorTalk(const byte *msg) override;
 	void translateText(const byte *text, byte *trans_buff) override;
 	void loadLanguageBundle() override;
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 9da48c6d919..32dc86764a8 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -149,6 +149,18 @@ void ScummEngine_v6::enqueueText(const byte *text, int x, int y, byte color, byt
 	BlastText &bt = _blastTextQueue[_blastTextQueuePos++];
 	assert(_blastTextQueuePos <= ARRAYSIZE(_blastTextQueue));
 
+	convertMessageToString(text, bt.text, sizeof(bt.text));
+	bt.xpos = x;
+	bt.ypos = y;
+	bt.color = color;
+	bt.charset = charset;
+	bt.center = center;
+}
+
+#ifdef ENABLE_SCUMM_7_8
+void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped) {
+	assert(_blastTextQueuePos + 1 <= ARRAYSIZE(_blastTextQueue));
+
 	if (_useCJKMode) {
 		// The Dig expressly checks for x == 160 && y == 189 && charset == 3. Usually, if the game wants to print CJK text at the bottom
 		// of the screen it will use y = 183. So maybe this is a hack to fix some script texts that weren forgotten in the CJK converting
@@ -160,44 +172,28 @@ void ScummEngine_v6::enqueueText(const byte *text, int x, int y, byte color, byt
 			y += 2;
 	}
 
-	convertMessageToString(text, bt.text, sizeof(bt.text));
+	byte textBuf[512];
+	convertMessageToString(text, textBuf, sizeof(textBuf));
 
-	// HACK: This corrects the vertical placement of the object descriptions in COMI. The original text renderer does this in the same way
-	// we do in smush_font.cpp, lines 338 - 344: The dimensions of the whole block of text get measured first and then the necessary changes
-	// will be made before printing. Unfortunately, the way our ScummEngine_v7::CHARSET_1() is implemented we can't properly adjust
-	// the y postion there: If we have already printed several lines of text and then realize that we're getting out of bounds then it is too
-	// late, we can't move up the text we've already printed. The same applies to horizontal fixes: if we have already printed several lines
-	// and then encounter a line that is out of bounds we can't move the whole block to the left or right any more (as we should).
-	// Possible TODO: moving the measuring logic contained in SmushFont::drawStringWrap() to ScummEngine_v6 to make it available here, too.
 	if (_game.id == GID_CMI && wrapped) {
-		int of = _charset->getCurID();
-		_charset->setCurID(charset);
-		// Note that this won't detect (and measure correctly) 1-byte characters contained
-		// in 2-byte strings. For now, I trust that it won't happen with the object strings.
-		int clipHeight = _charset->getCharHeight(*bt.text) + 1;
-		_charset->setCurID(of);
-
-		clipHeight = clipHeight + clipHeight / 2;
-		y = MIN<int>(y, 470 - clipHeight);
-
 		// HACK: Wrap the text when it's too long.
 		// The original does this by drawing word by word and adjusting x and y for each step of the pipeline.
 		// This, instead, is a mixture of code from SmushFont::drawStringWrap() and CHARSET_1(), which is just 
 		// enough to split the full line into more smaller lines, which are treated as different blastText in 
 		// the queue. Anyway this shouldn't generate more than 2 lines total but it's generalized just in case.
-		_charset->addLinebreaks(0, (byte *) bt.text, 0, _screenWidth - 20);
+		_charset->addLinebreaks(0, textBuf, 0, _screenWidth - 20);
 		int16 substrPos[200];
 		memset(substrPos, 0, sizeof(substrPos));
 		int16 substrLen[200];
 		memset(substrLen, 0, sizeof(substrLen));
 
-		int len = (int) strlen((char *) bt.text);
+		int len = (int)Common::strnlen((char *)textBuf, sizeof(textBuf));
 		int curPos = -1;
 		int numLines = 1;
 
 		// Save splitting information (position of the substring and its length)
 		while (curPos <= len) {
-			if (bt.text[curPos] == '\r') {
+			if (textBuf[curPos] == '\r') {
 				substrPos[numLines] = curPos + 1;
 				substrLen[numLines - 1] -= 1;
 				numLines++;
@@ -207,42 +203,54 @@ void ScummEngine_v6::enqueueText(const byte *text, int x, int y, byte color, byt
 			curPos++;
 		}
 
-		// If we have found more than one line, then we split the lines and
-		// draw them from top to bottom
-		if (numLines > 1) {
-			int lastLineY = 0;
-			for (int i = 1; i < numLines; i++) {
-				BlastText &bt_secondary = _blastTextQueue[_blastTextQueuePos++];
-				strncpy((char*)bt_secondary.text, (char*)(bt.text + substrPos[i]), substrLen[i]);
-				bt_secondary.xpos = x;
-				bt_secondary.ypos = lastLineY = y + (numLines - i) * _charset->getStringHeight((char*)bt_secondary.text);
-				bt_secondary.color = color;
-				bt_secondary.charset = charset;
-				bt_secondary.center = center;
-			}
-
-			// Truncate the first substring accordingly
-			bt.text[substrLen[0]] = '\0';
+		// Split the lines and draw them from top to bottom
+		int lastLineY = 0;
+		for (int i = 0; i < numLines; i++) {
+			BlastText &bt = _blastTextQueue[_blastTextQueuePos++];
+			Common::strlcpy((char*)bt.text, (char*)(textBuf + substrPos[i]), substrLen[i]);
+			bt.text[substrLen[0]] = '\0'; // Truncate the substring accordingly
+			bt.xpos = x;
+			bt.ypos = lastLineY = y + (_charset->getStringHeight((char*)textBuf) * i);
+			bt.color = color;
+			bt.charset = charset;
+			bt.center = center;
+		}
 
-			// Now correct the vertical placement for the last line, and compensate bottom-up
-			int diffY = lastLineY - MIN<int>(lastLineY, 470 - clipHeight);
+		// HACK: This corrects the vertical placement of the object descriptions in COMI. The original text renderer does this in the same way
+		// we do in smush_font.cpp, lines 338 - 344: The dimensions of the whole block of text get measured first and then the necessary changes
+		// will be made before printing. Unfortunately, the way our ScummEngine_v7::CHARSET_1() is implemented we can't properly adjust
+		// the y postion there: If we have already printed several lines of text and then realize that we're getting out of bounds then it is too
+		// late, we can't move up the text we've already printed. The same applies to horizontal fixes: if we have already printed several lines
+		// and then encounter a line that is out of bounds we can't move the whole block to the left or right any more (as we should).
+		// Possible TODO: moving the measuring logic contained in SmushFont::drawStringWrap() to ScummEngine_v6 to make it available here, too.
 
-			for (int i = 1; i < numLines; i++) {
-				BlastText &bt_compensation = _blastTextQueue[_blastTextQueuePos - i];
-				bt_compensation.ypos -= diffY;
-			}
+		int of = _charset->getCurID();
+		_charset->setCurID(charset);
+		// Note that this won't detect (and measure correctly) 1-byte characters contained
+		// in 2-byte strings. For now, I trust that it won't happen with the object strings.
+		int clipHeight = _charset->getCharHeight(*textBuf) + 1;
+		_charset->setCurID(of);
+		clipHeight = clipHeight + clipHeight / 2;
 
-			// Also adjust the first line
-			y -= diffY;
+		// Correct for the vertical placement for the last line, and compensate bottom-up
+		int diffY = lastLineY - MIN<int>(lastLineY, 470 - clipHeight);
+		
+		for (int i = 0; i < numLines; i++) {
+			BlastText &bt = _blastTextQueue[_blastTextQueuePos - 1 - i];
+			bt.ypos -= diffY;
 		}
-	}
+	} else {
+		BlastText &bt = _blastTextQueue[_blastTextQueuePos++];
 
-	bt.xpos = x;
-	bt.ypos = y;
-	bt.color = color;
-	bt.charset = charset;
-	bt.center = center;
+		Common::strlcpy((char*)bt.text, (char*)textBuf, sizeof(textBuf));
+		bt.xpos = x;
+		bt.ypos = y;
+		bt.color = color;
+		bt.charset = charset;
+		bt.center = center;
+	}
 }
+#endif
 
 void ScummEngine_v6::drawBlastTexts() {
 	byte *buf;


Commit: eb276dcf48a0fb1adebee6e1ab02b635a2a00c98
    https://github.com/scummvm/scummvm/commit/eb276dcf48a0fb1adebee6e1ab02b635a2a00c98
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: Fix typo

Changed paths:
    engines/scumm/scumm_v7.h


diff --git a/engines/scumm/scumm_v7.h b/engines/scumm/scumm_v7.h
index 9cfe3cda1f7..a6623130d32 100644
--- a/engines/scumm/scumm_v7.h
+++ b/engines/scumm/scumm_v7.h
@@ -125,7 +125,7 @@ protected:
 
 	int getObjectIdFromOBIM(const byte *obim) override;
 
-	virtual void enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped = false) override;
+	void enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped = false) override;
 	void actorTalk(const byte *msg) override;
 	void translateText(const byte *text, byte *trans_buff) override;
 	void loadLanguageBundle() override;


Commit: addf810b9db7dab8fb9f50025c5a43c90169c6d1
    https://github.com/scummvm/scummvm/commit/addf810b9db7dab8fb9f50025c5a43c90169c6d1
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: Use getStringHeight() in v7-8 CHARSET_1() for pixel-perfect wrapping

Changed paths:
    engines/scumm/string.cpp


diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 32dc86764a8..7ec98707036 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -207,10 +207,10 @@ void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byt
 		int lastLineY = 0;
 		for (int i = 0; i < numLines; i++) {
 			BlastText &bt = _blastTextQueue[_blastTextQueuePos++];
-			Common::strlcpy((char*)bt.text, (char*)(textBuf + substrPos[i]), substrLen[i]);
+			Common::strlcpy((char *)bt.text, (char *)(textBuf + substrPos[i]), substrLen[i]);
 			bt.text[substrLen[0]] = '\0'; // Truncate the substring accordingly
 			bt.xpos = x;
-			bt.ypos = lastLineY = y + (_charset->getStringHeight((char*)textBuf) * i);
+			bt.ypos = lastLineY = y + (_charset->getStringHeight((char *)textBuf) * i);
 			bt.color = color;
 			bt.charset = charset;
 			bt.center = center;
@@ -242,7 +242,7 @@ void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byt
 	} else {
 		BlastText &bt = _blastTextQueue[_blastTextQueuePos++];
 
-		Common::strlcpy((char*)bt.text, (char*)textBuf, sizeof(textBuf));
+		Common::strlcpy((char *)bt.text, (char *)textBuf, sizeof(textBuf));
 		bt.xpos = x;
 		bt.ypos = y;
 		bt.color = color;
@@ -1074,7 +1074,7 @@ void ScummEngine_v7::CHARSET_1() {
 			if (subtitlePos.y < _screenHeight - 10) {
 				addSubtitleToQueue(subtitleBuffer + substring[i].pos, subtitlePos, _charsetColor, _charset->getCurID());
 			}
-			subtitlePos.y += _charset->getFontHeight();
+			subtitlePos.y += _charset->getStringHeight((char *)subtitleBuffer);
 		}
 	} else {
 		int code = 0;
@@ -1093,7 +1093,7 @@ void ScummEngine_v7::CHARSET_1() {
 				}
 				if (subtitlePos.y < _screenHeight - 10) {
 					addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID());
-					subtitlePos.y += _charset->getFontHeight();
+					subtitlePos.y += _charset->getStringHeight((char *)subtitleBuffer);
 				}
 				subtitleLine = subtitleBuffer;
 			} else {


Commit: d1764b52d056e996cca65ac75f4b7ff2293478a3
    https://github.com/scummvm/scummvm/commit/d1764b52d056e996cca65ac75f4b7ff2293478a3
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: COMI: Fix an oversight in enqueueText()

Changed paths:
    engines/scumm/string.cpp


diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 7ec98707036..e30fff4cea4 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -176,6 +176,9 @@ void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byt
 	convertMessageToString(text, textBuf, sizeof(textBuf));
 
 	if (_game.id == GID_CMI && wrapped) {
+		int of = _charset->getCurID();
+		_charset->setCurID(charset);
+
 		// HACK: Wrap the text when it's too long.
 		// The original does this by drawing word by word and adjusting x and y for each step of the pipeline.
 		// This, instead, is a mixture of code from SmushFont::drawStringWrap() and CHARSET_1(), which is just 
@@ -207,8 +210,8 @@ void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byt
 		int lastLineY = 0;
 		for (int i = 0; i < numLines; i++) {
 			BlastText &bt = _blastTextQueue[_blastTextQueuePos++];
-			Common::strlcpy((char *)bt.text, (char *)(textBuf + substrPos[i]), substrLen[i]);
-			bt.text[substrLen[0]] = '\0'; // Truncate the substring accordingly
+			Common::strlcpy((char *)bt.text, (char *)(textBuf + substrPos[i]), substrLen[i] + 1);
+			bt.text[substrLen[0] + 1] = '\0'; // Truncate the substring accordingly
 			bt.xpos = x;
 			bt.ypos = lastLineY = y + (_charset->getStringHeight((char *)textBuf) * i);
 			bt.color = color;
@@ -224,8 +227,6 @@ void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byt
 		// and then encounter a line that is out of bounds we can't move the whole block to the left or right any more (as we should).
 		// Possible TODO: moving the measuring logic contained in SmushFont::drawStringWrap() to ScummEngine_v6 to make it available here, too.
 
-		int of = _charset->getCurID();
-		_charset->setCurID(charset);
 		// Note that this won't detect (and measure correctly) 1-byte characters contained
 		// in 2-byte strings. For now, I trust that it won't happen with the object strings.
 		int clipHeight = _charset->getCharHeight(*textBuf) + 1;


Commit: 05980ff29425ec4e01bac82fca4e955e90e4a678
    https://github.com/scummvm/scummvm/commit/05980ff29425ec4e01bac82fca4e955e90e4a678
Author: athrxx (athrxx at scummvm.org)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: remove unneeded function

Changed paths:
    engines/scumm/scumm_v6.h
    engines/scumm/scumm_v7.h
    engines/scumm/string.cpp


diff --git a/engines/scumm/scumm_v6.h b/engines/scumm/scumm_v6.h
index b05a01f6d51..ec46d349a54 100644
--- a/engines/scumm/scumm_v6.h
+++ b/engines/scumm/scumm_v6.h
@@ -158,7 +158,6 @@ protected:
 	void useBompCursor(const byte *im, int w, int h);
 	void grabCursor(int x, int y, int w, int h);
 
-	virtual void enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped = false);
 	void drawBlastTexts();
 	void removeBlastTexts();
 
diff --git a/engines/scumm/scumm_v7.h b/engines/scumm/scumm_v7.h
index a6623130d32..14f2b4b2bf1 100644
--- a/engines/scumm/scumm_v7.h
+++ b/engines/scumm/scumm_v7.h
@@ -125,7 +125,7 @@ protected:
 
 	int getObjectIdFromOBIM(const byte *obim) override;
 
-	void enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped = false) override;
+	void enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped = false);
 	void actorTalk(const byte *msg) override;
 	void translateText(const byte *text, byte *trans_buff) override;
 	void loadLanguageBundle() override;
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index e30fff4cea4..949e0ba3444 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -144,19 +144,6 @@ void ScummEngine::showMessageDialog(const byte *msg) {
 #pragma mark --- V6 blast text queue code ---
 #pragma mark -
 
-
-void ScummEngine_v6::enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped) {
-	BlastText &bt = _blastTextQueue[_blastTextQueuePos++];
-	assert(_blastTextQueuePos <= ARRAYSIZE(_blastTextQueue));
-
-	convertMessageToString(text, bt.text, sizeof(bt.text));
-	bt.xpos = x;
-	bt.ypos = y;
-	bt.color = color;
-	bt.charset = charset;
-	bt.center = center;
-}
-
 #ifdef ENABLE_SCUMM_7_8
 void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped) {
 	assert(_blastTextQueuePos + 1 <= ARRAYSIZE(_blastTextQueue));
@@ -175,7 +162,7 @@ void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byt
 	byte textBuf[512];
 	convertMessageToString(text, textBuf, sizeof(textBuf));
 
-	if (_game.id == GID_CMI && wrapped) {
+	if (wrapped) {
 		int of = _charset->getCurID();
 		_charset->setCurID(charset);
 
@@ -219,16 +206,6 @@ void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byt
 			bt.center = center;
 		}
 
-		// HACK: This corrects the vertical placement of the object descriptions in COMI. The original text renderer does this in the same way
-		// we do in smush_font.cpp, lines 338 - 344: The dimensions of the whole block of text get measured first and then the necessary changes
-		// will be made before printing. Unfortunately, the way our ScummEngine_v7::CHARSET_1() is implemented we can't properly adjust
-		// the y postion there: If we have already printed several lines of text and then realize that we're getting out of bounds then it is too
-		// late, we can't move up the text we've already printed. The same applies to horizontal fixes: if we have already printed several lines
-		// and then encounter a line that is out of bounds we can't move the whole block to the left or right any more (as we should).
-		// Possible TODO: moving the measuring logic contained in SmushFont::drawStringWrap() to ScummEngine_v6 to make it available here, too.
-
-		// Note that this won't detect (and measure correctly) 1-byte characters contained
-		// in 2-byte strings. For now, I trust that it won't happen with the object strings.
 		int clipHeight = _charset->getCharHeight(*textBuf) + 1;
 		_charset->setCurID(of);
 		clipHeight = clipHeight + clipHeight / 2;


Commit: 7fe7b30459072eefbfc721ecdcc8e5ad4f4ea6cf
    https://github.com/scummvm/scummvm/commit/7fe7b30459072eefbfc721ecdcc8e5ad4f4ea6cf
Author: athrxx (athrxx at scummvm.org)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: (SCUMM5/FM-Towns) - minor cleanup

Changed paths:
    engines/scumm/charset.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 652ca4a9878..be478792aa3 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -2251,6 +2251,7 @@ void CharsetRendererNES::drawChar(int chr, Graphics::Surface &s, int x, int y) {
 #ifdef USE_RGB_COLOR
 #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
 CharsetRendererTownsClassic::CharsetRendererTownsClassic(ScummEngine *vm) : CharsetRendererClassic(vm), _sjisCurChar(0) {
+	assert(vm->_game.platform == Common::kPlatformFMTowns);
 }
 
 int CharsetRendererTownsClassic::getCharWidth(uint16 chr) {
@@ -2312,11 +2313,9 @@ void CharsetRendererTownsClassic::drawBitsN(const Graphics::Surface&, byte *dst,
 	assert(bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8);
 	bits = *src++;
 	numbits = 8;
-	byte *cmap = _vm->_charsetColorMap;
+	byte *cmap = _vm->_townsCharsetColorMap;
 	byte *dst2 = dst;
 
-	if (_vm->_game.platform == Common::kPlatformFMTowns)
-		cmap = _vm->_townsCharsetColorMap;
 	if (scale2x) {
 		dst2 += _vm->_textSurface.pitch;
 		pitch <<= 1;
@@ -2354,7 +2353,7 @@ bool CharsetRendererTownsClassic::prepareDraw(uint16 chr) {
 	processCharsetColors();
 	bool noSjis = false;
 
-	if (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_useCJKMode) {
+	if (_vm->_useCJKMode) {
 		if ((chr & 0x00ff) == 0x00fd) {
 			chr >>= 8;
 			noSjis = true;


Commit: 1f561327252e2e991c5102b48385fa40d1e076bd
    https://github.com/scummvm/scummvm/commit/1f561327252e2e991c5102b48385fa40d1e076bd
Author: athrxx (athrxx at scummvm.org)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: (SCUMM7/8) - reorganize font rendering - first part

(inspired by PR 3276 - this here has the desired effect, but actually allows the removal of hacks, workarounds and redundancy code instead of adding more of that sort)

The purpose is to have the same accurate font rendering that we already have in the Smush code also for the ingame texts. The original interpreters draw the text like that, so this is not a weirdo invention of mine.

This is still broken. The main purpose was to get as much code done as necessary to have it at least compile again and correctly run the Smush texts.

The rest still needs quite some work...

Changed paths:
  A engines/scumm/charset_v7.h
  A engines/scumm/string_v7.cpp
  A engines/scumm/string_v7.h
    engines/scumm/charset.cpp
    engines/scumm/charset.h
    engines/scumm/insane/insane.cpp
    engines/scumm/module.mk
    engines/scumm/nut_renderer.cpp
    engines/scumm/nut_renderer.h
    engines/scumm/scumm.cpp
    engines/scumm/scumm.h
    engines/scumm/scumm_v6.h
    engines/scumm/scumm_v7.h
    engines/scumm/smush/smush_font.h
    engines/scumm/smush/smush_player.cpp
    engines/scumm/string.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index be478792aa3..0a57a6db5ca 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -433,7 +433,7 @@ void CharsetRendererV3::setCurID(int32 id) {
 	}
 }
 
-int CharsetRendererCommon::getFontHeight() {
+int CharsetRendererCommon::getFontHeight() const {
 	if (_vm->_useCJKMode)
 		return MAX(_vm->_2byteHeight + 1, _fontHeight);
 	else
@@ -441,7 +441,7 @@ int CharsetRendererCommon::getFontHeight() {
 }
 
 // do spacing for variable width old-style font
-int CharsetRendererClassic::getCharWidth(uint16 chr) {
+int CharsetRendererClassic::getCharWidth(uint16 chr) const {
 	int spacing = 0;
 
 	if (_vm->_useCJKMode && chr >= 0x80)
@@ -454,7 +454,7 @@ int CharsetRendererClassic::getCharWidth(uint16 chr) {
 	return spacing;
 }
 
-int CharsetRenderer::getStringWidth(int arg, const byte *text, uint strLenMax) {
+int CharsetRenderer::getStringWidth(int arg, const byte *text) {
 	int pos = 0;
 	int width = 1;
 	int chr;
@@ -532,49 +532,6 @@ int CharsetRenderer::getStringWidth(int arg, const byte *text, uint strLenMax) {
 	return width;
 }
 
-int CharsetRenderer::getStringHeight(const char *str, uint numBytesMax) {
-	assert(str);
-	int totalHeight = 0;
-	int lineHeight = 0;
-
-	if (!numBytesMax)
-		return 0;
-
-	while (*str && numBytesMax) {
-		while (str[0] == '^') {
-			switch (str[1]) {
-			case 'f':
-				// We should change the font on the fly at this point
-				// which would result in a different width result.
-				// This has never been observed in the game though, and
-				// as such, we don't handle it.
-				str += 4;
-				break;
-			case 'c':
-				str += 5;
-				break;
-			default:
-				error("CharsetRenderer::getStringHeight(): Invalid escape code in text string");
-			}
-		}
-
-		if (*str == '\n') {
-			totalHeight += (lineHeight ? lineHeight : getFontHeight()) + 1;
-			lineHeight = 0;
-		} else if (*str != '\r' && *str != _vm->_newLineCharacter) {
-			lineHeight = MAX<int>(lineHeight, getCharHeight(*str));
-			if (is2ByteCharacter(_vm->_language, *str)) {
-				++str;
-				--numBytesMax;
-			}
-		}
-		++str;
-		--numBytesMax;
-	}
-
-	return totalHeight + (lineHeight ? lineHeight : getFontHeight()) + 1;
-}
-
 void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
 	int lastKoreanLineBreak = -1;
 	int origPos = pos;
@@ -715,7 +672,7 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
 	setCurID(oldID);
 }
 
-int CharsetRendererV3::getCharWidth(uint16 chr) {
+int CharsetRendererV3::getCharWidth(uint16 chr) const {
 	int spacing = 0;
 
 	if (_vm->_useCJKMode && (chr & 0x80))
@@ -1317,7 +1274,7 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co
 CharsetRendererTownsV3::CharsetRendererTownsV3(ScummEngine *vm) : CharsetRendererV3(vm), _sjisCurChar(0) {
 }
 
-int CharsetRendererTownsV3::getCharWidth(uint16 chr) {
+int CharsetRendererTownsV3::getCharWidth(uint16 chr) const {
 	if (_vm->isScummvmKorTarget()) {
 		return CharsetRendererV3::getCharWidth(chr);
 	}
@@ -1337,7 +1294,7 @@ int CharsetRendererTownsV3::getCharWidth(uint16 chr) {
 	return spacing;
 }
 
-int CharsetRendererTownsV3::getFontHeight() {
+int CharsetRendererTownsV3::getFontHeight() const {
 	if (_vm->isScummvmKorTarget()) {
 		return CharsetRendererV3::getFontHeight();
 	}
@@ -1699,7 +1656,7 @@ int CharsetRendererMac::getDrawWidthIntern(uint16 chr) {
 //       graphics resolution. But for font 1 in Indiana Jones and the Last
 //       crusade we want the actual dimensions for drawing the text boxes.
 
-int CharsetRendererMac::getFontHeight() {
+int CharsetRendererMac::getFontHeight() const {
 	int height = _macFonts[_curId].getFontHeight();
 
         // If we ever need the height for font 1 in Last Crusade (we don't at
@@ -1710,7 +1667,7 @@ int CharsetRendererMac::getFontHeight() {
 	return height;
 }
 
-int CharsetRendererMac::getCharWidth(uint16 chr) {
+int CharsetRendererMac::getCharWidth(uint16 chr) const {
 	int width = getDrawWidthIntern(chr);
 
 	// For font 1 in Last Crusade, we want the real width. It is used for
@@ -2008,8 +1965,25 @@ void CharsetRendererMac::setColor(byte color) {
 }
 
 #ifdef ENABLE_SCUMM_7_8
-CharsetRendererNut::CharsetRendererNut(ScummEngine *vm)
-	 : CharsetRenderer(vm) {
+int CharsetRendererV7::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) {
+	if (!prepareDraw(chr))
+		return 0;
+
+	VirtScreen &vs = _vm->_virtscr[kMainVirtScreen];
+	drawBits1(vs, x + vs.xstart, y, _charPtr, MAX<int>(clipRect.top, y), _width - 1, _height);
+	return _width;
+}
+
+int CharsetRendererV7::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) {
+	if (!prepareDraw(chr))
+		return 0;
+
+	VirtScreen &vs = _vm->_virtscr[kMainVirtScreen];
+	drawBitsN(vs, buffer + y * vs.pitch + x, _charPtr, _vm->_bytesPerPixel, y, _origWidth, _origHeight);
+	return _width;
+}
+
+CharsetRendererNut::CharsetRendererNut(ScummEngine *vm) : CharsetRenderer(vm) {
 	_current = 0;
 
 	for (int i = 0; i < 5; i++) {
@@ -2039,20 +2013,26 @@ void CharsetRendererNut::setCurID(int32 id) {
 	assert(_current);
 }
 
-int CharsetRendererNut::getCharHeight(byte chr) {
+int CharsetRendererNut::setFont(int id) {
+	int old = _curId;
+	if (id >= 0)
+		setCurID(id);
+	return old;
+}
+
+int CharsetRendererNut::getCharHeight(uint16 chr) const {
 	assert(_current);
-	return _current->getCharHeight(chr);
+	return _current->getCharHeight(chr & 0xFF);
 }
 
-int CharsetRendererNut::getCharWidth(uint16 chr) {
+int CharsetRendererNut::getCharWidth(uint16 chr) const {
 	assert(_current);
 	return _current->getCharWidth(chr);
 }
 
-int CharsetRendererNut::getFontHeight() {
-	// FIXME / TODO: how to implement this properly???
+int CharsetRendererNut::getFontHeight() const {
 	assert(_current);
-	return _current->getCharHeight('|');
+	return _current->getFontHeight();
 }
 
 int CharsetRendererNut::getStringWidth(int arg, const byte *text, uint strLenMax) {
@@ -2102,7 +2082,7 @@ int CharsetRendererNut::getStringWidth(int arg, const byte *text, uint strLenMax
 }
 
 void CharsetRendererNut::printChar(int chr, bool ignoreCharsetMask) {
-	Common::Rect shadow;
+	/*Common::Rect shadow;
 
 	assert(_current);
 	if (chr == '@')
@@ -2148,10 +2128,11 @@ void CharsetRendererNut::printChar(int chr, bool ignoreCharsetMask) {
 		drawTop -= _vm->_screenTop;
 	}
 
+	Common::Rect clipRect(s.w, s.h);
 	if (chr >= 256 && _vm->_useCJKMode)
-		_current->draw2byte(s, chr, _left, drawTop, _color);
+		_current->draw2byte((uint8*)s.getBasePtr(0, 0), clipRect, _left, drawTop, s.pitch, _color, chr);
 	else
-		_current->drawChar(s, (byte)chr, _left, drawTop, _color);
+		_current->drawChar((uint8*)s.getBasePtr(0, 0), clipRect, _left, drawTop, s.pitch, _color, (byte)chr);
 	_vm->markRectAsDirty(kMainVirtScreen, shadow);
 
 	if (_str.left > _left)
@@ -2167,7 +2148,17 @@ void CharsetRendererNut::printChar(int chr, bool ignoreCharsetMask) {
 		_str.right = shadow.right;
 
 	if (_str.bottom < shadow.bottom)
-		_str.bottom = shadow.bottom;
+		_str.bottom = shadow.bottom;*/
+}
+
+int CharsetRendererNut::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) {
+	assert(_current);
+	return _current->draw2byte(buffer, clipRect, x, y, pitch, col, chr);
+}
+
+int CharsetRendererNut::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) {
+	assert(_current);
+	return _current->drawChar(buffer, clipRect, x, y, pitch, col, chr);
 }
 #endif
 
@@ -2254,7 +2245,7 @@ CharsetRendererTownsClassic::CharsetRendererTownsClassic(ScummEngine *vm) : Char
 	assert(vm->_game.platform == Common::kPlatformFMTowns);
 }
 
-int CharsetRendererTownsClassic::getCharWidth(uint16 chr) {
+int CharsetRendererTownsClassic::getCharWidth(uint16 chr) const {
 	int spacing = 0;
 
 	if (_vm->_useCJKMode) {
@@ -2286,7 +2277,7 @@ int CharsetRendererTownsClassic::getCharWidth(uint16 chr) {
 	return spacing;
 }
 
-int CharsetRendererTownsClassic::getFontHeight() {
+int CharsetRendererTownsClassic::getFontHeight() const {
 	static const uint8 sjisFontHeightM1[] = { 0, 8, 9, 8, 9, 8, 9, 0, 0, 0 };
 	static const uint8 sjisFontHeightM2[] = { 0, 8, 9, 9, 9, 8, 9, 9, 9, 8 };
 	static const uint8 sjisFontHeightI4[] = { 0, 8, 9, 9, 9, 8, 8, 8, 8, 8 };
@@ -2408,7 +2399,7 @@ void CharsetRendererTownsClassic::setupShadowMode() {
 	_vm->_cjkFont->toggleFlippedMode((_vm->_game.id == GID_MONKEY || _vm->_game.id == GID_MONKEY2) && _curId == 3);
 }
 
-bool CharsetRendererTownsClassic::useFontRomCharacter(uint16 chr) {
+bool CharsetRendererTownsClassic::useFontRomCharacter(uint16 chr) const {
 	if (!_vm->_useCJKMode)
 		return false;
 
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index 0a19a2ddba3..f013be8984a 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -26,6 +26,7 @@
 #include "common/rect.h"
 #include "graphics/fonts/macfont.h"
 #include "graphics/sjis.h"
+#include "scumm/charset_v7.h"
 #include "scumm/scumm.h"
 #include "scumm/gfx.h"
 
@@ -96,17 +97,16 @@ public:
 	virtual void printChar(int chr, bool ignoreCharsetMask) = 0;
 	virtual void drawChar(int chr, Graphics::Surface &s, int x, int y) {}
 
-	virtual int getStringWidth(int arg, const byte *text uint strLenMax = 100000);
-	int getStringHeight(const char *str, uint strLenMax = 100000);
+	virtual int getStringWidth(int arg, const byte *text);
 	void addLinebreaks(int a, byte *str, int pos, int maxwidth);
 	void translateColor();
 
 	virtual void setCurID(int32 id) = 0;
 	int getCurID() { return _curId; }
 
-	virtual int getFontHeight() = 0;
-	virtual int getCharHeight(byte chr) { return getFontHeight(); }
-	virtual int getCharWidth(uint16 chr) = 0;
+	virtual int getFontHeight() const = 0;
+	virtual int getCharHeight(uint16 chr) const { return getFontHeight(); }
+	virtual int getCharWidth(uint16 chr) const = 0;
 
 	virtual void setColor(byte color) { _color = color; translateColor(); }
 
@@ -128,7 +128,7 @@ public:
 
 	void setCurID(int32 id) override;
 
-	int getFontHeight() override;
+	int getFontHeight() const override;
 };
 
 class CharsetRendererPC : public CharsetRendererCommon {
@@ -168,7 +168,7 @@ public:
 	void printChar(int chr, bool ignoreCharsetMask) override;
 	void drawChar(int chr, Graphics::Surface &s, int x, int y) override;
 
-	int getCharWidth(uint16 chr) override;
+	int getCharWidth(uint16 chr) const override;
 };
 
 #ifdef USE_RGB_COLOR
@@ -177,14 +177,14 @@ class CharsetRendererTownsClassic : public CharsetRendererClassic {
 public:
 	CharsetRendererTownsClassic(ScummEngine *vm);
 
-	int getCharWidth(uint16 chr) override;
-	int getFontHeight() override;
+	int getCharWidth(uint16 chr) const override;
+	int getFontHeight() const override;
 
 private:
 	void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height) override;
 	bool prepareDraw(uint16 chr) override;
 	void setupShadowMode();
-	bool useFontRomCharacter(uint16 chr);
+	bool useFontRomCharacter(uint16 chr) const;
 	void processCharsetColors();
 
 	uint16 _sjisCurChar;
@@ -205,8 +205,8 @@ public:
 	void printChar(int chr, bool ignoreCharsetMask) override;
 	void drawChar(int chr, Graphics::Surface &s, int x, int y) override;
 
-	int getFontHeight() override { return 8; }
-	int getCharWidth(uint16 chr) override { return 8; }
+	int getFontHeight() const override { return 8; }
+	int getCharWidth(uint16 chr) const override { return 8; }
 };
 
 class CharsetRendererV3 : public CharsetRendererPC {
@@ -224,15 +224,15 @@ public:
 	void drawChar(int chr, Graphics::Surface &s, int x, int y) override;
 	void setCurID(int32 id) override;
 	void setColor(byte color) override;
-	int getCharWidth(uint16 chr) override;
+	int getCharWidth(uint16 chr) const override;
 };
 
 class CharsetRendererTownsV3 : public CharsetRendererV3 {
 public:
 	CharsetRendererTownsV3(ScummEngine *vm);
 
-	int getCharWidth(uint16 chr) override;
-	int getFontHeight() override;
+	int getCharWidth(uint16 chr) const override;
+	int getFontHeight() const override;
 
 private:
 	void enableShadow(bool enable) override;
@@ -272,7 +272,7 @@ public:
 	~CharsetRendererV2() override;
 
 	void setCurID(int32 id) override {}
-	int getCharWidth(uint16 chr) override { return 8; }
+	int getCharWidth(uint16 chr) const override { return 8; }
 };
 
 class CharsetRendererMac : public CharsetRendererCommon {
@@ -299,20 +299,29 @@ public:
 
 	void setCurID(int32 id) override;
 
-	int getStringWidth(int arg, const byte *text, uint strLenMax = 100000) override;
-	int getFontHeight() override;
-	int getCharWidth(uint16 chr) override;
+	int getStringWidth(int arg, const byte *text) override;
+	int getFontHeight() const override;
+	int getCharWidth(uint16 chr) const override;
 	void printChar(int chr, bool ignoreCharsetMask) override;
 	void drawChar(int chr, Graphics::Surface &s, int x, int y) override;
 	void setColor(byte color) override;
 };
 
 #ifdef ENABLE_SCUMM_7_8
-class CharsetRendererNut : public CharsetRenderer {
-protected:
-	NutRenderer *_fr[5];
-	NutRenderer *_current;
+class CharsetRendererV7 : public CharsetRendererClassic, public GlyphRenderer_v7 {
+public:
+	CharsetRendererV7(ScummEngine *vm) : CharsetRendererClassic(vm) {}
+	~CharsetRendererV7() override {};
+
+	int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) override;
+	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) override;
+	int getCharWidth(uint16 chr) const override { return CharsetRendererClassic::getCharWidth(chr);	}
+	int getCharHeight(uint16 chr) const override { return ((chr & 0x80) && _vm->_useCJKMode) ? _vm->_2byteHeight + 1 : _fontHeight; }
+	int getFontHeight() const override { return _fontHeight; }
+	int setFont(int) override { return 0; }
+};
 
+class CharsetRendererNut : public CharsetRenderer, public GlyphRenderer_v7 {
 public:
 	CharsetRendererNut(ScummEngine *vm);
 	~CharsetRendererNut() override;
@@ -320,11 +329,18 @@ public:
 	void printChar(int chr, bool ignoreCharsetMask) override;
 
 	void setCurID(int32 id) override;
+	int setFont(int id) override;
 
-	int getStringWidth(int arg, const byte *text, uint strLenMax = 1000000) override;
-	int getFontHeight() override;
-	int getCharHeight(byte chr) override;
-	int getCharWidth(uint16 chr) override;
+	int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) override;
+	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) override;
+
+	int getFontHeight() const override;
+	int getCharWidth(uint16 chr) const override;
+	int getCharHeight(uint16 chr) const override;
+
+private:
+	NutRenderer *_fr[5];
+	NutRenderer *_current;
 };
 #endif
 
diff --git a/engines/scumm/charset_v7.h b/engines/scumm/charset_v7.h
new file mode 100644
index 00000000000..8ecdefb39e3
--- /dev/null
+++ b/engines/scumm/charset_v7.h
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SCUMM_CHARSET_V7_H
+#define SCUMM_CHARSET_V7_H
+
+#ifdef ENABLE_SCUMM_7_8
+
+#include "common/rect.h"
+
+namespace Scumm {
+
+class GlyphRenderer_v7 {
+public:
+	virtual int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) = 0;
+	virtual int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) = 0;
+	virtual int getCharWidth(uint16 chr) const = 0;
+	virtual int getCharHeight(uint16 chr) const = 0;
+	virtual int getFontHeight() const = 0;
+	virtual int setFont(int id) = 0;
+};
+
+} // End of namespace Scumm
+
+#endif
+#endif
diff --git a/engines/scumm/insane/insane.cpp b/engines/scumm/insane/insane.cpp
index 4e602691289..04ffa710c02 100644
--- a/engines/scumm/insane/insane.cpp
+++ b/engines/scumm/insane/insane.cpp
@@ -1293,7 +1293,6 @@ void Insane::smlayer_showStatusMsg(int32 arg_0, byte *renderBitmap, int32 codecp
 	}
 
 	assert(sf != NULL);
-	sf->setColor(color);
 
 	// flags:
 	// bit 0 - center                  0x01
@@ -1303,10 +1302,10 @@ void Insane::smlayer_showStatusMsg(int32 arg_0, byte *renderBitmap, int32 codecp
 	// bit 4 - fill background         0x10
 	if ((flags & 4) || _vm->_language == Common::HE_ISR) {
 		Common::Rect clipRect(0, 0, _player->_width, _player->_height);
-		sf->drawStringWrap(str, renderBitmap, clipRect, pos_x, pos_y, flags & 1);
+		sf->drawStringWrap(str, renderBitmap, clipRect, pos_x, pos_y, color, flags & 1);
 	} else {
 		Common::Rect clipRect(10, 0, 310, _player->_height);
-		sf->drawString(str, renderBitmap, clipRect, pos_x, pos_y, flags & 1);
+		sf->drawString(str, renderBitmap, clipRect, pos_x, pos_y, color, flags & 1);
 	}
 
 
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 43ec1e2864f..31fafa428f4 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -87,6 +87,7 @@ endif
 ifdef ENABLE_SCUMM_7_8
 MODULE_OBJS += \
 	nut_renderer.o \
+	string_v7.o \
 	script_v8.o \
 	imuse_digi/dimuse_bndmgr.o \
 	imuse_digi/dimuse_codecs.o \
@@ -118,8 +119,7 @@ MODULE_OBJS += \
 	smush/codec47.o \
 	smush/smush_player.o \
 	smush/saud_channel.o \
-	smush/smush_mixer.o \
-	smush/smush_font.o
+	smush/smush_mixer.o
 
 ifdef USE_ARM_SMUSH_ASM
 MODULE_OBJS += \
diff --git a/engines/scumm/nut_renderer.cpp b/engines/scumm/nut_renderer.cpp
index f7e77281427..f70a36df8aa 100644
--- a/engines/scumm/nut_renderer.cpp
+++ b/engines/scumm/nut_renderer.cpp
@@ -353,6 +353,137 @@ void NutRenderer::drawFrame(byte *dst, int c, int x, int y) {
 	}
 }
 
+int NutRenderer::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr, bool hardcodedColors, bool smushColorMode) {
+	int width = MIN((int)_chars[chr].width, clipRect.right - x);
+	int height = MIN((int)_chars[chr].height, clipRect.bottom - y);
+	int minX = x < clipRect.left ? clipRect.left - x : 0;
+	int minY = y < clipRect.top ? clipRect.top - y : 0;
+	const byte *src = unpackChar(chr);
+	byte *dst = buffer + pitch * y + x;
+
+	if (width <= 0 || height <= 0)
+		return 0;
+
+	if (minY) {
+		src += minY * _chars[chr].width;
+		dst += minY * pitch;
+	}
+
+	char color = (col != -1) ? col : 1;
+
+	if (_vm->_game.version == 7) {
+		if (hardcodedColors) {
+			for (int j = minY; j < height; j++) {
+				for (int i = minX; i < width; i++) {
+					int8 value = *src++;
+					if (value != _chars[chr].transparency)
+						dst[i] = value;
+				}
+				dst += pitch;
+			}
+		} else {
+			for (int j = minY; j < height; j++) {
+				for (int i = minX; i < width; i++) {
+					int8 value = *src++;
+					if (value == 1)
+						dst[i] = color;
+					else if (value != _chars[chr].transparency)
+						dst[i] = 0;
+				}
+				dst += pitch;
+			}
+		}
+	} else {
+		if (smushColorMode) {
+			for (int j = minY; j < height; j++) {
+				for (int i = minX; i < width; i++) {
+					int8 value = *src++;
+					if (value == -color)
+						dst[i] = 0xFF;
+					else if (value == -31)
+						dst[i] = 0;
+					else if (value != _chars[chr].transparency)
+						dst[i] = value;
+				}
+				dst += pitch;
+			}
+		} else {
+			for (int j = minY; j < height; j++) {
+				for (int i = minX; i < width; i++) {
+					int8 value = *src++;
+					if (value != _chars[chr].transparency)
+						dst[i] = (value == 1) ? color : value;
+				}
+				dst += pitch;
+			}
+		}		
+	}
+	return width;
+}
+
+int NutRenderer::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) {
+	int width = MIN((int)_vm->_2byteWidth, clipRect.right - x);
+	int height = MIN((int)_vm->_2byteHeight, clipRect.bottom - y);
+	int minX = x < clipRect.left ? clipRect.left - x : 0;
+	int minY = y < clipRect.top ? clipRect.top - y : 0;
+
+	if (width <= 0 || height <= 0)
+		return 0;
+
+	const byte *src = _vm->get2byteCharPtr(chr);
+	byte bits = 0;
+
+	if (width <= 0 || height <= 0)
+		return 0;
+
+	if (minY) {
+		src += minY * _vm->_2byteWidth;
+		buffer += minY * pitch;
+	}
+
+	enum ShadowMode {
+		kNone,
+		kNormalShadowMode,
+		kCJKv7ShadowMode,
+		kCJKv8ShadowMode
+	};
+
+	ShadowMode shadowMode = _vm->_useCJKMode ? (_vm->_game.version == 8 ? kCJKv8ShadowMode : kCJKv7ShadowMode) : kNone;
+
+	int shadowOffsetXTable[4] = { -1, 0, 1, 0 };
+	int shadowOffsetYTable[4] = { 0, 1, 0, 0 };
+	int shadowOffsetColorTable[4] = { 0, 0, 0, col };
+
+	const byte *origSrc = src;
+	for (int shadowIdx = (shadowMode == kCJKv8ShadowMode) ? 0 : (shadowMode == kCJKv7ShadowMode ? 2 : 3); shadowIdx < 4; shadowIdx++) {
+		int offX = MAX<int>(x + shadowOffsetXTable[shadowIdx], clipRect.left);
+		int offY = MAX<int>(y + shadowOffsetYTable[shadowIdx], clipRect.top);
+		byte drawColor = shadowOffsetColorTable[shadowIdx];
+
+		src = origSrc;
+		byte *dst = buffer + pitch * offY + offX;
+
+		for (int j = minY; j < height; j++) {
+			for (int i = minX; i < width; i++) {
+				if (offX + i < 0)
+					continue;
+				if ((i % 8) == 0)
+					bits = *src++;
+				if (bits & revBitMask(i % 8)) {
+					if (shadowMode == kNormalShadowMode) {
+						dst[i + 1] = 0;
+						dst[pitch + i] = 0;
+						dst[pitch + i + 1] = 0;
+					}
+					dst[i] = drawColor;
+				}
+			}
+			dst += pitch;
+		}
+	}
+	return width + 1;
+}
+/*
 void NutRenderer::drawChar(const Graphics::Surface &s, byte c, int x, int y, byte color) {
 	// FIXME: This gets passed a const destination Surface. Intuitively this
 	// should never get written to. But sadly it does... For now we simply
@@ -431,6 +562,6 @@ void NutRenderer::draw2byte(const Graphics::Surface &s, int c, int x, int y, byt
 			dst += s.pitch;
 		}
 	}
-}
+}*/
 
 } // End of namespace Scumm
diff --git a/engines/scumm/nut_renderer.h b/engines/scumm/nut_renderer.h
index fc843d27f7a..2bd0077a675 100644
--- a/engines/scumm/nut_renderer.h
+++ b/engines/scumm/nut_renderer.h
@@ -46,6 +46,7 @@ protected:
 	byte *_paletteMap;
 	byte _bpp;
 	byte _palette[16];
+
 	struct {
 		uint16 width;
 		uint16 height;
@@ -65,11 +66,13 @@ public:
 	int getNumChars() const { return _numChars; }
 
 	void drawFrame(byte *dst, int c, int x, int y);
-	void drawChar(const Graphics::Surface &s, byte c, int x, int y, byte color);
-	void draw2byte(const Graphics::Surface &s, int c, int x, int y, byte color);
+	int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr);
+	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr, bool hardcodedColors = false, bool smushColorMode = false);
 
 	int getCharWidth(byte c) const;
 	int getCharHeight(byte c) const;
+
+	int getFontHeight() const { return _fontHeight; }
 };
 
 } // End of namespace Scumm
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index f3833b69b2a..18bfca0fe2e 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -71,6 +71,7 @@
 #include "scumm/scumm_v0.h"
 #include "scumm/scumm_v8.h"
 #include "scumm/sound.h"
+#include "scumm/string_v7.h"
 #include "scumm/imuse/sysex.h"
 #include "scumm/he/localizer.h"
 #include "scumm/he/sprite_he.h"
@@ -1074,6 +1075,8 @@ ScummEngine_v7::ScummEngine_v7(OSystem *syst, const DetectorResult &dr)
 	_languageIndex = NULL;
 	clearSubtitleQueue();
 
+	_textV7 = NULL;
+
 	_game.features |= GF_NEW_COSTUMES;
 }
 
@@ -1088,6 +1091,7 @@ ScummEngine_v7::~ScummEngine_v7() {
 	}
 
 	delete _insane;
+	delete _textV7;
 
 	free(_languageBuffer);
 	free(_languageIndex);
@@ -1715,8 +1719,14 @@ void ScummEngine::setupCharsetRenderer(const Common::String &macFontFile) {
 		else
 			_charset = new CharsetRendererV3(this);
 #ifdef ENABLE_SCUMM_7_8
+	} else if (_game.version == 7) {
+		CharsetRendererV7 *c7 = new CharsetRendererV7(this);
+		_charset = c7;
+		createTextRenderer(c7);
 	} else if (_game.version == 8) {
-		_charset = new CharsetRendererNut(this);
+		CharsetRendererNut *c8 = new CharsetRendererNut(this);
+		_charset = c8;
+		createTextRenderer(c8);
 #endif
 	} else {
 #ifdef USE_RGB_COLOR
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 74654fe5fbf..d242e64c17f 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -94,6 +94,7 @@ class ScummEngine;
 class ScummDebugger;
 class Sound;
 class Localizer;
+class GlyphRenderer_v7;
 
 struct Box;
 struct BoxCoords;
@@ -1218,6 +1219,8 @@ private:
 
 	const byte *searchTranslatedLine(const byte *text, const TranslationRange &range, bool useIndex);
 
+	virtual void createTextRenderer(GlyphRenderer_v7 *gr) {}
+
 public:
 
 	/* Scumm Vars */
diff --git a/engines/scumm/scumm_v6.h b/engines/scumm/scumm_v6.h
index ec46d349a54..7aa95167478 100644
--- a/engines/scumm/scumm_v6.h
+++ b/engines/scumm/scumm_v6.h
@@ -87,11 +87,13 @@ protected:
 	struct BlastText : TextObject {
 		Common::Rect rect;
 		bool center;
+		bool wrap;
 
 		void clear() {
 			this->TextObject::clear();
 			rect = Common::Rect();
 			center = false;
+			wrap = false;
 		}
 	};
 
@@ -158,7 +160,7 @@ protected:
 	void useBompCursor(const byte *im, int w, int h);
 	void grabCursor(int x, int y, int w, int h);
 
-	void drawBlastTexts();
+	virtual void drawBlastTexts();
 	void removeBlastTexts();
 
 	void enqueueObject(int objectNumber, int objectX, int objectY, int objectWidth,
diff --git a/engines/scumm/scumm_v7.h b/engines/scumm/scumm_v7.h
index 14f2b4b2bf1..cf5f0bc4fe6 100644
--- a/engines/scumm/scumm_v7.h
+++ b/engines/scumm/scumm_v7.h
@@ -31,6 +31,7 @@ namespace Scumm {
 class Insane;
 class SmushMixer;
 class SmushPlayer;
+class TextRenderer_v7;
 
 class ScummEngine_v7 : public ScummEngine_v6 {
 	friend class SmushPlayer;
@@ -64,6 +65,7 @@ public:
 	};
 
 protected:
+	TextRenderer_v7 *_textV7;
 	int _verbLineSpacing;
 	bool _existLanguageFile;
 	char *_languageBuffer;
@@ -125,7 +127,9 @@ protected:
 
 	int getObjectIdFromOBIM(const byte *obim) override;
 
+	void createTextRenderer(GlyphRenderer_v7 *gr) override;
 	void enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped = false);
+	void drawBlastTexts() override;
 	void actorTalk(const byte *msg) override;
 	void translateText(const byte *text, byte *trans_buff) override;
 	void loadLanguageBundle() override;
@@ -135,7 +139,6 @@ protected:
 
 	void pauseEngineIntern(bool pause) override;
 
-
 	void o6_kernelSetFunctions() override;
 };
 
diff --git a/engines/scumm/smush/smush_font.h b/engines/scumm/smush/smush_font.h
index 18f63ea2fe7..d4eac2ce291 100644
--- a/engines/scumm/smush/smush_font.h
+++ b/engines/scumm/smush/smush_font.h
@@ -24,38 +24,44 @@
 
 #include "common/scummsys.h"
 #include "scumm/nut_renderer.h"
+#include "scumm/scumm.h"
+#include "scumm/string_v7.h"
 
 namespace Scumm {
 
-class SmushFont : public NutRenderer {
-protected:
-	int16 _color;
-	bool _new_colors;
-	bool _original;
+class SmushFont : public NutRenderer, public GlyphRenderer_v7 {
+public:
+	SmushFont(ScummEngine *vm, const char *filename, bool useOriginalColors) :
+		NutRenderer(vm, filename), _hardcodedFontColors(useOriginalColors) {
+		_r = new TextRenderer_v7(vm, this);
+	}
 
+	~SmushFont() override {	delete _r;}
 
-	int getStringWidth(const char *str, uint numBytesMax);
-	int getStringHeight(const char *str, uint numBytesMax);
-	int draw2byte(byte *buffer, int dst_width, int x, int y, int idx);
-	int drawChar(byte *buffer, int dst_width, int x, int y, byte chr);
-	void drawSubstring(const char *str, uint numBytesMax, byte *buffer, int dst_width, int x, int y);
+	void drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int16 col, bool center) {
+		_r->drawString(str, buffer, clipRect, x, y, _vm->_screenWidth, col, center);
+	}
 
-public:
-	SmushFont(ScummEngine *vm, const char *filename, bool use_original_colors, bool new_colors);
-
-	void setColor(byte c) { _color = c; }
-	void drawString    (const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, bool center);
-	void drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, bool center);
-
-	static inline bool is2ByteCharacter(Common::Language lang, byte c) {
-		if (lang == Common::JA_JPN)
-			return (c >= 0x80 && c <= 0x9F) || (c >= 0xE0 && c <= 0xFD);
-		else if (lang == Common::KO_KOR)
-			return (c >= 0xB0 && c <= 0xD0);
-		else if (lang == Common::ZH_TWN || lang == Common::ZH_CHN)
-			return (c >= 0x80);
-		return false;
+	void drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int16 col, bool center) {
+		_r->drawStringWrap(str, buffer, clipRect, x, y, _vm->_screenWidth, col, center);
+	}
+
+private:
+	int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) override {
+		return NutRenderer::draw2byte(buffer, clipRect, x, y, pitch, _vm->_game.id == GID_CMI ? 255 : (_vm->_game.id == GID_DIG && col == -1 ? 1 : col), chr);
+	}
+
+	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) override {
+		return NutRenderer::drawChar(buffer, clipRect, x, y, pitch, col, chr, _hardcodedFontColors, true);
 	}
+
+	int getCharWidth(uint16 chr) const override { return NutRenderer::getCharWidth(chr & 0xFF); }
+	int getCharHeight(uint16 chr) const override { return NutRenderer::getCharHeight(chr & 0xFF); }
+	int getFontHeight() const override { return NutRenderer::getFontHeight(); }
+	int setFont(int) override { return 0; }
+
+	TextRenderer_v7 *_r;
+	const bool _hardcodedFontColors;
 };
 
 } // End of namespace Scumm
diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp
index e86f9bddf92..40c29c78613 100644
--- a/engines/scumm/smush/smush_player.cpp
+++ b/engines/scumm/smush/smush_player.cpp
@@ -627,6 +627,7 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 			string2[0] = 0;
 	}
 
+	const char *str2 = str;
 	while (str[0] == '^') {
 		switch (str[1]) {
 		case 'f':
@@ -645,7 +646,7 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 			error("invalid escape code in text string");
 		}
 	}
-
+	str = str2;
 	// This is a hack from the original COMI CJK interpreter. Its purpose is to avoid
 	// ugly combinations of two byte characters (rendered with the respective special
 	// font) and standard one byte (NUT font) characters (see bug #11947).
@@ -655,6 +656,13 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 	}
 
 	SmushFont *sf = getFont(fontId);
+	assert(sf != NULL);
+
+
+	// The HACK that used to be here to prevent bug #2220 is no longer necessary and
+	// has been removed. The font renderer can handle all ^codes it encounters (font
+	// changes on the fly will be ignored for Smush texts, since our code design does
+	// not permit it and the feature isn't used anyway).
 
 	// HACK. This is to prevent bug #2220. In updated Win95 dig
 	// there is such line:
@@ -672,9 +680,9 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 	// We just strip that off and assume that neither font
 	// nor font color was altered. Proper fix would be to feed
 	// drawString() with each line sequentally
-	char *string3 = NULL, *sptr2;
+/*	char *string3 = NULL, *sptr2;
 	const char *sptr;
-
+	
 	if (strchr(str, '^')) {
 		string3 = (char *)malloc(strlen(str) + 1);
 
@@ -691,17 +699,14 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 					error("invalid escape code in text string");
 				}
 			} else {
-				if (SmushFont::is2ByteCharacter(_vm->_language, *sptr))
+				if (TextRenderer_v7::is2ByteCharacter(_vm->_language, *sptr))
 					*sptr2++ = *sptr++;
 				*sptr2++ = *sptr++;
 			}
 		}
 		*sptr2++ = *sptr++; // copy zero character
 		str = string3;
-	}
-
-	assert(sf != NULL);
-	sf->setColor(color);
+	}*/
 
 	if (_vm->_game.id == GID_CMI && string2[0] != 0) {
 		str = string2;
@@ -729,16 +734,16 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 			height = _height - 20;
 		}
 		Common::Rect clipRect(MAX<int>(0, left), MAX<int>(0, top), MIN<int>(left + width, _width), MIN<int>(top + height, _height));
-		sf->drawStringWrap(str, _dst, clipRect, pos_x, pos_y, flags & 1);
+		sf->drawStringWrap(str, _dst, clipRect, pos_x, pos_y, color, flags & 1);
 	} else {
 		// Similiar to the wrapped text, COMI will pass on rect coords here, which will later be lost. Unlike with the wrapped text, it will
 		// finally use the full screen dimenstions. SCUMM7 renders directly from here (see comment above), but also with the full screen.
 		Common::Rect clipRect(0, 0, _width, _height);
-		sf->drawString(str, _dst, clipRect, pos_x, pos_y, flags & 1);
+		sf->drawString(str, _dst, clipRect, pos_x, pos_y, color, flags & 1);
 	}
 
 	free(string);
-	free(string3);
+	//free(string3);
 }
 
 const char *SmushPlayer::getString(int id) {
@@ -1039,23 +1044,13 @@ SmushFont *SmushPlayer::getFont(int font) {
 
 			assert(font >= 0 && font < ARRAYSIZE(ft_fonts));
 
-			_sf[font] = new SmushFont(_vm, ft_fonts[font], true, false);
-		}
-	} else if (_vm->_game.id == GID_DIG) {
-		if (!(_vm->_game.features & GF_DEMO)) {
-			assert(font >= 0 && font < 4);
-
-			sprintf(file_font, "font%d.nut", font);
-			_sf[font] = new SmushFont(_vm, file_font, font != 0, false);
+			_sf[font] = new SmushFont(_vm, ft_fonts[font], true);
 		}
-	} else if (_vm->_game.id == GID_CMI) {
-		int numFonts = (_vm->_game.features & GF_DEMO) ? 4 : 5;
+	} else {
+		int numFonts = (_vm->_game.id == GID_CMI && !(_vm->_game.features & GF_DEMO)) ? 5 : 4;
 		assert(font >= 0 && font < numFonts);
-
 		sprintf(file_font, "font%d.nut", font);
-		_sf[font] = new SmushFont(_vm, file_font, false, true);
-	} else {
-		error("SmushPlayer::getFont() Unknown font setup for game");
+		_sf[font] = new SmushFont(_vm, file_font, _vm->_game.id == GID_DIG && font != 0);
 	}
 
 	assert(_sf[font]);
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 949e0ba3444..cc37c7a72ce 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -36,7 +36,7 @@
 #include "scumm/resource.h"
 #include "scumm/scumm.h"
 #include "scumm/scumm_v6.h"
-#include "scumm/scumm_v8.h"
+#include "scumm/scumm_v7.h"
 #include "scumm/verbs.h"
 #include "scumm/he/sound_he.h"
 
@@ -88,17 +88,6 @@ void ScummEngine::printString(int m, const byte *msg) {
 	}
 }
 
-#ifdef ENABLE_SCUMM_7_8
-void ScummEngine_v8::printString(int m, const byte *msg) {
-	if (m == 4) {
-		const StringTab &st = _string[m];
-		enqueueText(msg, st.xpos, st.ypos, st.color, st.charset, st.center, st.wrapping);
-	} else {
-		ScummEngine::printString(m, msg);
-	}
-}
-#endif
-
 void ScummEngine::debugMessage(const byte *msg) {
 	byte buffer[500];
 	convertMessageToString(msg, buffer, sizeof(buffer));
@@ -145,89 +134,7 @@ void ScummEngine::showMessageDialog(const byte *msg) {
 #pragma mark -
 
 #ifdef ENABLE_SCUMM_7_8
-void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped) {
-	assert(_blastTextQueuePos + 1 <= ARRAYSIZE(_blastTextQueue));
-
-	if (_useCJKMode) {
-		// The Dig expressly checks for x == 160 && y == 189 && charset == 3. Usually, if the game wants to print CJK text at the bottom
-		// of the screen it will use y = 183. So maybe this is a hack to fix some script texts that weren forgotten in the CJK converting
-		// process.
-		if (_game.id == GID_DIG && x == 160 && y == 189 && charset == 3)
-			y -= 6;
-		// COMI always adds a y-offset of 2 in CJK mode.
-		if (_game.id == GID_CMI)
-			y += 2;
-	}
-
-	byte textBuf[512];
-	convertMessageToString(text, textBuf, sizeof(textBuf));
-
-	if (wrapped) {
-		int of = _charset->getCurID();
-		_charset->setCurID(charset);
-
-		// HACK: Wrap the text when it's too long.
-		// The original does this by drawing word by word and adjusting x and y for each step of the pipeline.
-		// This, instead, is a mixture of code from SmushFont::drawStringWrap() and CHARSET_1(), which is just 
-		// enough to split the full line into more smaller lines, which are treated as different blastText in 
-		// the queue. Anyway this shouldn't generate more than 2 lines total but it's generalized just in case.
-		_charset->addLinebreaks(0, textBuf, 0, _screenWidth - 20);
-		int16 substrPos[200];
-		memset(substrPos, 0, sizeof(substrPos));
-		int16 substrLen[200];
-		memset(substrLen, 0, sizeof(substrLen));
-
-		int len = (int)Common::strnlen((char *)textBuf, sizeof(textBuf));
-		int curPos = -1;
-		int numLines = 1;
-
-		// Save splitting information (position of the substring and its length)
-		while (curPos <= len) {
-			if (textBuf[curPos] == '\r') {
-				substrPos[numLines] = curPos + 1;
-				substrLen[numLines - 1] -= 1;
-				numLines++;
-			} else {
-				substrLen[numLines - 1] += 1;
-			}
-			curPos++;
-		}
-
-		// Split the lines and draw them from top to bottom
-		int lastLineY = 0;
-		for (int i = 0; i < numLines; i++) {
-			BlastText &bt = _blastTextQueue[_blastTextQueuePos++];
-			Common::strlcpy((char *)bt.text, (char *)(textBuf + substrPos[i]), substrLen[i] + 1);
-			bt.text[substrLen[0] + 1] = '\0'; // Truncate the substring accordingly
-			bt.xpos = x;
-			bt.ypos = lastLineY = y + (_charset->getStringHeight((char *)textBuf) * i);
-			bt.color = color;
-			bt.charset = charset;
-			bt.center = center;
-		}
-
-		int clipHeight = _charset->getCharHeight(*textBuf) + 1;
-		_charset->setCurID(of);
-		clipHeight = clipHeight + clipHeight / 2;
 
-		// Correct for the vertical placement for the last line, and compensate bottom-up
-		int diffY = lastLineY - MIN<int>(lastLineY, 470 - clipHeight);
-		
-		for (int i = 0; i < numLines; i++) {
-			BlastText &bt = _blastTextQueue[_blastTextQueuePos - 1 - i];
-			bt.ypos -= diffY;
-		}
-	} else {
-		BlastText &bt = _blastTextQueue[_blastTextQueuePos++];
-
-		Common::strlcpy((char *)bt.text, (char *)textBuf, sizeof(textBuf));
-		bt.xpos = x;
-		bt.ypos = y;
-		bt.color = color;
-		bt.charset = charset;
-		bt.center = center;
-	}
-}
 #endif
 
 void ScummEngine_v6::drawBlastTexts() {
@@ -236,7 +143,6 @@ void ScummEngine_v6::drawBlastTexts() {
 	int i;
 
 	for (i = 0; i < _blastTextQueuePos; i++) {
-
 		buf = _blastTextQueue[i].text;
 
 		_charset->_top = _blastTextQueue[i].ypos + _screenTop;
@@ -246,10 +152,6 @@ void ScummEngine_v6::drawBlastTexts() {
 		_charset->_disableOffsX = _charset->_firstChar = true;
 		_charset->setCurID(_blastTextQueue[i].charset);
 
-		if (_game.version >= 7 && _language == Common::HE_ISR) {
-			fakeBidiString(buf, false);
-		}
-
 		do {
 			_charset->_left = _blastTextQueue[i].xpos;
 
@@ -271,19 +173,6 @@ void ScummEngine_v6::drawBlastTexts() {
 				if (c == 0x0B)
 					continue;
 
-				// Some localizations may override colors
-				// See credits in Chinese COMI
-				if (_game.id == GID_CMI && _language == Common::ZH_TWN &&
-				      c == '^' && (buf == _blastTextQueue[i].text + 1)) {
-					if (*buf == 'c') {
-						int color = buf[3] - '0' + 10 *(buf[2] - '0');
-						_charset->setColor(color);
-
-						buf += 4;
-						c = *buf++;
-					}
-				}
-
 				if (c != 0 && c != 0xFF && c != '\n' && c != _newLineCharacter) {
 					if (c & 0x80 && _useCJKMode) {
 						if (_language == Common::JA_JPN && !checkSJISCode(c)) {
@@ -312,51 +201,6 @@ void ScummEngine_v6::removeBlastTexts() {
 	_blastTextQueuePos = 0;
 }
 
-
-#pragma mark -
-#pragma mark --- V7 subtitle queue code ---
-#pragma mark -
-
-
-#ifdef ENABLE_SCUMM_7_8
-void ScummEngine_v7::processSubtitleQueue() {
-	for (int i = 0; i < _subtitleQueuePos; ++i) {
-		SubtitleText *st = &_subtitleQueue[i];
-		if (!st->actorSpeechMsg && (!ConfMan.getBool("subtitles") || VAR(VAR_VOICE_MODE) == 0))
-			// no subtitles and there's a speech variant of the message, don't display the text
-			continue;
-		enqueueText(st->text, st->xpos, st->ypos, st->color, st->charset, false);
-	}
-}
-
-void ScummEngine_v7::addSubtitleToQueue(const byte *text, const Common::Point &pos, byte color, byte charset) {
-	if (text[0] && strcmp((const char *)text, " ") != 0) {
-		assert(_subtitleQueuePos < ARRAYSIZE(_subtitleQueue));
-		SubtitleText *st = &_subtitleQueue[_subtitleQueuePos];
-		int i = 0;
-		while (1) {
-			st->text[i] = text[i];
-			if (!text[i])
-				break;
-			++i;
-		}
-		st->xpos = pos.x;
-		st->ypos = pos.y;
-		st->color = color;
-		st->charset = charset;
-		st->actorSpeechMsg = _haveActorSpeechMsg;
-		++_subtitleQueuePos;
-	}
-}
-
-void ScummEngine_v7::clearSubtitleQueue() {
-	memset(_subtitleQueue, 0, sizeof(_subtitleQueue));
-	_subtitleQueuePos = 0;
-}
-#endif
-
-
-
 #pragma mark -
 #pragma mark --- Core message/subtitle code ---
 #pragma mark -
@@ -926,169 +770,6 @@ void ScummEngine::CHARSET_1() {
 #endif
 }
 
-#ifdef ENABLE_SCUMM_7_8
-void ScummEngine_v7::CHARSET_1() {
-	if (_game.id == GID_FT) {
-		ScummEngine::CHARSET_1();
-		return;
-	}
-
-	byte subtitleBuffer[2048];
-	byte *subtitleLine = subtitleBuffer;
-	Common::Point subtitlePos;
-
-	processSubtitleQueue();
-
-	if (!_haveMsg)
-		return;
-
-	Actor *a = NULL;
-	if (getTalkingActor() != 0xFF)
-		a = derefActorSafe(getTalkingActor(), "CHARSET_1");
-
-	StringTab saveStr = _string[0];
-	if (a && _string[0].overhead) {
-		int s;
-
-		_string[0].xpos = a->getPos().x - _virtscr[kMainVirtScreen].xstart;
-		s = a->_scalex * a->_talkPosX / 255;
-		_string[0].xpos += (a->_talkPosX - s) / 2 + s;
-
-		_string[0].ypos = a->getPos().y - a->getElevation() - _screenTop;
-		s = a->_scaley * a->_talkPosY / 255;
-		_string[0].ypos += (a->_talkPosY - s) / 2 + s;
-	}
-
-	_charset->setColor(_charsetColor);
-
-	if (a && a->_charset)
-		_charset->setCurID(a->_charset);
-	else
-		_charset->setCurID(_string[0].charset);
-
-	if (_talkDelay)
-		return;
-
-	if (VAR(VAR_HAVE_MSG)) {
-		if ((_sound->_sfxMode & 2) == 0) {
-			stopTalk();
-		}
-		return;
-	}
-
-	if (a && !_string[0].no_talk_anim) {
-		a->runActorTalkScript(a->_talkStartFrame);
-	}
-
-	if (!_keepText) {
-		clearSubtitleQueue();
-		_nextLeft = _string[0].xpos;
-		_nextTop = _string[0].ypos + _screenTop;
-	}
-
-	_charset->_disableOffsX = _charset->_firstChar = !_keepText;
-
-	_talkDelay = VAR(VAR_DEFAULT_TALK_DELAY);
-	for (int i = _charsetBufPos; _charsetBuffer[i]; ++i) {
-		_talkDelay += VAR(VAR_CHARINC);
-	}
-
-	if (_string[0].wrapping) {
-		_charset->addLinebreaks(0, _charsetBuffer, _charsetBufPos, _screenWidth - 20);
-
-		struct { int pos, w; } substring[10];
-		int count = 0;
-		int maxLineWidth = 0;
-		int lastPos = 0;
-		int code = 0;
-		while (handleNextCharsetCode(a, &code)) {
-			if (code == 13 || code == 0) {
-				*subtitleLine++ = '\0';
-				assert(count < 10);
-				substring[count].w = _charset->getStringWidth(0, subtitleBuffer + lastPos);
-				if (maxLineWidth < substring[count].w) {
-					maxLineWidth = substring[count].w;
-				}
-				substring[count].pos = lastPos;
-				++count;
-				lastPos = subtitleLine - subtitleBuffer;
-			} else {
-				*subtitleLine++ = code;
-				*subtitleLine = '\0';
-			}
-			if (code == 0) {
-				break;
-			}
-		}
-
-		int h = count * _charset->getFontHeight();
-		h += _charset->getFontHeight() / 2;
-		subtitlePos.y = _string[0].ypos;
-		if (subtitlePos.y + h > _screenHeight - 10) {
-			subtitlePos.y = _screenHeight - 10 - h;
-		}
-		if (subtitlePos.y < 10) {
-			subtitlePos.y = 10;
-		}
-
-		for (int i = 0; i < count; ++i) {
-			subtitlePos.x = _string[0].xpos;
-			if (_string[0].center) {
-				if (subtitlePos.x + maxLineWidth / 2 > _screenWidth - 10) {
-					subtitlePos.x = _screenWidth - 10 - maxLineWidth / 2;
-				}
-				if (subtitlePos.x - maxLineWidth / 2 < 10) {
-					subtitlePos.x = 10 + maxLineWidth / 2;
-				}
-				subtitlePos.x -= substring[i].w / 2;
-			} else {
-				if (subtitlePos.x + maxLineWidth > _screenWidth - 10) {
-					subtitlePos.x = _screenWidth - 10 - maxLineWidth;
-				}
-				if (subtitlePos.x - maxLineWidth < 10) {
-					subtitlePos.x = 10;
-				}
-			}
-			if (subtitlePos.y < _screenHeight - 10) {
-				addSubtitleToQueue(subtitleBuffer + substring[i].pos, subtitlePos, _charsetColor, _charset->getCurID());
-			}
-			subtitlePos.y += _charset->getStringHeight((char *)subtitleBuffer);
-		}
-	} else {
-		int code = 0;
-		subtitlePos.y = _string[0].ypos;
-		if (subtitlePos.y < 10) {
-			subtitlePos.y = 10;
-		}
-		while (handleNextCharsetCode(a, &code)) {
-			if (code == 13 || code == 0) {
-				subtitlePos.x = _string[0].xpos;
-				if (_string[0].center) {
-					subtitlePos.x -= _charset->getStringWidth(0, subtitleBuffer) / 2;
-				}
-				if (subtitlePos.x < 10) {
-					subtitlePos.x = 10;
-				}
-				if (subtitlePos.y < _screenHeight - 10) {
-					addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID());
-					subtitlePos.y += _charset->getStringHeight((char *)subtitleBuffer);
-				}
-				subtitleLine = subtitleBuffer;
-			} else {
-				*subtitleLine++ = code;
-			}
-			*subtitleLine = '\0';
-			if (code == 0) {
-				break;
-			}
-		}
-	}
-	_haveMsg = (_game.version == 8) ? 2 : 1;
-	_keepText = false;
-	_string[0] = saveStr;
-}
-#endif
-
 void ScummEngine::drawString(int a, const byte *msg) {
 	byte buf[270];
 	byte *space;
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
new file mode 100644
index 00000000000..604f3dce059
--- /dev/null
+++ b/engines/scumm/string_v7.cpp
@@ -0,0 +1,595 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifdef ENABLE_SCUMM_7_8
+
+#include "common/config-manager.h"
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/scumm_v8.h"
+#include "scumm/sound.h"
+#include "scumm/string_v7.h"
+
+namespace Scumm {
+
+TextRenderer_v7::TextRenderer_v7(ScummEngine *vm, GlyphRenderer_v7 *gr)
+	: _gameId(vm->_game.id), _lang(vm->_language), _2byteCharWidth(vm->_2byteWidth), _screenWidth(vm->_screenWidth), _useCJKMode(vm->_useCJKMode), _lineBreakMarker(vm->_newLineCharacter), _gr(gr) {
+}
+
+int TextRenderer_v7::getStringWidth(const char *str, uint numBytesMax) {
+	assert(str);
+
+	if (!numBytesMax)
+		return 0;
+
+	int maxWidth = 0;
+	int width = 0;
+	int font = _gr->setFont(-1);
+
+	while (*str && numBytesMax) {
+		while (str[0] == '^') {
+			switch (str[1]) {
+			case 'f':
+				_gr->setFont(str[3] - '0');
+				str += 4;
+				break;
+			case 'c':
+				str += 5;
+				break;
+			default:
+				error("CharsetRenderer::getStringWidth(): Invalid escape code in text string");
+			}
+		}
+
+		if (is2ByteCharacter(_lang, *str)) {
+			width += _2byteCharWidth + (_lang != Common::JA_JPN ? 1 : 0);
+			++str;
+			--numBytesMax;
+		} else if (*str == '\n') {
+			maxWidth = MAX<int>(width, maxWidth);
+			width = 0;
+		} else if (*str != '\r' && *str != _lineBreakMarker) {
+			width += _gr->getCharWidth(*str);
+		}
+		++str;
+		--numBytesMax;
+	}
+
+	_gr->setFont(font);
+
+	return MAX<int>(width, maxWidth);
+}
+
+int TextRenderer_v7::getStringHeight(const char *str, uint numBytesMax) {
+	assert(str);
+
+	if (!numBytesMax)
+		return 0;
+
+	int totalHeight = 0;
+	int lineHeight = 0;
+	int font = _gr->setFont(-1);
+
+	while (*str && numBytesMax) {
+		while (str[0] == '^') {
+			switch (str[1]) {
+			case 'f':
+				_gr->setFont(str[3] - '0');
+				str += 4;
+				break;
+			case 'c':
+				str += 5;
+				break;
+			default:
+				error("CharsetRenderer::getStringWidth(): Invalid escape code in text string");
+			}
+		}
+
+		if (*str == '\n') {
+			totalHeight += (lineHeight ? lineHeight : _gr->getFontHeight()) + 1;
+			lineHeight = 0;
+		} else if (*str != '\r' && *str != _lineBreakMarker) {
+			lineHeight = MAX<int>(lineHeight, _gr->getCharHeight(*str));
+			if (is2ByteCharacter(_lang, *str)) {
+				++str;
+				--numBytesMax;
+			}
+		}
+		++str;
+		--numBytesMax;
+	}
+
+	_gr->setFont(font);
+
+	return totalHeight + (lineHeight ? lineHeight : _gr->getFontHeight()) + 1;
+}
+
+void TextRenderer_v7::drawSubstring(const char *str, uint numBytesMax, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 &col) {
+	if (_lang == Common::HE_ISR) {
+		for (int i = numBytesMax; i > 0; i--) {
+			x += _gr->drawChar(buffer, clipRect, x, y, pitch, col, str[i - 1]);
+		}
+	} else {
+		for (int i = 0; str[i] != 0 && numBytesMax; ++i) {
+			while (str[i] == '^') {
+				switch (str[i + 1]) {
+				case 'f':
+					_gr->setFont(str[i + 3] - '0');
+					i += 4;
+					break;
+				case 'c':
+					col = str[4] - '0' + 10 *(str[3] - '0');
+					i += 5;
+					break;
+				default:
+					error("CharsetRenderer::getStringWidth(): Invalid escape code in text string");
+				}
+			}
+
+			if (is2ByteCharacter(_lang, str[i])) {
+				x += _gr->draw2byte(buffer, clipRect, x, y, pitch, col, (byte)str[i] + 256 * (byte)str[i + 1]);
+				++i;
+				--numBytesMax;
+			} else if (str[i] != '\n' && str[i] != _lineBreakMarker) {
+				x += _gr->drawChar(buffer, clipRect, x, y, pitch, col, str[i]);
+			}
+			--numBytesMax;
+		}
+	}
+}
+
+#define SCUMM7_MAX_STRINGS		80
+
+void TextRenderer_v7::drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, bool center) {
+	debugC(DEBUG_GENERAL, "TextRenderer_v7::drawString(str: '%s', x: %d, y: %d, col: %d, clipRect: (%d, %d, %d, %d), center: %d)", str, x, y, col, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, center);
+
+	int totalLen = (int)strlen(str);
+	int lineStart = 0;
+
+	// COMI always does this for CJK strings (before any other possible yPos fixes).
+	if (_gameId == GID_CMI) {
+		if (_useCJKMode)
+			y += 2;
+		// No idea whether it is actually used. We currently don't handle this flag.
+		/*if (flags & 0x40)
+			y -= (getStringHeight(str, totalLen) / 2);*/
+	}
+
+	int y2 = y;
+	int maxWidth = 0;
+	int font = _gr->setFont(-1);
+
+	for (int pos = 0; pos <= totalLen; ++pos) {
+		if (str[pos] != '\0' && str[pos] != '\n')
+			continue;
+
+		int len = pos - lineStart;
+		int height = getStringHeight(str + lineStart, len);
+		if (y < clipRect.bottom) {
+			int width = getStringWidth(str + lineStart, len);
+			maxWidth = MAX<int>(maxWidth, width);
+			drawSubstring(str + lineStart, len, buffer, clipRect, center ? (x - width / 2) : x, y, pitch, col);
+			y += height;
+		}
+
+		lineStart = pos + 1;
+	}
+
+	_gr->setFont(font);
+
+	clipRect.left = center ? x - maxWidth : x;
+	clipRect.right = MIN<int>(clipRect.right, clipRect.left + maxWidth);
+	clipRect.top = y2;
+	clipRect.bottom = y;
+}
+
+void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, bool center) {
+	debugC(DEBUG_GENERAL, "TextRenderer_v7::drawStringWrap(str: '%s', x: %d, y: %d, col: %d, clipRect: (%d, %d, %d, %d), center: %d)", str, x, y, col, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, center);
+	// This implementation is from COMI. Things are done a bit differently than in the older implementations.
+	// In particular, the older version would insert '\0' chars into the string to cut off the sub strings
+	// before calling getStringWidth(), getStringHeight() or drawSubstring() and replace these chars with the
+	// original values afterwards. COMI allows a byte length limitation in all the functions so that the sub
+	// string length can be passed and no cut off '\0' chars are needed.
+
+	int len = (int)strlen(str);
+	Common::String spaceSeparators(Common::String::format(" %c", (char)_lineBreakMarker));
+	Common::String breakSeparators(Common::String::format(" \n%c", (char)_lineBreakMarker));
+
+	int16 substrByteLength[SCUMM7_MAX_STRINGS];
+	memset(substrByteLength, 0, sizeof(substrByteLength));
+	int16 substrWidths[SCUMM7_MAX_STRINGS];
+	memset(substrWidths, 0, sizeof(substrWidths));
+	int16 substrStart[SCUMM7_MAX_STRINGS];
+	memset(substrStart, 0, sizeof(substrStart));
+
+	int16 numSubstrings = 0;
+	int height = 0;
+	int lastSubstrHeight = 0;
+	int maxWidth = 0;
+	int curWidth = 0;
+	int curPos = -1;
+
+	// COMI does this for CJK strings (before any other possible yPos fixes, see lines 343 - 355).
+	if (_gameId == GID_CMI && _useCJKMode)
+		y += 2;
+
+	while (curPos < len) {
+		int textStart = curPos + 1;
+		while (str[textStart] && spaceSeparators.contains(str[textStart]))
+			++textStart;
+
+		int separatorWidth = curPos > 0 ? getStringWidth(str + curPos, textStart - curPos) : 0;
+
+		int nextSeparatorPos = textStart;
+		while (!breakSeparators.contains(str[nextSeparatorPos])) {
+			if (++nextSeparatorPos == len)
+				break;
+		}
+
+		int wordWidth = getStringWidth(str + textStart, nextSeparatorPos - textStart);
+		int newWidth = curWidth + separatorWidth + wordWidth;
+
+		if (curWidth && newWidth > clipRect.width()) {
+			if (numSubstrings < SCUMM7_MAX_STRINGS) {
+				substrWidths[numSubstrings] = curWidth;
+				substrByteLength[numSubstrings] = curPos - substrStart[numSubstrings];
+				numSubstrings++;
+			}
+			newWidth = wordWidth;
+			substrStart[numSubstrings] = textStart;
+		}
+		curWidth = newWidth;
+
+		curPos = nextSeparatorPos;
+		if (!spaceSeparators.contains(str[curPos])) {
+			// This one is only triggered by '\n' (which frequently happens in COMI/English).
+			if (numSubstrings < SCUMM7_MAX_STRINGS) {
+				substrWidths[numSubstrings] = curWidth;
+				substrByteLength[numSubstrings] = curPos - substrStart[numSubstrings];
+				numSubstrings++;
+				substrStart[numSubstrings] = curPos + 1;
+			}
+			curWidth = 0;
+		}
+	}
+
+	if (curWidth && numSubstrings < SCUMM7_MAX_STRINGS) {
+		substrWidths[numSubstrings] = curWidth;
+		substrByteLength[numSubstrings] = curPos - substrStart[numSubstrings];
+		numSubstrings++;
+	}
+
+	for (int i = 0; i < numSubstrings; ++i) {
+		maxWidth = MAX<int>(maxWidth, substrWidths[i]);
+		lastSubstrHeight = substrByteLength[i] > 0 ? getStringHeight(str + substrStart[i], substrByteLength[i]) : 0;
+		height += lastSubstrHeight;
+	}
+
+	// I have verified these y-corrections for DIG (English and Chinese), COMI (English and Chinese) and FT (English).
+	// In COMI there seem to be more text flags which we don't support and for which I haven't seen use cases yet. I
+	// put some commented-out code in here as a reminder...
+	int clipHeight = height + lastSubstrHeight / 2;
+
+	/*if (_vm->_game.id == GID_CMI && (flags & 0x40))
+		y -= (lastSubstrHeight / 2);*/
+
+	if (y > clipRect.bottom - clipHeight /*&& !(_vm->_game.id == GID_CMI && (flags & 0x100))*/)
+		y = clipRect.bottom - clipHeight;
+
+	if (y < clipRect.top)
+		y = clipRect.top;
+
+	if (center) {
+		if (x + (maxWidth >> 1) > clipRect.right)
+			x = clipRect.right - (maxWidth >> 1);
+		if (x - (maxWidth >> 1) < clipRect.left)
+			x = clipRect.left + (maxWidth >> 1);
+	} else {
+		if (x > clipRect.right - maxWidth)
+			x = clipRect.right - maxWidth;
+		if (x < clipRect.left)
+			x = clipRect.left;
+	}
+
+	int y2 = y;
+	int font = _gr->setFont(-1);
+
+	for (int i = 0; i < numSubstrings; i++) {
+		int xpos = center ? x - substrWidths[i] / 2 : x;
+		len = substrByteLength[i] > 0 ? substrByteLength[i] : 0;
+		drawSubstring(str + substrStart[i], len, buffer, clipRect, xpos, y, pitch, col);
+		y += getStringHeight(str + substrStart[i], len);
+	}
+
+	_gr->setFont(font);
+
+	clipRect.left = center ? x - maxWidth / 2 : x;
+	clipRect.right = MIN<int>(clipRect.right, clipRect.left + maxWidth);
+	clipRect.top = y2;
+	clipRect.bottom = y;
+}
+
+void ScummEngine_v7::createTextRenderer(GlyphRenderer_v7 *gr) {
+	assert(gr);
+	_textV7 = new TextRenderer_v7(this, gr);
+}
+
+void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped) {
+	assert(_blastTextQueuePos + 1 <= ARRAYSIZE(_blastTextQueue));
+
+	if (_useCJKMode) {
+		// The Dig expressly checks for x == 160 && y == 189 && charset == 3. Usually, if the game wants to print CJK text at the bottom
+		// of the screen it will use y = 183. So maybe this is a hack to fix some script texts that weren forgotten in the CJK converting
+		// process.
+		if (_game.id == GID_DIG && x == 160 && y == 189 && charset == 3)
+			y -= 6;
+	}
+
+	BlastText &bt = _blastTextQueue[_blastTextQueuePos];
+	convertMessageToString(text, bt.text, sizeof(bt.text));
+	if (!*bt.text)
+		return;
+
+	_blastTextQueuePos++;
+	bt.xpos = x;
+	bt.ypos = y;
+	bt.color = color;
+	bt.charset = charset;
+	bt.center = center;
+	bt.wrap = wrapped;
+}
+
+void ScummEngine_v7::drawBlastTexts() {
+	VirtScreen *vs = &_virtscr[kMainVirtScreen];
+	Common::Rect _defaultTextClipRect = Common::Rect(vs->w, vs->h);
+	Common::Rect _wrappedTextClipRect = _game.id == GID_CMI ? Common::Rect(10, 10, 630, 470) : _defaultTextClipRect;
+
+	for (int i = 0; i < _blastTextQueuePos; i++) {
+		BlastText &bt = _blastTextQueue[i];
+
+		_charset->setCurID(_blastTextQueue[i].charset);
+
+		if (bt.wrap) {
+			bt.rect = _wrappedTextClipRect;
+			_textV7->drawStringWrap((const char*)bt.text, (byte*)vs->getPixels(0, 0), bt.rect, bt.xpos, bt.ypos, vs->pitch, bt.color, bt.center);
+		} else {
+			bt.rect = _defaultTextClipRect;
+			_textV7->drawString((const char*)bt.text, (byte*)vs->getPixels(0, 0), bt.rect, bt.xpos, bt.ypos, vs->pitch, bt.color, bt.center);
+		}
+
+		markRectAsDirty(vs->number, bt.rect);
+	}
+}
+
+void ScummEngine_v8::printString(int m, const byte *msg) {
+	if (m == 4) {
+		const StringTab &st = _string[m];
+		enqueueText(msg, st.xpos, st.ypos, st.color, st.charset, st.center, st.wrapping);
+	} else {
+		ScummEngine::printString(m, msg);
+	}
+}
+
+#pragma mark -
+#pragma mark --- V7 subtitle queue code ---
+#pragma mark -
+
+void ScummEngine_v7::processSubtitleQueue() {
+	for (int i = 0; i < _subtitleQueuePos; ++i) {
+		SubtitleText *st = &_subtitleQueue[i];
+		if (!st->actorSpeechMsg && (!ConfMan.getBool("subtitles") || VAR(VAR_VOICE_MODE) == 0))
+			// no subtitles and there's a speech variant of the message, don't display the text
+			continue;
+		enqueueText(st->text, st->xpos, st->ypos, st->color, st->charset, false);
+	}
+}
+
+void ScummEngine_v7::addSubtitleToQueue(const byte *text, const Common::Point &pos, byte color, byte charset) {
+	if (text[0] && strcmp((const char *)text, " ") != 0) {
+		assert(_subtitleQueuePos < ARRAYSIZE(_subtitleQueue));
+		SubtitleText *st = &_subtitleQueue[_subtitleQueuePos];
+		int i = 0;
+		while (1) {
+			st->text[i] = text[i];
+			if (!text[i])
+				break;
+			++i;
+		}
+		st->xpos = pos.x;
+		st->ypos = pos.y;
+		st->color = color;
+		st->charset = charset;
+		st->actorSpeechMsg = _haveActorSpeechMsg;
+		++_subtitleQueuePos;
+	}
+}
+
+void ScummEngine_v7::clearSubtitleQueue() {
+	memset(_subtitleQueue, 0, sizeof(_subtitleQueue));
+	_subtitleQueuePos = 0;
+}
+
+void ScummEngine_v7::CHARSET_1() {
+	if (_game.id == GID_FT) {
+		ScummEngine::CHARSET_1();
+		return;
+	}
+
+	byte subtitleBuffer[2048];
+	byte *subtitleLine = subtitleBuffer;
+	Common::Point subtitlePos;
+
+	processSubtitleQueue();
+
+	if (!_haveMsg)
+		return;
+
+	Actor *a = NULL;
+	if (getTalkingActor() != 0xFF)
+		a = derefActorSafe(getTalkingActor(), "CHARSET_1");
+
+	StringTab saveStr = _string[0];
+	if (a && _string[0].overhead) {
+		int s;
+
+		_string[0].xpos = a->getPos().x - _virtscr[kMainVirtScreen].xstart;
+		s = a->_scalex * a->_talkPosX / 255;
+		_string[0].xpos += (a->_talkPosX - s) / 2 + s;
+
+		_string[0].ypos = a->getPos().y - a->getElevation() - _screenTop;
+		s = a->_scaley * a->_talkPosY / 255;
+		_string[0].ypos += (a->_talkPosY - s) / 2 + s;
+	}
+
+	_charset->setColor(_charsetColor);
+
+	if (a && a->_charset)
+		_charset->setCurID(a->_charset);
+	else
+		_charset->setCurID(_string[0].charset);
+
+	if (_talkDelay)
+		return;
+
+	if (VAR(VAR_HAVE_MSG)) {
+		if ((_sound->_sfxMode & 2) == 0) {
+			stopTalk();
+		}
+		return;
+	}
+
+	if (a && !_string[0].no_talk_anim) {
+		a->runActorTalkScript(a->_talkStartFrame);
+	}
+
+	if (!_keepText) {
+		clearSubtitleQueue();
+		_nextLeft = _string[0].xpos;
+		_nextTop = _string[0].ypos + _screenTop;
+	}
+
+	_charset->_disableOffsX = _charset->_firstChar = !_keepText;
+
+	_talkDelay = VAR(VAR_DEFAULT_TALK_DELAY);
+	for (int i = _charsetBufPos; _charsetBuffer[i]; ++i) {
+		_talkDelay += VAR(VAR_CHARINC);
+	}
+
+	if (_string[0].wrapping) {
+		_charset->addLinebreaks(0, _charsetBuffer, _charsetBufPos, _screenWidth - 20);
+
+		struct { int pos, w; } substring[10];
+		int count = 0;
+		int maxLineWidth = 0;
+		int lastPos = 0;
+		int code = 0;
+		while (handleNextCharsetCode(a, &code)) {
+			if (code == 13 || code == 0) {
+				*subtitleLine++ = '\0';
+				assert(count < 10);
+				substring[count].w = _charset->getStringWidth(0, subtitleBuffer + lastPos);
+				if (maxLineWidth < substring[count].w) {
+					maxLineWidth = substring[count].w;
+				}
+				substring[count].pos = lastPos;
+				++count;
+				lastPos = subtitleLine - subtitleBuffer;
+			} else {
+				*subtitleLine++ = code;
+				*subtitleLine = '\0';
+			}
+			if (code == 0) {
+				break;
+			}
+		}
+
+		int h = count * _charset->getFontHeight();
+		h += _charset->getFontHeight() / 2;
+		subtitlePos.y = _string[0].ypos;
+		if (subtitlePos.y + h > _screenHeight - 10) {
+			subtitlePos.y = _screenHeight - 10 - h;
+		}
+		if (subtitlePos.y < 10) {
+			subtitlePos.y = 10;
+		}
+
+		for (int i = 0; i < count; ++i) {
+			subtitlePos.x = _string[0].xpos;
+			if (_string[0].center) {
+				if (subtitlePos.x + maxLineWidth / 2 > _screenWidth - 10) {
+					subtitlePos.x = _screenWidth - 10 - maxLineWidth / 2;
+				}
+				if (subtitlePos.x - maxLineWidth / 2 < 10) {
+					subtitlePos.x = 10 + maxLineWidth / 2;
+				}
+				subtitlePos.x -= substring[i].w / 2;
+			} else {
+				if (subtitlePos.x + maxLineWidth > _screenWidth - 10) {
+					subtitlePos.x = _screenWidth - 10 - maxLineWidth;
+				}
+				if (subtitlePos.x - maxLineWidth < 10) {
+					subtitlePos.x = 10;
+				}
+			}
+			if (subtitlePos.y < _screenHeight - 10) {
+				addSubtitleToQueue(subtitleBuffer + substring[i].pos, subtitlePos, _charsetColor, _charset->getCurID());
+			}
+			subtitlePos.y += _charset->getFontHeight();
+		}
+	} else {
+		int code = 0;
+		subtitlePos.y = _string[0].ypos;
+		if (subtitlePos.y < 10) {
+			subtitlePos.y = 10;
+		}
+		while (handleNextCharsetCode(a, &code)) {
+			if (code == 13 || code == 0) {
+				subtitlePos.x = _string[0].xpos;
+				if (_string[0].center) {
+					subtitlePos.x -= _charset->getStringWidth(0, subtitleBuffer) / 2;
+				}
+				if (subtitlePos.x < 10) {
+					subtitlePos.x = 10;
+				}
+				if (subtitlePos.y < _screenHeight - 10) {
+					addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID());
+					subtitlePos.y += _charset->getFontHeight();
+				}
+				subtitleLine = subtitleBuffer;
+			} else {
+				*subtitleLine++ = code;
+			}
+			*subtitleLine = '\0';
+			if (code == 0) {
+				break;
+			}
+		}
+	}
+	_haveMsg = (_game.version == 8) ? 2 : 1;
+	_keepText = false;
+	_string[0] = saveStr;
+}
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/string_v7.h b/engines/scumm/string_v7.h
new file mode 100644
index 00000000000..a2e8487ac12
--- /dev/null
+++ b/engines/scumm/string_v7.h
@@ -0,0 +1,73 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SCUMM_STRING_V7_H
+#define SCUMM_STRINGV_7_H
+
+#ifdef ENABLE_SCUMM_7_8
+
+#include "common/scummsys.h"
+#include "scumm/charset_v7.h"
+
+namespace Scumm {
+
+class ScummEngine;
+
+class TextRenderer_v7 {
+public:
+	TextRenderer_v7(ScummEngine *vm, GlyphRenderer_v7 *gr);
+	~TextRenderer_v7() {}
+
+	void drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, bool center);
+	void drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, bool center);
+
+	int getStringWidth(const char *str) { return getStringWidth(str, 100000); }
+	int getStringHeight(const char *str) { return getStringHeight(str, 100000); }
+
+	static inline bool is2ByteCharacter(Common::Language lang, byte c) {
+		if (lang == Common::JA_JPN)
+			return (c >= 0x80 && c <= 0x9F) || (c >= 0xE0 && c <= 0xFD);
+		else if (lang == Common::KO_KOR)
+			return (c >= 0xB0 && c <= 0xD0);
+		else if (lang == Common::ZH_TWN || lang == Common::ZH_CNA)
+			return (c >= 0x80);
+		return false;
+	}
+
+private:
+	int getStringWidth(const char *str, uint numBytesMax);
+	int getStringHeight(const char *str, uint numBytesMax);
+	void drawSubstring(const char *str, uint numBytesMax, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 &col);
+
+	const Common::Language _lang;
+	const byte _gameId;
+	const bool _useCJKMode;
+	const byte _2byteCharWidth;
+	const byte _lineBreakMarker;
+	const uint16 _screenWidth;
+	GlyphRenderer_v7 *_gr;
+};
+
+} // End of namespace Scumm
+
+#endif
+#endif


Commit: 0256e92c250eef56e86dd3685747ad561c71cbff
    https://github.com/scummvm/scummvm/commit/0256e92c250eef56e86dd3685747ad561c71cbff
Author: athrxx (athrxx at scummvm.org)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: (SCUMM7/8) - reorganize font rendering - second part

- Attach actor talk texts to the appropriate text renderer and get rid of redundant code.
- Cleanup subtitle text handling.
- Fix handling of ^codes.
- Fix more regressions from last commit.
- Correct some x/y positioning.

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/charset.h
    engines/scumm/nut_renderer.cpp
    engines/scumm/nut_renderer.h
    engines/scumm/scumm.cpp
    engines/scumm/scumm_v7.h
    engines/scumm/smush/smush_player.cpp
    engines/scumm/string.cpp
    engines/scumm/string_v7.cpp
    engines/scumm/string_v7.h


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 0a57a6db5ca..8907849df70 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1965,21 +1965,39 @@ void CharsetRendererMac::setColor(byte color) {
 }
 
 #ifdef ENABLE_SCUMM_7_8
-int CharsetRendererV7::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) {
-	if (!prepareDraw(chr))
+int CharsetRendererV7::draw2byte(byte*, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) {
+	if (_vm->isScummvmKorTarget()) {
+		enableShadow(true);
+		_charPtr = _vm->get2byteCharPtr(chr);
+		_origWidth = _width = _vm->_2byteWidth;
+		_origHeight = _height = _vm->_2byteHeight;
+		_offsX = _offsY = 0;
+	} else if (!prepareDraw(chr)) {
 		return 0;
+	}
 
+	_color = col;
 	VirtScreen &vs = _vm->_virtscr[kMainVirtScreen];
-	drawBits1(vs, x + vs.xstart, y, _charPtr, MAX<int>(clipRect.top, y), _width - 1, _height);
-	return _width;
+	drawBits1(vs, x + vs.xstart, y, _charPtr, MAX<int>(clipRect.top, y), _origWidth, _origHeight);
+
+	return _origWidth + _spacing;
 }
 
 int CharsetRendererV7::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) {
 	if (!prepareDraw(chr))
 		return 0;
 
+	if (_vm->isScummvmKorTarget()) {
+		_origWidth = _width;
+		_origHeight = _height;
+	}
+
+	_width = getCharWidth(chr);
+
+	_vm->_charsetColorMap[1] = col;
 	VirtScreen &vs = _vm->_virtscr[kMainVirtScreen];
-	drawBitsN(vs, buffer + y * vs.pitch + x, _charPtr, _vm->_bytesPerPixel, y, _origWidth, _origHeight);
+	drawBitsN(vs, buffer + (y + _offsY) * vs.pitch + vs.xstart + x, _charPtr, *_fontPtr, y, _origWidth, _origHeight);
+
 	return _width;
 }
 
@@ -2027,7 +2045,7 @@ int CharsetRendererNut::getCharHeight(uint16 chr) const {
 
 int CharsetRendererNut::getCharWidth(uint16 chr) const {
 	assert(_current);
-	return _current->getCharWidth(chr);
+	return _current->getCharWidth(chr & 0xFF);
 }
 
 int CharsetRendererNut::getFontHeight() const {
@@ -2035,122 +2053,6 @@ int CharsetRendererNut::getFontHeight() const {
 	return _current->getFontHeight();
 }
 
-int CharsetRendererNut::getStringWidth(int arg, const byte *text, uint strLenMax) {
-	// SCUMM7 games actually use the same implemention (minus the strLenMax parameter). If
-	// any text placement bugs in one of these games come up it might be worth to look at
-	// that. Or simply for the fact that we could get rid of SmushFont::getStringWidth()...
-	if (!strLenMax)
-		return 0;
-
-	int maxWidth = 0;
-	int width = 0;
-
-	while (*text && strLenMax) {
-		while (text[0] == '^') {
-			switch (text[1]) {
-			case 'f':
-				// We should change the font on the fly at this point
-				// which would result in a different width result.
-				// This has never been observed in the game though, and
-				// as such, we don't handle it.
-				text += 4;
-				break;
-			case 'c':
-				text += 5;
-				break;
-			default:
-				error("CharsetRenderer::getStringWidth(): Invalid escape code in text string");
-			}
-		}
-
-		if (is2ByteCharacter(_vm->_language, *text)) {
-			width += _vm->_2byteWidth + (_vm->_language != Common::JA_JPN ? 1 : 0);
-			++text;
-			--strLenMax;
-		} else if (*text == '\n') {
-			maxWidth = MAX<int>(width, maxWidth);
-			width = 0;
-		} else if (*text != '\r' && *text != _vm->_newLineCharacter) {
-			width += getCharWidth(*text);
-		}
-
-		++text;
-		--strLenMax;
-	}
-
-	return MAX<int>(width, maxWidth);
-}
-
-void CharsetRendererNut::printChar(int chr, bool ignoreCharsetMask) {
-	/*Common::Rect shadow;
-
-	assert(_current);
-	if (chr == '@')
-		return;
-
-	shadow.left = _left;
-	shadow.top = _top;
-
-	if (_firstChar) {
-		_str.left = (shadow.left >= 0) ? shadow.left : 0;
-		_str.top = (shadow.top >= 0) ? shadow.top : 0;
-		_str.right = _str.left;
-		_str.bottom = _str.top;
-		_firstChar = false;
-	}
-
-	int width = _current->getCharWidth(chr);
-	int height = _current->getCharHeight(chr);
-
-	bool is2byte = chr >= 256 && _vm->_useCJKMode;
-	if (is2byte) {
-		width = _vm->_2byteWidth;
-		if (_vm->_game.id == GID_CMI)
-			height++; // One extra pixel for the shadow
-	}
-
-	shadow.right = _left + width;
-	shadow.bottom = _top + height;
-
-	Graphics::Surface s;
-	if (!ignoreCharsetMask) {
-		_hasMask = true;
-		_textScreenID = kMainVirtScreen;
-	}
-
-	int drawTop = _top;
-	if (ignoreCharsetMask) {
-		VirtScreen *vs = &_vm->_virtscr[kMainVirtScreen];
-		s = *vs;
-		s.setPixels(vs->getPixels(0, 0));
-	} else {
-		s = _vm->_textSurface;
-		drawTop -= _vm->_screenTop;
-	}
-
-	Common::Rect clipRect(s.w, s.h);
-	if (chr >= 256 && _vm->_useCJKMode)
-		_current->draw2byte((uint8*)s.getBasePtr(0, 0), clipRect, _left, drawTop, s.pitch, _color, chr);
-	else
-		_current->drawChar((uint8*)s.getBasePtr(0, 0), clipRect, _left, drawTop, s.pitch, _color, (byte)chr);
-	_vm->markRectAsDirty(kMainVirtScreen, shadow);
-
-	if (_str.left > _left)
-		_str.left = _left;
-
-	// Original keeps glyph width and character dimensions separately
-	if ((_vm->_language == Common::ZH_TWN || _vm->_language == Common::KO_KOR) && is2byte)
-		width++;
-
-	_left += width;
-
-	if (_str.right < shadow.right)
-		_str.right = shadow.right;
-
-	if (_str.bottom < shadow.bottom)
-		_str.bottom = shadow.bottom;*/
-}
-
 int CharsetRendererNut::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) {
 	assert(_current);
 	return _current->draw2byte(buffer, clipRect, x, y, pitch, col, chr);
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index f013be8984a..1d88434a028 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -310,15 +310,20 @@ public:
 #ifdef ENABLE_SCUMM_7_8
 class CharsetRendererV7 : public CharsetRendererClassic, public GlyphRenderer_v7 {
 public:
-	CharsetRendererV7(ScummEngine *vm) : CharsetRendererClassic(vm) {}
+	CharsetRendererV7(ScummEngine *vm) : CharsetRendererClassic(vm), _spacing(vm->_useCJKMode && vm->_language != Common::JA_JPN ? 1 : 0) {}
 	~CharsetRendererV7() override {};
 
+	void printChar(int chr, bool ignoreCharsetMask) override { error("CharsetRendererV7::printChar(): Unexpected legacy function call"); }
+
 	int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) override;
 	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) override;
-	int getCharWidth(uint16 chr) const override { return CharsetRendererClassic::getCharWidth(chr);	}
+	int getCharWidth(uint16 chr) const override { return ((chr & 0x80) && _vm->_useCJKMode) ? _vm->_2byteWidth + _spacing : CharsetRendererClassic::getCharWidth(chr);	}
 	int getCharHeight(uint16 chr) const override { return ((chr & 0x80) && _vm->_useCJKMode) ? _vm->_2byteHeight + 1 : _fontHeight; }
 	int getFontHeight() const override { return _fontHeight; }
 	int setFont(int) override { return 0; }
+
+private:
+	const int _spacing;
 };
 
 class CharsetRendererNut : public CharsetRenderer, public GlyphRenderer_v7 {
@@ -326,7 +331,7 @@ public:
 	CharsetRendererNut(ScummEngine *vm);
 	~CharsetRendererNut() override;
 
-	void printChar(int chr, bool ignoreCharsetMask) override;
+	void printChar(int chr, bool ignoreCharsetMask) override { error("CharsetRendererNut::printChar(): Unexpected legacy function call"); }
 
 	void setCurID(int32 id) override;
 	int setFont(int id) override;
diff --git a/engines/scumm/nut_renderer.cpp b/engines/scumm/nut_renderer.cpp
index f70a36df8aa..7274cdee1c4 100644
--- a/engines/scumm/nut_renderer.cpp
+++ b/engines/scumm/nut_renderer.cpp
@@ -33,14 +33,28 @@ NutRenderer::NutRenderer(ScummEngine *vm, const char *filename) :
 	_maxCharSize(0),
 	_fontHeight(0),
 	_charBuffer(0),
-	_decodedData(0) {
-	memset(_chars, 0, sizeof(_chars));
-	loadFont(filename);
+	_decodedData(0),
+	_2byteColorTable(0),
+	_2byteShadowXOffsetTable(0),
+	_2byteShadowYOffsetTable(0),
+	_2byteMainColor(0),
+	_spacing(vm->_useCJKMode && vm->_language != Common::JA_JPN ? 1 : 0),
+	_2byteSteps(vm->_game.version == 8 ? 4 : 2) {
+		static const int8 cjkShadowOffsetsX[4] = { -1, 0, 1, 0 };
+		static const int8 cjkShadowOffsetsY[4] = { 0, 1, 0, 0 };
+		_2byteShadowXOffsetTable = &cjkShadowOffsetsX[ARRAYSIZE(cjkShadowOffsetsX) - _2byteSteps];
+		_2byteShadowYOffsetTable = &cjkShadowOffsetsY[ARRAYSIZE(cjkShadowOffsetsY) - _2byteSteps];
+		_2byteColorTable = new uint8[_2byteSteps];
+		memset(_2byteColorTable, 0, _2byteSteps);
+		_2byteMainColor = &_2byteColorTable[_2byteSteps - 1];
+		memset(_chars, 0, sizeof(_chars));
+		loadFont(filename);
 }
 
 NutRenderer::~NutRenderer() {
 	delete[] _charBuffer;
 	delete[] _decodedData;
+	delete[] _2byteColorTable;
 }
 
 void smush_decode_codec1(byte *dst, const byte *src, int left, int top, int width, int height, int pitch);
@@ -260,7 +274,7 @@ void NutRenderer::loadFont(const char *filename) {
 
 int NutRenderer::getCharWidth(byte c) const {
 	if (c >= 0x80 && _vm->_useCJKMode)
-		return _vm->_2byteWidth / 2;
+		return _vm->_2byteWidth + _spacing;
 
 	if (c >= _numChars)
 		error("invalid character in NutRenderer::getCharWidth : %d (%d)", c, _numChars);
@@ -369,6 +383,9 @@ int NutRenderer::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, in
 		dst += minY * pitch;
 	}
 
+	if (minX)
+		dst += minX;
+
 	char color = (col != -1) ? col : 1;
 
 	if (_vm->_game.version == 7) {
@@ -426,39 +443,33 @@ int NutRenderer::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, i
 	int height = MIN((int)_vm->_2byteHeight, clipRect.bottom - y);
 	int minX = x < clipRect.left ? clipRect.left - x : 0;
 	int minY = y < clipRect.top ? clipRect.top - y : 0;
+	*_2byteMainColor = col;
 
 	if (width <= 0 || height <= 0)
 		return 0;
 
 	const byte *src = _vm->get2byteCharPtr(chr);
-	byte bits = 0;
 
 	if (width <= 0 || height <= 0)
 		return 0;
 
 	if (minY) {
-		src += minY * _vm->_2byteWidth;
-		buffer += minY * pitch;
+		src += ((minY * _vm->_2byteWidth) >> 3);
+		buffer += (minY * pitch);
 	}
 
-	enum ShadowMode {
-		kNone,
-		kNormalShadowMode,
-		kCJKv7ShadowMode,
-		kCJKv8ShadowMode
-	};
-
-	ShadowMode shadowMode = _vm->_useCJKMode ? (_vm->_game.version == 8 ? kCJKv8ShadowMode : kCJKv7ShadowMode) : kNone;
-
-	int shadowOffsetXTable[4] = { -1, 0, 1, 0 };
-	int shadowOffsetYTable[4] = { 0, 1, 0, 0 };
-	int shadowOffsetColorTable[4] = { 0, 0, 0, col };
+	if (minX) {
+		src += (minX >> 3);
+		buffer += minX;
+	}
 
+	byte bits = *src;
 	const byte *origSrc = src;
-	for (int shadowIdx = (shadowMode == kCJKv8ShadowMode) ? 0 : (shadowMode == kCJKv7ShadowMode ? 2 : 3); shadowIdx < 4; shadowIdx++) {
-		int offX = MAX<int>(x + shadowOffsetXTable[shadowIdx], clipRect.left);
-		int offY = MAX<int>(y + shadowOffsetYTable[shadowIdx], clipRect.top);
-		byte drawColor = shadowOffsetColorTable[shadowIdx];
+
+	for (int step = 0; step < _2byteSteps; ++step) {
+		int offX = MAX<int>(x + _2byteShadowXOffsetTable[step], clipRect.left);
+		int offY = MAX<int>(y + _2byteShadowYOffsetTable[step], clipRect.top);
+		byte drawColor = _2byteColorTable[step];
 
 		src = origSrc;
 		byte *dst = buffer + pitch * offY + offX;
@@ -469,99 +480,14 @@ int NutRenderer::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, i
 					continue;
 				if ((i % 8) == 0)
 					bits = *src++;
-				if (bits & revBitMask(i % 8)) {
-					if (shadowMode == kNormalShadowMode) {
-						dst[i + 1] = 0;
-						dst[pitch + i] = 0;
-						dst[pitch + i + 1] = 0;
-					}
+				if (bits & revBitMask(i % 8))
 					dst[i] = drawColor;
-				}
 			}
 			dst += pitch;
 		}
 	}
-	return width + 1;
-}
-/*
-void NutRenderer::drawChar(const Graphics::Surface &s, byte c, int x, int y, byte color) {
-	// FIXME: This gets passed a const destination Surface. Intuitively this
-	// should never get written to. But sadly it does... For now we simply
-	// cast the const qualifier away.
-	byte *dst = (byte *)const_cast<void *>(s.getBasePtr(x, y));
-	const int width = MIN((int)_chars[c].width, s.w - x);
-	const int height = MIN((int)_chars[c].height, s.h - y);
-	const byte *src = unpackChar(c);
-	int srcPitch = _chars[c].width;
-
-	const int minX = x < 0 ? -x : 0;
-	const int minY = y < 0 ? -y : 0;
-
-	if (height <= 0 || width <= 0) {
-		return;
-	}
-
-	if (minY) {
-		src += minY * srcPitch;
-		dst += minY * s.pitch;
-	}
 
-	for (int ty = minY; ty < height; ty++) {
-		for (int tx = minX; tx < width; tx++) {
-			if (src[tx] != _chars[c].transparency) {
-				if (src[tx] == 1) {
-					dst[tx] = color;
-				} else {
-					dst[tx] = src[tx];
-				}
-			}
-		}
-		src += srcPitch;
-		dst += s.pitch;
-	}
+	return width + _spacing;
 }
 
-void NutRenderer::draw2byte(const Graphics::Surface &s, int c, int x, int y, byte color) {
-	const int width = _vm->_2byteWidth;
-	const int height = MIN(_vm->_2byteHeight, s.h - y);
-	const byte *src = _vm->get2byteCharPtr(c);
-	byte bits = 0;
-
-	if (height <= 0 || width <= 0) {
-		return;
-	}
-
-	int shadowOffsetXTable[4] = {-1, 0, 1, 0};
-	int shadowOffsetYTable[4] = {0, 1, 0, 0};
-	int shadowOffsetColorTable[4] = {0, 0, 0, color};
-	int shadowIdx = (_vm->_useCJKMode && _vm->_game.id == GID_CMI) ? 0 : 3;
-
-	const byte *origSrc = src;
-
-	for (; shadowIdx < 4; shadowIdx++) {
-		int offX = x + shadowOffsetXTable[shadowIdx];
-		int offY = y + shadowOffsetYTable[shadowIdx];
-		byte drawColor = shadowOffsetColorTable[shadowIdx];
-
-		// FIXME: This gets passed a const destination Surface. Intuitively this
-		// should never get written to. But sadly it does... For now we simply
-		// cast the const qualifier away.
-		byte *dst = (byte *)const_cast<void *>(s.getBasePtr(offX, offY));
-		src = origSrc;
-
-		for (int ty = 0; ty < height; ty++) {
-			for (int tx = 0; tx < width; tx++) {
-				if ((tx & 7) == 0)
-					bits = *src++;
-				if (offX + tx < 0 || offX + tx >= s.w || offY + ty < 0)
-					continue;
-				if (bits & revBitMask(tx % 8)) {
-					dst[tx] = drawColor;
-				}
-			}
-			dst += s.pitch;
-		}
-	}
-}*/
-
 } // End of namespace Scumm
diff --git a/engines/scumm/nut_renderer.h b/engines/scumm/nut_renderer.h
index 2bd0077a675..e9958516b47 100644
--- a/engines/scumm/nut_renderer.h
+++ b/engines/scumm/nut_renderer.h
@@ -41,12 +41,19 @@ protected:
 	int _numChars;
 	int _maxCharSize;
 	int _fontHeight;
+	int _spacing;
 	byte *_charBuffer;
 	byte *_decodedData;
 	byte *_paletteMap;
 	byte _bpp;
 	byte _palette[16];
 
+	const int8 *_2byteShadowXOffsetTable;
+	const int8 *_2byteShadowYOffsetTable;
+	uint8 *_2byteColorTable;
+	uint8 *_2byteMainColor;
+	const int _2byteSteps;
+
 	struct {
 		uint16 width;
 		uint16 height;
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 18bfca0fe2e..5feffff84e8 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1076,6 +1076,8 @@ ScummEngine_v7::ScummEngine_v7(OSystem *syst, const DetectorResult &dr)
 	clearSubtitleQueue();
 
 	_textV7 = NULL;
+	_defaultTextClipRect = Common::Rect(_screenWidth, _screenHeight);
+	_wrappedTextClipRect = Common::Rect(10, 10, _screenWidth - 10, _screenHeight - 10);
 
 	_game.features |= GF_NEW_COSTUMES;
 }
@@ -2980,7 +2982,6 @@ void ScummEngine::restart() {
 	// subclass which is implemented using a memory buffer (i.e. no actual file is
 	// created). Then to restart we just have to load that pseudo save state.
 
-
 	int i;
 
 	// Reset some stuff
diff --git a/engines/scumm/scumm_v7.h b/engines/scumm/scumm_v7.h
index cf5f0bc4fe6..4d5eb45d423 100644
--- a/engines/scumm/scumm_v7.h
+++ b/engines/scumm/scumm_v7.h
@@ -66,6 +66,9 @@ public:
 
 protected:
 	TextRenderer_v7 *_textV7;
+	Common::Rect _defaultTextClipRect;
+	Common::Rect _wrappedTextClipRect;
+
 	int _verbLineSpacing;
 	bool _existLanguageFile;
 	char *_languageBuffer;
@@ -80,10 +83,18 @@ protected:
 		byte charset;
 		byte text[256];
 		bool actorSpeechMsg;
+		bool center;
+		bool wrap;
 	};
 #else
 	struct SubtitleText : TextObject {
+		void clear() {
+			TextObject::clear();
+			actorSpeechMsg = center = wrap = false;
+		}
 		bool actorSpeechMsg;
+		bool center;
+		bool wrap;
 	};
 #endif
 
@@ -94,7 +105,7 @@ protected:
 
 public:
 	void processSubtitleQueue();
-	void addSubtitleToQueue(const byte *text, const Common::Point &pos, byte color, byte charset);
+	void addSubtitleToQueue(const byte *text, const Common::Point &pos, byte color, byte charset, bool center, bool wrap);
 	void clearSubtitleQueue();
 	void CHARSET_1() override;
 	bool isSmushActive() { return _smushActive; }
@@ -128,7 +139,7 @@ protected:
 	int getObjectIdFromOBIM(const byte *obim) override;
 
 	void createTextRenderer(GlyphRenderer_v7 *gr) override;
-	void enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped = false);
+	void enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrap = false);
 	void drawBlastTexts() override;
 	void actorTalk(const byte *msg) override;
 	void translateText(const byte *text, byte *trans_buff) override;
diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp
index 40c29c78613..7370b7692d5 100644
--- a/engines/scumm/smush/smush_player.cpp
+++ b/engines/scumm/smush/smush_player.cpp
@@ -627,26 +627,24 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 			string2[0] = 0;
 	}
 
-	const char *str2 = str;
 	while (str[0] == '^') {
 		switch (str[1]) {
 		case 'f':
-		{
 			fontId = str[3] - '0';
 			str += 4;
-		}
-		break;
+			break;
 		case 'c':
-		{
 			color = str[4] - '0' + 10 *(str[3] - '0');
 			str += 5;
-		}
 		break;
 		default:
 			error("invalid escape code in text string");
 		}
 	}
-	str = str2;
+
+	if (_vm->_game.id == GID_CMI && string2[0] != 0)
+		str = string2;
+
 	// This is a hack from the original COMI CJK interpreter. Its purpose is to avoid
 	// ugly combinations of two byte characters (rendered with the respective special
 	// font) and standard one byte (NUT font) characters (see bug #11947).
@@ -658,60 +656,11 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 	SmushFont *sf = getFont(fontId);
 	assert(sf != NULL);
 
-
-	// The HACK that used to be here to prevent bug #2220 is no longer necessary and
+	// The hack that used to be here to prevent bug #2220 is no longer necessary and
 	// has been removed. The font renderer can handle all ^codes it encounters (font
 	// changes on the fly will be ignored for Smush texts, since our code design does
 	// not permit it and the feature isn't used anyway).
 
-	// HACK. This is to prevent bug #2220. In updated Win95 dig
-	// there is such line:
-	//
-	// ^f01^c001LEAD TESTER
-	// Chris Purvis
-	// ^f01
-	// ^f01^c001WINDOWS COMPATIBILITY
-	// Chip Hinnenberg
-	// ^f01^c001WINDOWS TESTING
-	// Jim Davison
-	// Lynn Selk
-	//
-	// i.e. formatting exists not in the first line only
-	// We just strip that off and assume that neither font
-	// nor font color was altered. Proper fix would be to feed
-	// drawString() with each line sequentally
-/*	char *string3 = NULL, *sptr2;
-	const char *sptr;
-	
-	if (strchr(str, '^')) {
-		string3 = (char *)malloc(strlen(str) + 1);
-
-		for (sptr = str, sptr2 = string3; *sptr;) {
-			if (*sptr == '^') {
-				switch (sptr[1]) {
-				case 'f':
-					sptr += 4;
-					break;
-				case 'c':
-					sptr += 5;
-					break;
-				default:
-					error("invalid escape code in text string");
-				}
-			} else {
-				if (TextRenderer_v7::is2ByteCharacter(_vm->_language, *sptr))
-					*sptr2++ = *sptr++;
-				*sptr2++ = *sptr++;
-			}
-		}
-		*sptr2++ = *sptr++; // copy zero character
-		str = string3;
-	}*/
-
-	if (_vm->_game.id == GID_CMI && string2[0] != 0) {
-		str = string2;
-	}
-
 	// flags:
 	// bit 0 - center                  0x01
 	// bit 1 - not used (align right)  0x02
@@ -743,7 +692,6 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 	}
 
 	free(string);
-	//free(string3);
 }
 
 const char *SmushPlayer::getString(int id) {
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index cc37c7a72ce..899338bffcc 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -677,12 +677,12 @@ void ScummEngine::CHARSET_1() {
 		}
 
 		if (c == 13) {
-#ifdef ENABLE_SCUMM_7_8
+/*#ifdef ENABLE_SCUMM_7_8
 			if (_game.version >= 7 && subtitleLine != subtitleBuffer) {
 				((ScummEngine_v7 *)this)->addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID());
 				subtitleLine = subtitleBuffer;
 			}
-#endif
+#endif*/
 			if (!newLine())
 				break;
 			continue;
@@ -762,12 +762,12 @@ void ScummEngine::CHARSET_1() {
 	if (_game.platform == Common::kPlatformFMTowns && (c == 0 || c == 2 || c == 3))
 		memcpy(&_curStringRect, &_charset->_str, sizeof(Common::Rect));
 #endif
-
+/*
 #ifdef ENABLE_SCUMM_7_8
 	if (_game.version >= 7 && subtitleLine != subtitleBuffer) {
 		((ScummEngine_v7 *)this)->addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID());
 	}
-#endif
+#endif*/
 }
 
 void ScummEngine::drawString(int a, const byte *msg) {
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 604f3dce059..9b88ac3176f 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -33,7 +33,7 @@
 namespace Scumm {
 
 TextRenderer_v7::TextRenderer_v7(ScummEngine *vm, GlyphRenderer_v7 *gr)
-	: _gameId(vm->_game.id), _lang(vm->_language), _2byteCharWidth(vm->_2byteWidth), _screenWidth(vm->_screenWidth), _useCJKMode(vm->_useCJKMode), _lineBreakMarker(vm->_newLineCharacter), _gr(gr) {
+	: _gameId(vm->_game.id), _lang(vm->_language), _2byteCharWidth(vm->_2byteWidth), _screenWidth(vm->_screenWidth), _useCJKMode(vm->_useCJKMode), _spacing(vm->_language != Common::JA_JPN ? 1 : 0), _lineBreakMarker(vm->_newLineCharacter), _gr(gr) {
 }
 
 int TextRenderer_v7::getStringWidth(const char *str, uint numBytesMax) {
@@ -47,22 +47,25 @@ int TextRenderer_v7::getStringWidth(const char *str, uint numBytesMax) {
 	int font = _gr->setFont(-1);
 
 	while (*str && numBytesMax) {
-		while (str[0] == '^') {
-			switch (str[1]) {
-			case 'f':
+		if (*str == '^') {
+			if (str[1] == 'f') {
 				_gr->setFont(str[3] - '0');
 				str += 4;
-				break;
-			case 'c':
+				numBytesMax -= 4;
+				continue;
+			} else if (str[1] == 'c') {
 				str += 5;
-				break;
-			default:
-				error("CharsetRenderer::getStringWidth(): Invalid escape code in text string");
+				numBytesMax -= 5;
+				continue;
+			} else if (str[1] == 'l') {
+				str += 2;
+				numBytesMax -= 2;
+				continue;
 			}
 		}
 
 		if (is2ByteCharacter(_lang, *str)) {
-			width += _2byteCharWidth + (_lang != Common::JA_JPN ? 1 : 0);
+			width += _2byteCharWidth + _spacing;
 			++str;
 			--numBytesMax;
 		} else if (*str == '\n') {
@@ -91,17 +94,20 @@ int TextRenderer_v7::getStringHeight(const char *str, uint numBytesMax) {
 	int font = _gr->setFont(-1);
 
 	while (*str && numBytesMax) {
-		while (str[0] == '^') {
-			switch (str[1]) {
-			case 'f':
+		if (*str == '^') {
+			if (str[1] == 'f') {
 				_gr->setFont(str[3] - '0');
 				str += 4;
-				break;
-			case 'c':
+				numBytesMax -= 4;
+				continue;
+			} else if (str[1] == 'c') {
 				str += 5;
-				break;
-			default:
-				error("CharsetRenderer::getStringWidth(): Invalid escape code in text string");
+				numBytesMax -= 5;
+				continue;
+			} else if (str[1] == 'l') {
+				str += 2;
+				numBytesMax -= 2;
+				continue;
 			}
 		}
 
@@ -131,18 +137,21 @@ void TextRenderer_v7::drawSubstring(const char *str, uint numBytesMax, byte *buf
 		}
 	} else {
 		for (int i = 0; str[i] != 0 && numBytesMax; ++i) {
-			while (str[i] == '^') {
-				switch (str[i + 1]) {
-				case 'f':
+			if (str[i] == '^') {
+				if (str[i + 1] == 'f') {
 					_gr->setFont(str[i + 3] - '0');
+					i += 3;
+					numBytesMax -= 4;
+					continue;
+				} else if (str[i + 1] == 'c') {
+					col = str[i + 4] - '0' + 10 *(str[i + 3] - '0');
 					i += 4;
-					break;
-				case 'c':
-					col = str[4] - '0' + 10 *(str[3] - '0');
-					i += 5;
-					break;
-				default:
-					error("CharsetRenderer::getStringWidth(): Invalid escape code in text string");
+					numBytesMax -= 5;
+					continue;
+				} else if (str[i + 1] == 'l') {
+					i++;
+					numBytesMax -= 2;
+					continue;
 				}
 			}
 
@@ -197,7 +206,7 @@ void TextRenderer_v7::drawString(const char *str, byte *buffer, Common::Rect &cl
 
 	_gr->setFont(font);
 
-	clipRect.left = center ? x - maxWidth : x;
+	clipRect.left = center ? x - maxWidth / 2: x;
 	clipRect.right = MIN<int>(clipRect.right, clipRect.left + maxWidth);
 	clipRect.top = y2;
 	clipRect.bottom = y;
@@ -334,7 +343,7 @@ void ScummEngine_v7::createTextRenderer(GlyphRenderer_v7 *gr) {
 	_textV7 = new TextRenderer_v7(this, gr);
 }
 
-void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrapped) {
+void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrap) {
 	assert(_blastTextQueuePos + 1 <= ARRAYSIZE(_blastTextQueue));
 
 	if (_useCJKMode) {
@@ -347,7 +356,9 @@ void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byt
 
 	BlastText &bt = _blastTextQueue[_blastTextQueuePos];
 	convertMessageToString(text, bt.text, sizeof(bt.text));
-	if (!*bt.text)
+
+	// The original DIG interpreter expressly checks for " " strings here. And the game also sends these quite frequently...
+	if (!bt.text[0] || (bt.text[0] == (byte)' ' && !bt.text[1]))
 		return;
 
 	_blastTextQueuePos++;
@@ -356,13 +367,11 @@ void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byt
 	bt.color = color;
 	bt.charset = charset;
 	bt.center = center;
-	bt.wrap = wrapped;
+	bt.wrap = wrap;
 }
 
 void ScummEngine_v7::drawBlastTexts() {
 	VirtScreen *vs = &_virtscr[kMainVirtScreen];
-	Common::Rect _defaultTextClipRect = Common::Rect(vs->w, vs->h);
-	Common::Rect _wrappedTextClipRect = _game.id == GID_CMI ? Common::Rect(10, 10, 630, 470) : _defaultTextClipRect;
 
 	for (int i = 0; i < _blastTextQueuePos; i++) {
 		BlastText &bt = _blastTextQueue[i];
@@ -371,10 +380,10 @@ void ScummEngine_v7::drawBlastTexts() {
 
 		if (bt.wrap) {
 			bt.rect = _wrappedTextClipRect;
-			_textV7->drawStringWrap((const char*)bt.text, (byte*)vs->getPixels(0, 0), bt.rect, bt.xpos, bt.ypos, vs->pitch, bt.color, bt.center);
+			_textV7->drawStringWrap((const char*)bt.text, (byte*)vs->getBasePtr(0, 0), bt.rect, bt.xpos, bt.ypos, vs->pitch, bt.color, bt.center);
 		} else {
 			bt.rect = _defaultTextClipRect;
-			_textV7->drawString((const char*)bt.text, (byte*)vs->getPixels(0, 0), bt.rect, bt.xpos, bt.ypos, vs->pitch, bt.color, bt.center);
+			_textV7->drawString((const char*)bt.text, (byte*)vs->getBasePtr(0, 0), bt.rect, bt.xpos, bt.ypos, vs->pitch, bt.color, bt.center);
 		}
 
 		markRectAsDirty(vs->number, bt.rect);
@@ -400,11 +409,11 @@ void ScummEngine_v7::processSubtitleQueue() {
 		if (!st->actorSpeechMsg && (!ConfMan.getBool("subtitles") || VAR(VAR_VOICE_MODE) == 0))
 			// no subtitles and there's a speech variant of the message, don't display the text
 			continue;
-		enqueueText(st->text, st->xpos, st->ypos, st->color, st->charset, false);
+		enqueueText(st->text, st->xpos, st->ypos, st->color, st->charset, st->center, st->wrap);
 	}
 }
 
-void ScummEngine_v7::addSubtitleToQueue(const byte *text, const Common::Point &pos, byte color, byte charset) {
+void ScummEngine_v7::addSubtitleToQueue(const byte *text, const Common::Point &pos, byte color, byte charset, bool center, bool wrap) {
 	if (text[0] && strcmp((const char *)text, " ") != 0) {
 		assert(_subtitleQueuePos < ARRAYSIZE(_subtitleQueue));
 		SubtitleText *st = &_subtitleQueue[_subtitleQueuePos];
@@ -420,6 +429,8 @@ void ScummEngine_v7::addSubtitleToQueue(const byte *text, const Common::Point &p
 		st->color = color;
 		st->charset = charset;
 		st->actorSpeechMsg = _haveActorSpeechMsg;
+		st->center = center;
+		st->wrap = wrap;
 		++_subtitleQueuePos;
 	}
 }
@@ -435,10 +446,6 @@ void ScummEngine_v7::CHARSET_1() {
 		return;
 	}
 
-	byte subtitleBuffer[2048];
-	byte *subtitleLine = subtitleBuffer;
-	Common::Point subtitlePos;
-
 	processSubtitleQueue();
 
 	if (!_haveMsg)
@@ -452,21 +459,20 @@ void ScummEngine_v7::CHARSET_1() {
 	if (a && _string[0].overhead) {
 		int s;
 
-		_string[0].xpos = a->getPos().x - _virtscr[kMainVirtScreen].xstart;
+		_string[0].xpos = a->getPos().x + _screenWidth / 2 - camera._cur.x;
 		s = a->_scalex * a->_talkPosX / 255;
 		_string[0].xpos += (a->_talkPosX - s) / 2 + s;
 
-		_string[0].ypos = a->getPos().y - a->getElevation() - _screenTop;
+		int yyy1 = a->getPos().y;
+		int yyy2 = a->getElevation();
+
+		_string[0].ypos = a->getPos().y - a->getElevation() + _screenHeight / 2 - camera._cur.y;
 		s = a->_scaley * a->_talkPosY / 255;
 		_string[0].ypos += (a->_talkPosY - s) / 2 + s;
 	}
 
 	_charset->setColor(_charsetColor);
-
-	if (a && a->_charset)
-		_charset->setCurID(a->_charset);
-	else
-		_charset->setCurID(_string[0].charset);
+	_charset->setCurID(_string[0].charset);
 
 	if (_talkDelay)
 		return;
@@ -482,110 +488,19 @@ void ScummEngine_v7::CHARSET_1() {
 		a->runActorTalkScript(a->_talkStartFrame);
 	}
 
-	if (!_keepText) {
+	if (!_keepText)
 		clearSubtitleQueue();
-		_nextLeft = _string[0].xpos;
-		_nextTop = _string[0].ypos + _screenTop;
-	}
-
-	_charset->_disableOffsX = _charset->_firstChar = !_keepText;
 
 	_talkDelay = VAR(VAR_DEFAULT_TALK_DELAY);
-	for (int i = _charsetBufPos; _charsetBuffer[i]; ++i) {
+	int newPos = _charsetBufPos;
+	while (_charsetBuffer[newPos++])
 		_talkDelay += VAR(VAR_CHARINC);
-	}
 
-	if (_string[0].wrapping) {
-		_charset->addLinebreaks(0, _charsetBuffer, _charsetBufPos, _screenWidth - 20);
-
-		struct { int pos, w; } substring[10];
-		int count = 0;
-		int maxLineWidth = 0;
-		int lastPos = 0;
-		int code = 0;
-		while (handleNextCharsetCode(a, &code)) {
-			if (code == 13 || code == 0) {
-				*subtitleLine++ = '\0';
-				assert(count < 10);
-				substring[count].w = _charset->getStringWidth(0, subtitleBuffer + lastPos);
-				if (maxLineWidth < substring[count].w) {
-					maxLineWidth = substring[count].w;
-				}
-				substring[count].pos = lastPos;
-				++count;
-				lastPos = subtitleLine - subtitleBuffer;
-			} else {
-				*subtitleLine++ = code;
-				*subtitleLine = '\0';
-			}
-			if (code == 0) {
-				break;
-			}
-		}
-
-		int h = count * _charset->getFontHeight();
-		h += _charset->getFontHeight() / 2;
-		subtitlePos.y = _string[0].ypos;
-		if (subtitlePos.y + h > _screenHeight - 10) {
-			subtitlePos.y = _screenHeight - 10 - h;
-		}
-		if (subtitlePos.y < 10) {
-			subtitlePos.y = 10;
-		}
+	Common::Point subtitlePos(_string[0].xpos, _string[0].ypos);
+	addSubtitleToQueue(_charsetBuffer + _charsetBufPos, subtitlePos, _charsetColor, _charset->getCurID(), _string[0].center, _string[0].wrapping);
+	_charsetBufPos = newPos;
 
-		for (int i = 0; i < count; ++i) {
-			subtitlePos.x = _string[0].xpos;
-			if (_string[0].center) {
-				if (subtitlePos.x + maxLineWidth / 2 > _screenWidth - 10) {
-					subtitlePos.x = _screenWidth - 10 - maxLineWidth / 2;
-				}
-				if (subtitlePos.x - maxLineWidth / 2 < 10) {
-					subtitlePos.x = 10 + maxLineWidth / 2;
-				}
-				subtitlePos.x -= substring[i].w / 2;
-			} else {
-				if (subtitlePos.x + maxLineWidth > _screenWidth - 10) {
-					subtitlePos.x = _screenWidth - 10 - maxLineWidth;
-				}
-				if (subtitlePos.x - maxLineWidth < 10) {
-					subtitlePos.x = 10;
-				}
-			}
-			if (subtitlePos.y < _screenHeight - 10) {
-				addSubtitleToQueue(subtitleBuffer + substring[i].pos, subtitlePos, _charsetColor, _charset->getCurID());
-			}
-			subtitlePos.y += _charset->getFontHeight();
-		}
-	} else {
-		int code = 0;
-		subtitlePos.y = _string[0].ypos;
-		if (subtitlePos.y < 10) {
-			subtitlePos.y = 10;
-		}
-		while (handleNextCharsetCode(a, &code)) {
-			if (code == 13 || code == 0) {
-				subtitlePos.x = _string[0].xpos;
-				if (_string[0].center) {
-					subtitlePos.x -= _charset->getStringWidth(0, subtitleBuffer) / 2;
-				}
-				if (subtitlePos.x < 10) {
-					subtitlePos.x = 10;
-				}
-				if (subtitlePos.y < _screenHeight - 10) {
-					addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID());
-					subtitlePos.y += _charset->getFontHeight();
-				}
-				subtitleLine = subtitleBuffer;
-			} else {
-				*subtitleLine++ = code;
-			}
-			*subtitleLine = '\0';
-			if (code == 0) {
-				break;
-			}
-		}
-	}
-	_haveMsg = (_game.version == 8) ? 2 : 1;
+	_haveMsg = VAR(VAR_HAVE_MSG) = (_game.version == 8 && _string[0].no_talk_anim) ? 2 : 1;
 	_keepText = false;
 	_string[0] = saveStr;
 }
diff --git a/engines/scumm/string_v7.h b/engines/scumm/string_v7.h
index a2e8487ac12..6ce2edc3ca8 100644
--- a/engines/scumm/string_v7.h
+++ b/engines/scumm/string_v7.h
@@ -61,6 +61,7 @@ private:
 	const Common::Language _lang;
 	const byte _gameId;
 	const bool _useCJKMode;
+	const int _spacing;
 	const byte _2byteCharWidth;
 	const byte _lineBreakMarker;
 	const uint16 _screenWidth;


Commit: 37e58e8420fcb0208a8bda78b9f9070ed5da6975
    https://github.com/scummvm/scummvm/commit/37e58e8420fcb0208a8bda78b9f9070ed5da6975
Author: athrxx (athrxx at scummvm.org)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: (SCUMM7/8) - minor fix to string escape code handling

(the old values do not get restored)

Changed paths:
    engines/scumm/charset.h
    engines/scumm/charset_v7.h
    engines/scumm/smush/smush_font.h
    engines/scumm/string_v7.cpp
    engines/scumm/string_v7.h


diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index 1d88434a028..59c4fc73088 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -321,6 +321,7 @@ public:
 	int getCharHeight(uint16 chr) const override { return ((chr & 0x80) && _vm->_useCJKMode) ? _vm->_2byteHeight + 1 : _fontHeight; }
 	int getFontHeight() const override { return _fontHeight; }
 	int setFont(int) override { return 0; }
+	EscapeCodeFormat escapeCodeFormat() const override { return kEscCodesNONE; }
 
 private:
 	const int _spacing;
@@ -335,6 +336,7 @@ public:
 
 	void setCurID(int32 id) override;
 	int setFont(int id) override;
+	EscapeCodeFormat escapeCodeFormat() const override { return kEscCodesNUT; }
 
 	int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) override;
 	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) override;
diff --git a/engines/scumm/charset_v7.h b/engines/scumm/charset_v7.h
index 8ecdefb39e3..107b38c6a5c 100644
--- a/engines/scumm/charset_v7.h
+++ b/engines/scumm/charset_v7.h
@@ -31,12 +31,18 @@ namespace Scumm {
 
 class GlyphRenderer_v7 {
 public:
+	enum EscapeCodeFormat {
+		kEscCodesNONE = 0,
+		kEscCodesNUT = 1
+	};
+
 	virtual int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) = 0;
 	virtual int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) = 0;
 	virtual int getCharWidth(uint16 chr) const = 0;
 	virtual int getCharHeight(uint16 chr) const = 0;
 	virtual int getFontHeight() const = 0;
 	virtual int setFont(int id) = 0;
+	virtual EscapeCodeFormat escapeCodeFormat() const = 0;
 };
 
 } // End of namespace Scumm
diff --git a/engines/scumm/smush/smush_font.h b/engines/scumm/smush/smush_font.h
index d4eac2ce291..fa498c733a2 100644
--- a/engines/scumm/smush/smush_font.h
+++ b/engines/scumm/smush/smush_font.h
@@ -59,6 +59,7 @@ private:
 	int getCharHeight(uint16 chr) const override { return NutRenderer::getCharHeight(chr & 0xFF); }
 	int getFontHeight() const override { return NutRenderer::getFontHeight(); }
 	int setFont(int) override { return 0; }
+	EscapeCodeFormat escapeCodeFormat() const override { return kEscCodesNUT; }
 
 	TextRenderer_v7 *_r;
 	const bool _hardcodedFontColors;
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 9b88ac3176f..03f0ca8e134 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -32,8 +32,16 @@
 
 namespace Scumm {
 
-TextRenderer_v7::TextRenderer_v7(ScummEngine *vm, GlyphRenderer_v7 *gr)
-	: _gameId(vm->_game.id), _lang(vm->_language), _2byteCharWidth(vm->_2byteWidth), _screenWidth(vm->_screenWidth), _useCJKMode(vm->_useCJKMode), _spacing(vm->_language != Common::JA_JPN ? 1 : 0), _lineBreakMarker(vm->_newLineCharacter), _gr(gr) {
+TextRenderer_v7::TextRenderer_v7(ScummEngine *vm, GlyphRenderer_v7 *gr)	:
+	_gameId(vm->_game.id),
+	_lang(vm->_language),
+	_2byteCharWidth(vm->_2byteWidth),
+	_screenWidth(vm->_screenWidth),
+	_useCJKMode(vm->_useCJKMode),
+	_spacing(vm->_language != Common::JA_JPN ? 1 : 0),
+	_lineBreakMarker(vm->_newLineCharacter),
+	_processEscapeCodes (gr->escapeCodeFormat() == GlyphRenderer_v7::kEscCodesNUT),
+	_gr(gr) {
 }
 
 int TextRenderer_v7::getStringWidth(const char *str, uint numBytesMax) {
@@ -44,10 +52,9 @@ int TextRenderer_v7::getStringWidth(const char *str, uint numBytesMax) {
 
 	int maxWidth = 0;
 	int width = 0;
-	int font = _gr->setFont(-1);
 
 	while (*str && numBytesMax) {
-		if (*str == '^') {
+		if (_processEscapeCodes && *str == '^') {
 			if (str[1] == 'f') {
 				_gr->setFont(str[3] - '0');
 				str += 4;
@@ -78,8 +85,6 @@ int TextRenderer_v7::getStringWidth(const char *str, uint numBytesMax) {
 		--numBytesMax;
 	}
 
-	_gr->setFont(font);
-
 	return MAX<int>(width, maxWidth);
 }
 
@@ -91,10 +96,9 @@ int TextRenderer_v7::getStringHeight(const char *str, uint numBytesMax) {
 
 	int totalHeight = 0;
 	int lineHeight = 0;
-	int font = _gr->setFont(-1);
 
 	while (*str && numBytesMax) {
-		if (*str == '^') {
+		if (_processEscapeCodes && *str == '^') {
 			if (str[1] == 'f') {
 				_gr->setFont(str[3] - '0');
 				str += 4;
@@ -125,8 +129,6 @@ int TextRenderer_v7::getStringHeight(const char *str, uint numBytesMax) {
 		--numBytesMax;
 	}
 
-	_gr->setFont(font);
-
 	return totalHeight + (lineHeight ? lineHeight : _gr->getFontHeight()) + 1;
 }
 
@@ -137,7 +139,7 @@ void TextRenderer_v7::drawSubstring(const char *str, uint numBytesMax, byte *buf
 		}
 	} else {
 		for (int i = 0; str[i] != 0 && numBytesMax; ++i) {
-			if (str[i] == '^') {
+			if (_processEscapeCodes && str[i] == '^') {
 				if (str[i + 1] == 'f') {
 					_gr->setFont(str[i + 3] - '0');
 					i += 3;
@@ -186,7 +188,6 @@ void TextRenderer_v7::drawString(const char *str, byte *buffer, Common::Rect &cl
 
 	int y2 = y;
 	int maxWidth = 0;
-	int font = _gr->setFont(-1);
 
 	for (int pos = 0; pos <= totalLen; ++pos) {
 		if (str[pos] != '\0' && str[pos] != '\n')
@@ -204,8 +205,6 @@ void TextRenderer_v7::drawString(const char *str, byte *buffer, Common::Rect &cl
 		lineStart = pos + 1;
 	}
 
-	_gr->setFont(font);
-
 	clipRect.left = center ? x - maxWidth / 2: x;
 	clipRect.right = MIN<int>(clipRect.right, clipRect.left + maxWidth);
 	clipRect.top = y2;
@@ -321,7 +320,6 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 	}
 
 	int y2 = y;
-	int font = _gr->setFont(-1);
 
 	for (int i = 0; i < numSubstrings; i++) {
 		int xpos = center ? x - substrWidths[i] / 2 : x;
@@ -330,8 +328,6 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 		y += getStringHeight(str + substrStart[i], len);
 	}
 
-	_gr->setFont(font);
-
 	clipRect.left = center ? x - maxWidth / 2 : x;
 	clipRect.right = MIN<int>(clipRect.right, clipRect.left + maxWidth);
 	clipRect.top = y2;
diff --git a/engines/scumm/string_v7.h b/engines/scumm/string_v7.h
index 6ce2edc3ca8..33e432ada94 100644
--- a/engines/scumm/string_v7.h
+++ b/engines/scumm/string_v7.h
@@ -64,6 +64,7 @@ private:
 	const int _spacing;
 	const byte _2byteCharWidth;
 	const byte _lineBreakMarker;
+	const bool _processEscapeCodes;
 	const uint16 _screenWidth;
 	GlyphRenderer_v7 *_gr;
 };


Commit: f5f49d1b3ff3239378a9647dec4ebfa6f4a82d4f
    https://github.com/scummvm/scummvm/commit/f5f49d1b3ff3239378a9647dec4ebfa6f4a82d4f
Author: athrxx (athrxx at scummvm.org)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: (SCUMM7/8) - improve ingame text wrapping and positioning for DIG and COMI

This fixes a couple of regressions (mostly camera related). For DIG it seems that the older version of the interpreter uses CharsetRenderClassic type text display while the newer version uses COMI style. I stick with the COMI style, but with the necessary tweeks to make it pixel perfect for all versions.

COMI (I've tested English and Chinese) seemed to be pixel perfect without having to do much, same for DIG Chinese (new interpreter version). Even the verbs seem to be fine, although I haven't changed anything there yet (applies to COMI only afaik, I think DIG doesn't have text verbs).

DIG English (old interpreter version) is another matter. That one wraps text quite differently. Our version looked quite different from the original. But now it also seems to be fine. I even fixed a weirdo problem with the character width (It seems that the fonts have some kerning data; if we use that like we did the 'a' character gets displayed too narrow by 1 pixel. So all lines containing 'a' characters were slightly off). I have examined several strings in several camera settings for pixel perfect drawing in DIG and all seems good now...

FT: I'll do that separately. I haven't even checked if it needs any fixing. Maybe it is fine already. But it sure would be nice to get rid of any redundancies...

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/charset.h
    engines/scumm/charset_v7.h
    engines/scumm/nut_renderer.cpp
    engines/scumm/scumm.cpp
    engines/scumm/scumm_v7.h
    engines/scumm/smush/smush_font.h
    engines/scumm/string_v7.cpp
    engines/scumm/string_v7.h


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 8907849df70..aeb2b2eca2f 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1965,42 +1965,50 @@ void CharsetRendererMac::setColor(byte color) {
 }
 
 #ifdef ENABLE_SCUMM_7_8
-int CharsetRendererV7::draw2byte(byte*, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) {
-	if (_vm->isScummvmKorTarget()) {
-		enableShadow(true);
-		_charPtr = _vm->get2byteCharPtr(chr);
-		_origWidth = _width = _vm->_2byteWidth;
-		_origHeight = _height = _vm->_2byteHeight;
-		_offsX = _offsY = 0;
-	} else if (!prepareDraw(chr)) {
-		return 0;
+int CharsetRendererV7::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) {
+	const byte *src = _vm->get2byteCharPtr(chr);
+	buffer += (y * pitch + x);
+	_origWidth = _vm->_2byteWidth;
+	_origHeight = _vm->_2byteHeight;
+	uint8 bits = 0;
+	pitch -= _origWidth;
+	while (_origHeight--) {
+		for (x = 0; x < _origWidth; ++x) {
+			if ((x % 8) == 0)
+				bits = *src++;
+			if (bits & revBitMask(x % 8)) {
+				buffer[0] = col;
+				buffer[1] = _shadowColor;
+			}
+			buffer++;
+		}
+		buffer += pitch;
 	}
-
-	_color = col;
-	VirtScreen &vs = _vm->_virtscr[kMainVirtScreen];
-	drawBits1(vs, x + vs.xstart, y, _charPtr, MAX<int>(clipRect.top, y), _origWidth, _origHeight);
-
 	return _origWidth + _spacing;
 }
 
 int CharsetRendererV7::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) {
 	if (!prepareDraw(chr))
 		return 0;
-
-	if (_vm->isScummvmKorTarget()) {
-		_origWidth = _width;
-		_origHeight = _height;
-	}
-
 	_width = getCharWidth(chr);
-
 	_vm->_charsetColorMap[1] = col;
 	VirtScreen &vs = _vm->_virtscr[kMainVirtScreen];
-	drawBitsN(vs, buffer + (y + _offsY) * vs.pitch + vs.xstart + x, _charPtr, *_fontPtr, y, _origWidth, _origHeight);
-
+	drawBitsN(vs, buffer + (y + _offsY) * vs.pitch + x, _charPtr, *_fontPtr, y, _origWidth, _origHeight);
 	return _width;
 }
 
+
+int CharsetRendererV7::getCharWidth(uint16 chr) const {
+	if ((chr & 0x80) && _vm->_useCJKMode)
+		return _vm->_2byteWidth + _spacing;
+
+	int offs = READ_LE_UINT32(_fontPtr + (chr & 0xFF) * 4 + 4);
+	// SCUMM7 does not use the "kerning" from _fontPtr[offs + 2] here (compare CharsetRendererClassic::getCharWidth()
+	// to see the difference. Verfied from disasm and comparison with DOSBox (hard to notice, but e. g. the 'a' character
+	// used to be too narrow by 1 pixel, so all lines containing that character were slightly off).
+	return offs ? _fontPtr[offs] : 0;
+}
+
 CharsetRendererNut::CharsetRendererNut(ScummEngine *vm) : CharsetRenderer(vm) {
 	_current = 0;
 
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index 59c4fc73088..28090466826 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -310,21 +310,21 @@ public:
 #ifdef ENABLE_SCUMM_7_8
 class CharsetRendererV7 : public CharsetRendererClassic, public GlyphRenderer_v7 {
 public:
-	CharsetRendererV7(ScummEngine *vm) : CharsetRendererClassic(vm), _spacing(vm->_useCJKMode && vm->_language != Common::JA_JPN ? 1 : 0) {}
+	CharsetRendererV7(ScummEngine *vm) : CharsetRendererClassic(vm), _spacing(vm->_useCJKMode && vm->_language != Common::JA_JPN ? 1 : 0), _newStyle(vm->_useCJKMode) {}
 	~CharsetRendererV7() override {};
 
 	void printChar(int chr, bool ignoreCharsetMask) override { error("CharsetRendererV7::printChar(): Unexpected legacy function call"); }
 
 	int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) override;
 	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) override;
-	int getCharWidth(uint16 chr) const override { return ((chr & 0x80) && _vm->_useCJKMode) ? _vm->_2byteWidth + _spacing : CharsetRendererClassic::getCharWidth(chr);	}
-	int getCharHeight(uint16 chr) const override { return ((chr & 0x80) && _vm->_useCJKMode) ? _vm->_2byteHeight + 1 : _fontHeight; }
+	int getCharWidth(uint16 chr) const override;
+	int getCharHeight(uint16 chr) const override { return ((chr & 0x80) && _vm->_useCJKMode) ? _vm->_2byteHeight : _fontHeight; }
 	int getFontHeight() const override { return _fontHeight; }
 	int setFont(int) override { return 0; }
-	EscapeCodeFormat escapeCodeFormat() const override { return kEscCodesNONE; }
-
+	bool newStyleWrapping() const override { return _newStyle; }
 private:
 	const int _spacing;
+	const bool _newStyle;
 };
 
 class CharsetRendererNut : public CharsetRenderer, public GlyphRenderer_v7 {
@@ -336,7 +336,7 @@ public:
 
 	void setCurID(int32 id) override;
 	int setFont(int id) override;
-	EscapeCodeFormat escapeCodeFormat() const override { return kEscCodesNUT; }
+	bool newStyleWrapping() const override { return true; }
 
 	int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) override;
 	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) override;
diff --git a/engines/scumm/charset_v7.h b/engines/scumm/charset_v7.h
index 107b38c6a5c..53fd01235bc 100644
--- a/engines/scumm/charset_v7.h
+++ b/engines/scumm/charset_v7.h
@@ -31,18 +31,13 @@ namespace Scumm {
 
 class GlyphRenderer_v7 {
 public:
-	enum EscapeCodeFormat {
-		kEscCodesNONE = 0,
-		kEscCodesNUT = 1
-	};
-
 	virtual int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) = 0;
 	virtual int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) = 0;
 	virtual int getCharWidth(uint16 chr) const = 0;
 	virtual int getCharHeight(uint16 chr) const = 0;
 	virtual int getFontHeight() const = 0;
 	virtual int setFont(int id) = 0;
-	virtual EscapeCodeFormat escapeCodeFormat() const = 0;
+	virtual bool newStyleWrapping() const = 0;
 };
 
 } // End of namespace Scumm
diff --git a/engines/scumm/nut_renderer.cpp b/engines/scumm/nut_renderer.cpp
index 7274cdee1c4..373b31e2fd9 100644
--- a/engines/scumm/nut_renderer.cpp
+++ b/engines/scumm/nut_renderer.cpp
@@ -386,6 +386,7 @@ int NutRenderer::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, in
 	if (minX)
 		dst += minX;
 
+	int clipWdth = (_chars[chr].width - width);
 	char color = (col != -1) ? col : 1;
 
 	if (_vm->_game.version == 7) {
@@ -396,6 +397,7 @@ int NutRenderer::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, in
 					if (value != _chars[chr].transparency)
 						dst[i] = value;
 				}
+				src += clipWdth;
 				dst += pitch;
 			}
 		} else {
@@ -407,6 +409,7 @@ int NutRenderer::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, in
 					else if (value != _chars[chr].transparency)
 						dst[i] = 0;
 				}
+				src += clipWdth;
 				dst += pitch;
 			}
 		}
@@ -422,6 +425,7 @@ int NutRenderer::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, in
 					else if (value != _chars[chr].transparency)
 						dst[i] = value;
 				}
+				src += clipWdth;
 				dst += pitch;
 			}
 		} else {
@@ -431,6 +435,7 @@ int NutRenderer::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, in
 					if (value != _chars[chr].transparency)
 						dst[i] = (value == 1) ? color : value;
 				}
+				src += clipWdth;
 				dst += pitch;
 			}
 		}		
@@ -463,6 +468,7 @@ int NutRenderer::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, i
 		buffer += minX;
 	}
 
+	int clipWdth = (_vm->_2byteWidth - width);
 	byte bits = *src;
 	const byte *origSrc = src;
 
@@ -483,6 +489,10 @@ int NutRenderer::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, i
 				if (bits & revBitMask(i % 8))
 					dst[i] = drawColor;
 			}
+			for (int i = width; i < width + clipWdth; ++i) {
+				if (i % 8 == 0)
+					bits = *src++;
+			}
 			dst += pitch;
 		}
 	}
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 5feffff84e8..83c7810d341 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1076,8 +1076,9 @@ ScummEngine_v7::ScummEngine_v7(OSystem *syst, const DetectorResult &dr)
 	clearSubtitleQueue();
 
 	_textV7 = NULL;
+	_newTextRenderStyle = (_game.version == 8 || _language == Common::JA_JPN || _language == Common::KO_KOR || _language == Common::ZH_TWN);
 	_defaultTextClipRect = Common::Rect(_screenWidth, _screenHeight);
-	_wrappedTextClipRect = Common::Rect(10, 10, _screenWidth - 10, _screenHeight - 10);
+	_wrappedTextClipRect = _newTextRenderStyle ? Common::Rect(10, 10, _screenWidth - 10, _screenHeight - 10)  : Common::Rect(_screenWidth, _screenHeight);
 
 	_game.features |= GF_NEW_COSTUMES;
 }
diff --git a/engines/scumm/scumm_v7.h b/engines/scumm/scumm_v7.h
index 4d5eb45d423..e4fe1bc85f0 100644
--- a/engines/scumm/scumm_v7.h
+++ b/engines/scumm/scumm_v7.h
@@ -68,6 +68,7 @@ protected:
 	TextRenderer_v7 *_textV7;
 	Common::Rect _defaultTextClipRect;
 	Common::Rect _wrappedTextClipRect;
+	bool _newTextRenderStyle;
 
 	int _verbLineSpacing;
 	bool _existLanguageFile;
diff --git a/engines/scumm/smush/smush_font.h b/engines/scumm/smush/smush_font.h
index fa498c733a2..edffda46d7e 100644
--- a/engines/scumm/smush/smush_font.h
+++ b/engines/scumm/smush/smush_font.h
@@ -59,7 +59,7 @@ private:
 	int getCharHeight(uint16 chr) const override { return NutRenderer::getCharHeight(chr & 0xFF); }
 	int getFontHeight() const override { return NutRenderer::getFontHeight(); }
 	int setFont(int) override { return 0; }
-	EscapeCodeFormat escapeCodeFormat() const override { return kEscCodesNUT; }
+	bool newStyleWrapping() const override { return true; }
 
 	TextRenderer_v7 *_r;
 	const bool _hardcodedFontColors;
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 03f0ca8e134..68a0e21e08b 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -40,7 +40,7 @@ TextRenderer_v7::TextRenderer_v7(ScummEngine *vm, GlyphRenderer_v7 *gr)	:
 	_useCJKMode(vm->_useCJKMode),
 	_spacing(vm->_language != Common::JA_JPN ? 1 : 0),
 	_lineBreakMarker(vm->_newLineCharacter),
-	_processEscapeCodes (gr->escapeCodeFormat() == GlyphRenderer_v7::kEscCodesNUT),
+	_newStyle (gr->newStyleWrapping()),
 	_gr(gr) {
 }
 
@@ -54,7 +54,7 @@ int TextRenderer_v7::getStringWidth(const char *str, uint numBytesMax) {
 	int width = 0;
 
 	while (*str && numBytesMax) {
-		if (_processEscapeCodes && *str == '^') {
+		if (_newStyle && *str == '^') {
 			if (str[1] == 'f') {
 				_gr->setFont(str[3] - '0');
 				str += 4;
@@ -69,17 +69,30 @@ int TextRenderer_v7::getStringWidth(const char *str, uint numBytesMax) {
 				numBytesMax -= 2;
 				continue;
 			}
+		} else if (!_newStyle && *str == '@') {
+			str++;
+			numBytesMax--;
+			continue;
 		}
 
 		if (is2ByteCharacter(_lang, *str)) {
 			width += _2byteCharWidth + _spacing;
 			++str;
 			--numBytesMax;
-		} else if (*str == '\n') {
+		} else if (_newStyle && *str == '\n') {
 			maxWidth = MAX<int>(width, maxWidth);
 			width = 0;
-		} else if (*str != '\r' && *str != _lineBreakMarker) {
-			width += _gr->getCharWidth(*str);
+		} else if (!_newStyle && *str == '\r') {
+			break;
+		} else if (!_newStyle && *str == '\xff') {
+			++str;
+			--numBytesMax;
+			if (*str == 0 || *str == 3 || *str == 9 || *str == 1 || *str == 2 /*|| *str == 8*/)
+				return width;
+			// No handling for this, atm, SCUMM7 does not have these anyway
+			assert(*str != 8);
+		} else if (*str != '\r' && !(_newStyle && *str == _lineBreakMarker)) {
+			width += _gr->getCharWidth((uint8)*str);
 		}
 		++str;
 		--numBytesMax;
@@ -98,7 +111,7 @@ int TextRenderer_v7::getStringHeight(const char *str, uint numBytesMax) {
 	int lineHeight = 0;
 
 	while (*str && numBytesMax) {
-		if (_processEscapeCodes && *str == '^') {
+		if (_newStyle && *str == '^') {
 			if (str[1] == 'f') {
 				_gr->setFont(str[3] - '0');
 				str += 4;
@@ -139,7 +152,7 @@ void TextRenderer_v7::drawSubstring(const char *str, uint numBytesMax, byte *buf
 		}
 	} else {
 		for (int i = 0; str[i] != 0 && numBytesMax; ++i) {
-			if (_processEscapeCodes && str[i] == '^') {
+			if (_newStyle && str[i] == '^') {
 				if (str[i + 1] == 'f') {
 					_gr->setFont(str[i + 3] - '0');
 					i += 3;
@@ -223,6 +236,17 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 	Common::String spaceSeparators(Common::String::format(" %c", (char)_lineBreakMarker));
 	Common::String breakSeparators(Common::String::format(" \n%c", (char)_lineBreakMarker));
 
+	// This wouldn't work properly (at least not without careful extra work) with old style escape codes.
+	// I doubt very much that any SCUMM7 game would have them, since they don't always work properly when
+	// combined with speech playback and they also would be difficult to maintain with language bundles.
+	// Let's check for these, so I'll definitely notice during my tests...
+	if (!_newStyle) {
+		Common::String invalidChars("@\xff\x03\x09\x01\x02\x08");
+		for (int i = 0; i < len; ++i)
+		if (invalidChars.contains(str[i]))
+			assert(true == false);
+	}
+
 	int16 substrByteLength[SCUMM7_MAX_STRINGS];
 	memset(substrByteLength, 0, sizeof(substrByteLength));
 	int16 substrWidths[SCUMM7_MAX_STRINGS];
@@ -307,22 +331,26 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 	if (y < clipRect.top)
 		y = clipRect.top;
 
-	if (center) {
-		if (x + (maxWidth >> 1) > clipRect.right)
-			x = clipRect.right - (maxWidth >> 1);
-		if (x - (maxWidth >> 1) < clipRect.left)
-			x = clipRect.left + (maxWidth >> 1);
-	} else {
-		if (x > clipRect.right - maxWidth)
-			x = clipRect.right - maxWidth;
-		if (x < clipRect.left)
-			x = clipRect.left;
+	if (_newStyle) {
+		if (center) {
+			if (x + (maxWidth >> 1) > clipRect.right)
+				x = clipRect.right - (maxWidth >> 1);
+			if (x - (maxWidth >> 1) < clipRect.left)
+				x = clipRect.left + (maxWidth >> 1);
+		} else {
+			if (x > clipRect.right - maxWidth)
+				x = clipRect.right - maxWidth;
+			if (x < clipRect.left)
+				x = clipRect.left;
+		}
 	}
 
 	int y2 = y;
 
 	for (int i = 0; i < numSubstrings; i++) {
 		int xpos = center ? x - substrWidths[i] / 2 : x;
+		if (!_newStyle)
+			xpos = CLIP<int>(xpos, clipRect.left, _screenWidth - substrWidths[i]);
 		len = substrByteLength[i] > 0 ? substrByteLength[i] : 0;
 		drawSubstring(str + substrStart[i], len, buffer, clipRect, xpos, y, pitch, col);
 		y += getStringHeight(str + substrStart[i], len);
@@ -344,7 +372,7 @@ void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byt
 
 	if (_useCJKMode) {
 		// The Dig expressly checks for x == 160 && y == 189 && charset == 3. Usually, if the game wants to print CJK text at the bottom
-		// of the screen it will use y = 183. So maybe this is a hack to fix some script texts that weren forgotten in the CJK converting
+		// of the screen it will use y = 183. So maybe this is a hack to fix some script texts that were forgotten in the CJK converting
 		// process.
 		if (_game.id == GID_DIG && x == 160 && y == 189 && charset == 3)
 			y -= 6;
@@ -376,12 +404,30 @@ void ScummEngine_v7::drawBlastTexts() {
 
 		if (bt.wrap) {
 			bt.rect = _wrappedTextClipRect;
-			_textV7->drawStringWrap((const char*)bt.text, (byte*)vs->getBasePtr(0, 0), bt.rect, bt.xpos, bt.ypos, vs->pitch, bt.color, bt.center);
+
+			// This is for the "narrow" paragraph wrapping type that the older interpreters (e. g. FT, DIG English) do.
+			if (!_newTextRenderStyle) {
+				bt.xpos = CLIP<int>(bt.xpos, 80, 240);
+				bt.ypos = CLIP<int>(bt.ypos, 1, 160);
+				int maxWidth = _string[0].right - bt.xpos - 1;
+				if (bt.center) {
+					if (maxWidth > bt.xpos)
+						maxWidth = bt.xpos;
+					bt.rect.left = MAX<int>(0, bt.xpos - maxWidth);
+					bt.rect.right = MIN<int>(_screenWidth, bt.xpos + maxWidth);
+				}
+			}
+
+			_textV7->drawStringWrap((const char*)bt.text, (byte*)vs->getPixels(0, _screenTop), bt.rect, bt.xpos, bt.ypos, vs->pitch, bt.color, bt.center);
 		} else {
 			bt.rect = _defaultTextClipRect;
-			_textV7->drawString((const char*)bt.text, (byte*)vs->getBasePtr(0, 0), bt.rect, bt.xpos, bt.ypos, vs->pitch, bt.color, bt.center);
+			_textV7->drawString((const char*)bt.text, (byte*)vs->getPixels(0, _screenTop), bt.rect, bt.xpos, bt.ypos, vs->pitch, bt.color, bt.center);
 		}
 
+		bt.rect.top += _screenTop;
+		bt.rect.bottom += _screenTop;
+		bt.rect.left;
+		bt.rect.right;
 		markRectAsDirty(vs->number, bt.rect);
 	}
 }
@@ -454,15 +500,11 @@ void ScummEngine_v7::CHARSET_1() {
 	StringTab saveStr = _string[0];
 	if (a && _string[0].overhead) {
 		int s;
-
-		_string[0].xpos = a->getPos().x + _screenWidth / 2 - camera._cur.x;
+		_string[0].xpos = a->getPos().x - _virtscr[kMainVirtScreen].xstart;
 		s = a->_scalex * a->_talkPosX / 255;
 		_string[0].xpos += (a->_talkPosX - s) / 2 + s;
 
-		int yyy1 = a->getPos().y;
-		int yyy2 = a->getElevation();
-
-		_string[0].ypos = a->getPos().y - a->getElevation() + _screenHeight / 2 - camera._cur.y;
+		_string[0].ypos = a->getPos().y - a->getElevation() - _screenTop;
 		s = a->_scaley * a->_talkPosY / 255;
 		_string[0].ypos += (a->_talkPosY - s) / 2 + s;
 	}
diff --git a/engines/scumm/string_v7.h b/engines/scumm/string_v7.h
index 33e432ada94..e4e1678e4a6 100644
--- a/engines/scumm/string_v7.h
+++ b/engines/scumm/string_v7.h
@@ -64,7 +64,7 @@ private:
 	const int _spacing;
 	const byte _2byteCharWidth;
 	const byte _lineBreakMarker;
-	const bool _processEscapeCodes;
+	const bool _newStyle;
 	const uint16 _screenWidth;
 	GlyphRenderer_v7 *_gr;
 };


Commit: 78835f031816fc1a5fcaa22bb71764cd2f2a36a7
    https://github.com/scummvm/scummvm/commit/78835f031816fc1a5fcaa22bb71764cd2f2a36a7
Author: athrxx (athrxx at scummvm.org)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: (SCUMM7/8) - start cleaning up text verb drawing

(this commit actually makes more of a mess than it cleans up anything and it also kills Hebrew, but it's easier for me not to squash this)

Changed paths:
    engines/scumm/script_v8.cpp
    engines/scumm/string_v7.cpp
    engines/scumm/string_v7.h
    engines/scumm/verbs.cpp


diff --git a/engines/scumm/script_v8.cpp b/engines/scumm/script_v8.cpp
index 054ab8a3c5a..fd6df11d658 100644
--- a/engines/scumm/script_v8.cpp
+++ b/engines/scumm/script_v8.cpp
@@ -980,7 +980,7 @@ void ScummEngine_v8::o8_verbOps() {
 		if (_language == Common::HE_ISR)
 			vs->curRect.right = _screenWidth - 1 - pop();
 		else
-			vs->curRect.left = vs->origLeft = pop();
+			vs->origLeft = pop();
 		break;
 	case 0x9B:		// SO_VERB_ON Turn verb on
 		vs->curmode = 1;
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 68a0e21e08b..9075515d955 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -362,6 +362,17 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 	clipRect.bottom = y;
 }
 
+Common::Rect TextRenderer_v7::calcStringDimensions(const char *str, int x, int y, bool center) {
+	int wdth = getStringWidth(str);
+	if (_gameId == GID_CMI && _useCJKMode)
+		y += 2;
+	
+	if (center)
+		x -= (wdth >> 1);
+
+	return Common::Rect(x, y, x + wdth, y + getStringHeight(str));
+}
+
 void ScummEngine_v7::createTextRenderer(GlyphRenderer_v7 *gr) {
 	assert(gr);
 	_textV7 = new TextRenderer_v7(this, gr);
diff --git a/engines/scumm/string_v7.h b/engines/scumm/string_v7.h
index e4e1678e4a6..a692657751a 100644
--- a/engines/scumm/string_v7.h
+++ b/engines/scumm/string_v7.h
@@ -43,6 +43,8 @@ public:
 	int getStringWidth(const char *str) { return getStringWidth(str, 100000); }
 	int getStringHeight(const char *str) { return getStringHeight(str, 100000); }
 
+	Common::Rect calcStringDimensions(const char *str, int x, int y, bool center);
+
 	static inline bool is2ByteCharacter(Common::Language lang, byte c) {
 		if (lang == Common::JA_JPN)
 			return (c >= 0x80 && c <= 0x9F) || (c >= 0xE0 && c <= 0xFD);
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 83b565c8bd7..5924c65a615 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -26,6 +26,7 @@
 #include "scumm/resource.h"
 #include "scumm/scumm_v0.h"
 #include "scumm/scumm_v7.h"
+#include "scumm/string_v7.h"
 #include "scumm/verbs.h"
 
 namespace Scumm {
@@ -1012,35 +1013,32 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
 		_charset->setCurID(vs->charset_nr);
 
 		// Compute the text rect
-		int textWidth = 0;
-		vs->curRect.bottom = 0;
-		const byte *msg2 = msg;
-		while (*msg2) {
-			const int charWidth = _charset->getCharWidth(*msg2);
-			const int charHeight = _charset->getCharHeight(*msg2);
-			textWidth += charWidth;
-			if (vs->curRect.bottom < charHeight)
-				vs->curRect.bottom = charHeight;
-			msg2++;
-		}
-		vs->curRect.bottom += vs->curRect.top;
-		vs->oldRect = vs->curRect;
+		vs->oldRect = vs->curRect = _textV7->calcStringDimensions((const char*)msg, vs->origLeft, vs->curRect.top, vs->center);
+		
+		const int maxWidth = /*_language == Common::HE_ISR ? vs->curRect.right + 1 :*/ _screenWidth - vs->curRect.left;
+		int finalWidth = maxWidth;
 
-		const int maxWidth = _language == Common::HE_ISR ? vs->curRect.right + 1 : _screenWidth - vs->curRect.left;
-		if (_game.version == 8 && _charset->getStringWidth(0, buf) > maxWidth) {
+		if (_game.version == 8 && _textV7->getStringWidth((const char*)buf) > maxWidth) {
 			byte tmpBuf[384];
-			memcpy(tmpBuf, msg, 384);
+			int len = resStrLen(msg);
+			memcpy(tmpBuf, msg, len);
+			len--;
 
-			int len = resStrLen(tmpBuf) - 1;
 			while (len >= 0) {
 				if (tmpBuf[len] == ' ') {
 					tmpBuf[len] = 0;
-					if (_charset->getStringWidth(0, tmpBuf) <= maxWidth) {
+					if ((finalWidth = _textV7->getStringWidth((const char*)tmpBuf)) <= maxWidth) {
 						break;
 					}
 				}
 				--len;
 			}
+
+			enqueueText(tmpBuf, vs->origLeft, vs->curRect.top, color, vs->charset_nr, vs->center);
+			enqueueText(&msg[len + 1], vs->origLeft, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, vs->center);
+			vs->curRect.right = vs->curRect.left + finalWidth;
+			vs->curRect.bottom += _verbLineSpacing;
+			/*
 			int16 leftPos = vs->curRect.left;
 			if (_language == Common::HE_ISR)
 				vs->curRect.left = vs->origLeft = leftPos = vs->curRect.right - _charset->getStringWidth(0, tmpBuf);
@@ -1052,13 +1050,14 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
 					leftPos = vs->curRect.right - _charset->getStringWidth(0, &msg[len + 1]);
 				enqueueText(&msg[len + 1], leftPos, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, vs->center);
 				vs->curRect.bottom += _verbLineSpacing;
-			}
-		} else {
+			}*/
+		} else {/*
 			if (_language == Common::HE_ISR)
 				vs->curRect.left = vs->origLeft = vs->curRect.right - textWidth;
 			else
 				vs->curRect.right = vs->curRect.left + textWidth;
-			enqueueText(msg, vs->curRect.left, vs->curRect.top, color, vs->charset_nr, vs->center);
+			enqueueText(msg, vs->curRect.left, vs->curRect.top, color, vs->charset_nr, vs->center);*/
+			enqueueText(msg, vs->origLeft, vs->curRect.top, color, vs->charset_nr, vs->center);
 		}
 		_charset->setCurID(oldID);
 	}


Commit: 997e2c4b05a1ede954a492e3683b2d2b2a454487
    https://github.com/scummvm/scummvm/commit/997e2c4b05a1ede954a492e3683b2d2b2a454487
Author: athrxx (athrxx at scummvm.org)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: (SCUMM7/8) - cleanup

- remove outdated code
- move things from V6 to V7

Changed paths:
    engines/scumm/scumm.cpp
    engines/scumm/scumm_v6.h
    engines/scumm/scumm_v7.h
    engines/scumm/string.cpp
    engines/scumm/string_v7.cpp


diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 83c7810d341..c25c611aadc 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -839,10 +839,6 @@ ScummEngine_v6::ScummEngine_v6(OSystem *syst, const DetectorResult &dr)
 	for (uint i = 0; i < ARRAYSIZE(_blastObjectQueue); i++) {
 		_blastObjectQueue[i].clear();
 	}
-	_blastTextQueuePos = 0;
-	for (uint i = 0; i < ARRAYSIZE(_blastTextQueue); i++) {
-		_blastTextQueue[i].clear();
-	}
 
 	memset(_akosQueue, 0, sizeof(_akosQueue));
 	_akosQueuePos = 0;
@@ -1062,6 +1058,10 @@ void ScummEngine_vCUPhe::parseEvents() {
 #ifdef ENABLE_SCUMM_7_8
 ScummEngine_v7::ScummEngine_v7(OSystem *syst, const DetectorResult &dr)
 	: ScummEngine_v6(syst, dr) {
+	_blastTextQueuePos = 0;
+	for (uint i = 0; i < ARRAYSIZE(_blastTextQueue); i++) {
+		_blastTextQueue[i].clear();
+	}
 	_verbLineSpacing = 10;
 
 	_smushFrameRate = 0;
diff --git a/engines/scumm/scumm_v6.h b/engines/scumm/scumm_v6.h
index 7aa95167478..d1ccd9435ee 100644
--- a/engines/scumm/scumm_v6.h
+++ b/engines/scumm/scumm_v6.h
@@ -84,22 +84,6 @@ protected:
 	int _blastObjectQueuePos;
 	BlastObject _blastObjectQueue[200];
 
-	struct BlastText : TextObject {
-		Common::Rect rect;
-		bool center;
-		bool wrap;
-
-		void clear() {
-			this->TextObject::clear();
-			rect = Common::Rect();
-			center = false;
-			wrap = false;
-		}
-	};
-
-	int _blastTextQueuePos;
-	BlastText _blastTextQueue[50];
-
 	// Akos Class
 	struct {
 		int16 cmd;
@@ -160,8 +144,8 @@ protected:
 	void useBompCursor(const byte *im, int w, int h);
 	void grabCursor(int x, int y, int w, int h);
 
-	virtual void drawBlastTexts();
-	void removeBlastTexts();
+	virtual void drawBlastTexts() {}
+	virtual void removeBlastTexts() {}
 
 	void enqueueObject(int objectNumber, int objectX, int objectY, int objectWidth,
 	                   int objectHeight, int scaleX, int scaleY, int image, int mode);
diff --git a/engines/scumm/scumm_v7.h b/engines/scumm/scumm_v7.h
index e4fe1bc85f0..3d4e43735e1 100644
--- a/engines/scumm/scumm_v7.h
+++ b/engines/scumm/scumm_v7.h
@@ -142,6 +142,7 @@ protected:
 	void createTextRenderer(GlyphRenderer_v7 *gr) override;
 	void enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrap = false);
 	void drawBlastTexts() override;
+	void removeBlastTexts() override;
 	void actorTalk(const byte *msg) override;
 	void translateText(const byte *text, byte *trans_buff) override;
 	void loadLanguageBundle() override;
@@ -152,6 +153,22 @@ protected:
 	void pauseEngineIntern(bool pause) override;
 
 	void o6_kernelSetFunctions() override;
+
+	struct BlastText : TextObject {
+		Common::Rect rect;
+		bool center;
+		bool wrap;
+
+		void clear() {
+			this->TextObject::clear();
+			rect = Common::Rect();
+			center = false;
+			wrap = false;
+		}
+	};
+
+	int _blastTextQueuePos;
+	BlastText _blastTextQueue[50];
 };
 
 
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 899338bffcc..a0cdc460651 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -128,79 +128,6 @@ void ScummEngine::showMessageDialog(const byte *msg) {
 	VAR(VAR_KEYPRESS) = runDialog(dialog);
 }
 
-
-#pragma mark -
-#pragma mark --- V6 blast text queue code ---
-#pragma mark -
-
-#ifdef ENABLE_SCUMM_7_8
-
-#endif
-
-void ScummEngine_v6::drawBlastTexts() {
-	byte *buf;
-	int c;
-	int i;
-
-	for (i = 0; i < _blastTextQueuePos; i++) {
-		buf = _blastTextQueue[i].text;
-
-		_charset->_top = _blastTextQueue[i].ypos + _screenTop;
-		_charset->_right = _screenWidth - 1;
-		_charset->_center = _blastTextQueue[i].center;
-		_charset->setColor(_blastTextQueue[i].color);
-		_charset->_disableOffsX = _charset->_firstChar = true;
-		_charset->setCurID(_blastTextQueue[i].charset);
-
-		do {
-			_charset->_left = _blastTextQueue[i].xpos;
-
-			// Center text if necessary
-			if (_charset->_center) {
-				_charset->_left -= _charset->getStringWidth(0, buf) / 2;
-				if (_charset->_left < 0)
-					_charset->_left = 0;
-			}
-
-			do {
-				c = *buf++;
-
-				// FIXME: This is a workaround for bugs #1347 and #2440:
-				// In COMI, some text contains ASCII character 11 = 0xB. It's
-				// not quite clear what it is good for; so for now we just ignore
-				// it, which seems to match the original engine (BTW, traditionally,
-				// this is a 'vertical tab').
-				if (c == 0x0B)
-					continue;
-
-				if (c != 0 && c != 0xFF && c != '\n' && c != _newLineCharacter) {
-					if (c & 0x80 && _useCJKMode) {
-						if (_language == Common::JA_JPN && !checkSJISCode(c)) {
-							c = 0x20; //not in S-JIS
-						} else {
-							c += *buf++ * 256;
-						}
-					}
-					_charset->printChar(c, true);
-				}
-			} while (c && c != '\n');
-
-			_charset->_top += _charset->getFontHeight();
-		} while (c);
-
-		_blastTextQueue[i].rect = _charset->_str;
-	}
-}
-
-void ScummEngine_v6::removeBlastTexts() {
-	int i;
-
-	for (i = 0; i < _blastTextQueuePos; i++) {
-		restoreBackground(_blastTextQueue[i].rect);
-	}
-	_blastTextQueuePos = 0;
-}
-
 #pragma mark -
 #pragma mark --- Core message/subtitle code ---
 #pragma mark -
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 9075515d955..793592972fa 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -378,6 +378,10 @@ void ScummEngine_v7::createTextRenderer(GlyphRenderer_v7 *gr) {
 	_textV7 = new TextRenderer_v7(this, gr);
 }
 
+#pragma mark -
+#pragma mark --- V7 blast text queue code ---
+#pragma mark -
+
 void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrap) {
 	assert(_blastTextQueuePos + 1 <= ARRAYSIZE(_blastTextQueue));
 
@@ -443,6 +447,15 @@ void ScummEngine_v7::drawBlastTexts() {
 	}
 }
 
+void ScummEngine_v7::removeBlastTexts() {
+	int i;
+
+	for (i = 0; i < _blastTextQueuePos; i++) {
+		restoreBackground(_blastTextQueue[i].rect);
+	}
+	_blastTextQueuePos = 0;
+}
+
 void ScummEngine_v8::printString(int m, const byte *msg) {
 	if (m == 4) {
 		const StringTab &st = _string[m];


Commit: de4513cdff672b7d908be6b21c530f43a0114b8e
    https://github.com/scummvm/scummvm/commit/de4513cdff672b7d908be6b21c530f43a0114b8e
Author: athrxx (athrxx at scummvm.org)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: (SCUMM7/8) - implement original text formatting flags for more flexibility

Changed paths:
    engines/scumm/charset_v7.h
    engines/scumm/insane/insane.cpp
    engines/scumm/script_v6.cpp
    engines/scumm/scumm_v7.h
    engines/scumm/smush/smush_font.h
    engines/scumm/smush/smush_player.cpp
    engines/scumm/string_v7.cpp
    engines/scumm/string_v7.h
    engines/scumm/verbs.cpp


diff --git a/engines/scumm/charset_v7.h b/engines/scumm/charset_v7.h
index 53fd01235bc..cdadcad634f 100644
--- a/engines/scumm/charset_v7.h
+++ b/engines/scumm/charset_v7.h
@@ -29,6 +29,14 @@
 
 namespace Scumm {
 
+enum TextStyleFlags {
+	kStyleAlignLeft		=	0x00,
+	kStyleAlignCenter	=	0x01,
+	kStyleAlignRight	=	0x02,
+	kStyleWordWrap		=	0x04
+	// There are more flags, but I'll add them only as needed
+};
+
 class GlyphRenderer_v7 {
 public:
 	virtual int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) = 0;
diff --git a/engines/scumm/insane/insane.cpp b/engines/scumm/insane/insane.cpp
index 04ffa710c02..99b6f283fbe 100644
--- a/engines/scumm/insane/insane.cpp
+++ b/engines/scumm/insane/insane.cpp
@@ -1300,12 +1300,13 @@ void Insane::smlayer_showStatusMsg(int32 arg_0, byte *renderBitmap, int32 codecp
 	// bit 2 - word wrap               0x04
 	// bit 3 - switchable              0x08
 	// bit 4 - fill background         0x10
-	if ((flags & 4) || _vm->_language == Common::HE_ISR) {
+	TextStyleFlags flg = (TextStyleFlags)(flags & 7);
+	if ((flg & kStyleWordWrap) || _vm->_language == Common::HE_ISR) {
 		Common::Rect clipRect(0, 0, _player->_width, _player->_height);
-		sf->drawStringWrap(str, renderBitmap, clipRect, pos_x, pos_y, color, flags & 1);
+		sf->drawStringWrap(str, renderBitmap, clipRect, pos_x, pos_y, color, flg);
 	} else {
 		Common::Rect clipRect(10, 0, 310, _player->_height);
-		sf->drawString(str, renderBitmap, clipRect, pos_x, pos_y, color, flags & 1);
+		sf->drawString(str, renderBitmap, clipRect, pos_x, pos_y, color, flg);
 	}
 
 
diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp
index 49fc29e79e4..cb698a186a1 100644
--- a/engines/scumm/script_v6.cpp
+++ b/engines/scumm/script_v6.cpp
@@ -2586,7 +2586,7 @@ void ScummEngine_v7::o6_kernelSetFunctions() {
 		break;
 	case 16:
 	case 17:
-		enqueueText(getStringAddressVar(VAR_STRING2DRAW), args[3], args[4], args[2], args[1], (args[0] == 16));
+		enqueueText(getStringAddressVar(VAR_STRING2DRAW), args[3], args[4], args[2], args[1], (args[0] == 16) ? kStyleAlignCenter : kStyleAlignLeft);
 		break;
 	case 20:
 		_imuseDigital->setRadioChatterSFX(args[1]);
diff --git a/engines/scumm/scumm_v7.h b/engines/scumm/scumm_v7.h
index 3d4e43735e1..c6c2da24496 100644
--- a/engines/scumm/scumm_v7.h
+++ b/engines/scumm/scumm_v7.h
@@ -25,6 +25,7 @@
 #ifdef ENABLE_SCUMM_7_8
 
 #include "scumm/scumm_v6.h"
+#include "scumm/charset_v7.h"
 
 namespace Scumm {
 
@@ -140,7 +141,7 @@ protected:
 	int getObjectIdFromOBIM(const byte *obim) override;
 
 	void createTextRenderer(GlyphRenderer_v7 *gr) override;
-	void enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrap = false);
+	void enqueueText(const byte *text, int x, int y, byte color, byte charset, TextStyleFlags flags);
 	void drawBlastTexts() override;
 	void removeBlastTexts() override;
 	void actorTalk(const byte *msg) override;
@@ -156,14 +157,11 @@ protected:
 
 	struct BlastText : TextObject {
 		Common::Rect rect;
-		bool center;
-		bool wrap;
+		TextStyleFlags flags;
 
 		void clear() {
 			this->TextObject::clear();
 			rect = Common::Rect();
-			center = false;
-			wrap = false;
 		}
 	};
 
diff --git a/engines/scumm/smush/smush_font.h b/engines/scumm/smush/smush_font.h
index edffda46d7e..44510f5db2a 100644
--- a/engines/scumm/smush/smush_font.h
+++ b/engines/scumm/smush/smush_font.h
@@ -38,12 +38,12 @@ public:
 
 	~SmushFont() override {	delete _r;}
 
-	void drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int16 col, bool center) {
-		_r->drawString(str, buffer, clipRect, x, y, _vm->_screenWidth, col, center);
+	void drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int16 col, TextStyleFlags flags) {
+		_r->drawString(str, buffer, clipRect, x, y, _vm->_screenWidth, col, flags);
 	}
 
-	void drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int16 col, bool center) {
-		_r->drawStringWrap(str, buffer, clipRect, x, y, _vm->_screenWidth, col, center);
+	void drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int16 col, TextStyleFlags flags) {
+		_r->drawStringWrap(str, buffer, clipRect, x, y, _vm->_screenWidth, col, flags);
 	}
 
 private:
diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp
index 7370b7692d5..914b1e03742 100644
--- a/engines/scumm/smush/smush_player.cpp
+++ b/engines/scumm/smush/smush_player.cpp
@@ -671,8 +671,8 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 	// bit 6 - vertical fix (COMI)     0x40        (COMI handles this in the printing method, but I haven't seen a case where it is used)
 	// bit 7 - skip ^ codes (COMI)     0x80        (should be irrelevant for Smush, we strip these commands anyway)
 	// bit 8 - no vertical fix (COMI)  0x100       (COMI handles this in the printing method, but I haven't seen a case where it is used)
-
-	if ((flags & 4) || _vm->_language == Common::HE_ISR) {
+	TextStyleFlags flg = (TextStyleFlags)(flags & 7);
+	if ((flg & kStyleWordWrap) || _vm->_language == Common::HE_ISR) {
 		// COMI has to do it all a bit different, of course. SCUMM7 games immediately render the text from here and actually use the clipping data
 		// provided by the text resource. COMI does not render directly, but enqueues a blast string (which is then drawn through the usual main
 		// loop routines). During that process the rect data will get dumped and replaced with the following default values. It's hard to tell
@@ -683,12 +683,12 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 			height = _height - 20;
 		}
 		Common::Rect clipRect(MAX<int>(0, left), MAX<int>(0, top), MIN<int>(left + width, _width), MIN<int>(top + height, _height));
-		sf->drawStringWrap(str, _dst, clipRect, pos_x, pos_y, color, flags & 1);
+		sf->drawStringWrap(str, _dst, clipRect, pos_x, pos_y, color, flg);
 	} else {
 		// Similiar to the wrapped text, COMI will pass on rect coords here, which will later be lost. Unlike with the wrapped text, it will
 		// finally use the full screen dimenstions. SCUMM7 renders directly from here (see comment above), but also with the full screen.
 		Common::Rect clipRect(0, 0, _width, _height);
-		sf->drawString(str, _dst, clipRect, pos_x, pos_y, color, flags & 1);
+		sf->drawString(str, _dst, clipRect, pos_x, pos_y, color, flg);
 	}
 
 	free(string);
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 793592972fa..24f93d17ef9 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -184,8 +184,8 @@ void TextRenderer_v7::drawSubstring(const char *str, uint numBytesMax, byte *buf
 
 #define SCUMM7_MAX_STRINGS		80
 
-void TextRenderer_v7::drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, bool center) {
-	debugC(DEBUG_GENERAL, "TextRenderer_v7::drawString(str: '%s', x: %d, y: %d, col: %d, clipRect: (%d, %d, %d, %d), center: %d)", str, x, y, col, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, center);
+void TextRenderer_v7::drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags) {
+	debugC(DEBUG_GENERAL, "TextRenderer_v7::drawString(str: '%s', x: %d, y: %d, col: %d, clipRect: (%d, %d, %d, %d), flags: 0x%02x)", str, x, y, col, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, flags);
 
 	int totalLen = (int)strlen(str);
 	int lineStart = 0;
@@ -211,21 +211,21 @@ void TextRenderer_v7::drawString(const char *str, byte *buffer, Common::Rect &cl
 		if (y < clipRect.bottom) {
 			int width = getStringWidth(str + lineStart, len);
 			maxWidth = MAX<int>(maxWidth, width);
-			drawSubstring(str + lineStart, len, buffer, clipRect, center ? (x - width / 2) : x, y, pitch, col);
+			drawSubstring(str + lineStart, len, buffer, clipRect, (flags & kStyleAlignCenter) ? (x - width / 2) : x, y, pitch, col);
 			y += height;
 		}
 
 		lineStart = pos + 1;
 	}
 
-	clipRect.left = center ? x - maxWidth / 2: x;
+	clipRect.left = (flags & kStyleAlignCenter) ? x - maxWidth / 2: x;
 	clipRect.right = MIN<int>(clipRect.right, clipRect.left + maxWidth);
 	clipRect.top = y2;
 	clipRect.bottom = y;
 }
 
-void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, bool center) {
-	debugC(DEBUG_GENERAL, "TextRenderer_v7::drawStringWrap(str: '%s', x: %d, y: %d, col: %d, clipRect: (%d, %d, %d, %d), center: %d)", str, x, y, col, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, center);
+void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags) {
+	debugC(DEBUG_GENERAL, "TextRenderer_v7::drawStringWrap(str: '%s', x: %d, y: %d, col: %d, clipRect: (%d, %d, %d, %d), flags: 0x%02x)", str, x, y, col, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, flags);
 	// This implementation is from COMI. Things are done a bit differently than in the older implementations.
 	// In particular, the older version would insert '\0' chars into the string to cut off the sub strings
 	// before calling getStringWidth(), getStringHeight() or drawSubstring() and replace these chars with the
@@ -332,7 +332,7 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 		y = clipRect.top;
 
 	if (_newStyle) {
-		if (center) {
+		if (flags & kStyleAlignCenter) {
 			if (x + (maxWidth >> 1) > clipRect.right)
 				x = clipRect.right - (maxWidth >> 1);
 			if (x - (maxWidth >> 1) < clipRect.left)
@@ -348,7 +348,7 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 	int y2 = y;
 
 	for (int i = 0; i < numSubstrings; i++) {
-		int xpos = center ? x - substrWidths[i] / 2 : x;
+		int xpos = (flags & kStyleAlignCenter) ? x - substrWidths[i] / 2 : x;
 		if (!_newStyle)
 			xpos = CLIP<int>(xpos, clipRect.left, _screenWidth - substrWidths[i]);
 		len = substrByteLength[i] > 0 ? substrByteLength[i] : 0;
@@ -356,7 +356,7 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 		y += getStringHeight(str + substrStart[i], len);
 	}
 
-	clipRect.left = center ? x - maxWidth / 2 : x;
+	clipRect.left = (flags & kStyleAlignCenter) ? x - maxWidth / 2 : x;
 	clipRect.right = MIN<int>(clipRect.right, clipRect.left + maxWidth);
 	clipRect.top = y2;
 	clipRect.bottom = y;
@@ -382,7 +382,7 @@ void ScummEngine_v7::createTextRenderer(GlyphRenderer_v7 *gr) {
 #pragma mark --- V7 blast text queue code ---
 #pragma mark -
 
-void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center, bool wrap) {
+void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byte charset, TextStyleFlags flags) {
 	assert(_blastTextQueuePos + 1 <= ARRAYSIZE(_blastTextQueue));
 
 	if (_useCJKMode) {
@@ -405,8 +405,7 @@ void ScummEngine_v7::enqueueText(const byte *text, int x, int y, byte color, byt
 	bt.ypos = y;
 	bt.color = color;
 	bt.charset = charset;
-	bt.center = center;
-	bt.wrap = wrap;
+	bt.flags = flags;
 }
 
 void ScummEngine_v7::drawBlastTexts() {
@@ -417,7 +416,7 @@ void ScummEngine_v7::drawBlastTexts() {
 
 		_charset->setCurID(_blastTextQueue[i].charset);
 
-		if (bt.wrap) {
+		if (bt.flags & kStyleWordWrap) {
 			bt.rect = _wrappedTextClipRect;
 
 			// This is for the "narrow" paragraph wrapping type that the older interpreters (e. g. FT, DIG English) do.
@@ -425,7 +424,7 @@ void ScummEngine_v7::drawBlastTexts() {
 				bt.xpos = CLIP<int>(bt.xpos, 80, 240);
 				bt.ypos = CLIP<int>(bt.ypos, 1, 160);
 				int maxWidth = _string[0].right - bt.xpos - 1;
-				if (bt.center) {
+				if (bt.flags & kStyleAlignCenter) {
 					if (maxWidth > bt.xpos)
 						maxWidth = bt.xpos;
 					bt.rect.left = MAX<int>(0, bt.xpos - maxWidth);
@@ -433,10 +432,10 @@ void ScummEngine_v7::drawBlastTexts() {
 				}
 			}
 
-			_textV7->drawStringWrap((const char*)bt.text, (byte*)vs->getPixels(0, _screenTop), bt.rect, bt.xpos, bt.ypos, vs->pitch, bt.color, bt.center);
+			_textV7->drawStringWrap((const char*)bt.text, (byte*)vs->getPixels(0, _screenTop), bt.rect, bt.xpos, bt.ypos, vs->pitch, bt.color, bt.flags);
 		} else {
 			bt.rect = _defaultTextClipRect;
-			_textV7->drawString((const char*)bt.text, (byte*)vs->getPixels(0, _screenTop), bt.rect, bt.xpos, bt.ypos, vs->pitch, bt.color, bt.center);
+			_textV7->drawString((const char*)bt.text, (byte*)vs->getPixels(0, _screenTop), bt.rect, bt.xpos, bt.ypos, vs->pitch, bt.color, bt.flags);
 		}
 
 		bt.rect.top += _screenTop;
@@ -459,7 +458,10 @@ void ScummEngine_v7::removeBlastTexts() {
 void ScummEngine_v8::printString(int m, const byte *msg) {
 	if (m == 4) {
 		const StringTab &st = _string[m];
-		enqueueText(msg, st.xpos, st.ypos, st.color, st.charset, st.center, st.wrapping);
+		int flags = st.wrapping ? kStyleWordWrap : 0;
+		if (st.center)
+			flags |= kStyleAlignCenter;
+		enqueueText(msg, st.xpos, st.ypos, st.color, st.charset, (TextStyleFlags)flags);
 	} else {
 		ScummEngine::printString(m, msg);
 	}
@@ -475,7 +477,10 @@ void ScummEngine_v7::processSubtitleQueue() {
 		if (!st->actorSpeechMsg && (!ConfMan.getBool("subtitles") || VAR(VAR_VOICE_MODE) == 0))
 			// no subtitles and there's a speech variant of the message, don't display the text
 			continue;
-		enqueueText(st->text, st->xpos, st->ypos, st->color, st->charset, st->center, st->wrap);
+		int flags = st->wrap ? kStyleWordWrap : 0;
+		if (st->center)
+			flags |= kStyleAlignCenter;
+		enqueueText(st->text, st->xpos, st->ypos, st->color, st->charset, (TextStyleFlags)flags);
 	}
 }
 
diff --git a/engines/scumm/string_v7.h b/engines/scumm/string_v7.h
index a692657751a..b1f76ea8ad2 100644
--- a/engines/scumm/string_v7.h
+++ b/engines/scumm/string_v7.h
@@ -37,8 +37,8 @@ public:
 	TextRenderer_v7(ScummEngine *vm, GlyphRenderer_v7 *gr);
 	~TextRenderer_v7() {}
 
-	void drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, bool center);
-	void drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, bool center);
+	void drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags);
+	void drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags);
 
 	int getStringWidth(const char *str) { return getStringWidth(str, 100000); }
 	int getStringHeight(const char *str) { return getStringHeight(str, 100000); }
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 5924c65a615..06d70b0f25c 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -994,6 +994,8 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
 		else if (mode && vs->hicolor)
 			color = vs->hicolor;
 
+		TextStyleFlags flags = vs->center ? kStyleAlignCenter : kStyleAlignLeft;
+
 		const byte *msg = getResourceAddress(rtVerb, verb);
 		if (!msg)
 			return;
@@ -1034,8 +1036,8 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
 				--len;
 			}
 
-			enqueueText(tmpBuf, vs->origLeft, vs->curRect.top, color, vs->charset_nr, vs->center);
-			enqueueText(&msg[len + 1], vs->origLeft, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, vs->center);
+			enqueueText(tmpBuf, vs->origLeft, vs->curRect.top, color, vs->charset_nr, flags);
+			enqueueText(&msg[len + 1], vs->origLeft, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, flags);
 			vs->curRect.right = vs->curRect.left + finalWidth;
 			vs->curRect.bottom += _verbLineSpacing;
 			/*
@@ -1057,7 +1059,7 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
 			else
 				vs->curRect.right = vs->curRect.left + textWidth;
 			enqueueText(msg, vs->curRect.left, vs->curRect.top, color, vs->charset_nr, vs->center);*/
-			enqueueText(msg, vs->origLeft, vs->curRect.top, color, vs->charset_nr, vs->center);
+			enqueueText(msg, vs->origLeft, vs->curRect.top, color, vs->charset_nr, flags);
 		}
 		_charset->setCurID(oldID);
 	}


Commit: d9ca12886357719dbc08210893846f571325fbda
    https://github.com/scummvm/scummvm/commit/d9ca12886357719dbc08210893846f571325fbda
Author: athrxx (athrxx at scummvm.org)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: (SCUMM7/8) - implement right alignment flag for text

The original has it, although it isn't used. It is not meant for right to left languages, it just right-aligns left-to-right texts. Since I'll only use it for Hebrew I have to upgrade it a bit...

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/charset.h
    engines/scumm/charset_v7.h
    engines/scumm/nut_renderer.cpp
    engines/scumm/nut_renderer.h
    engines/scumm/smush/smush_font.h
    engines/scumm/string_v7.cpp
    engines/scumm/string_v7.h
    engines/scumm/verbs.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index aeb2b2eca2f..aa0bb4f6b5d 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1965,6 +1965,12 @@ void CharsetRendererMac::setColor(byte color) {
 }
 
 #ifdef ENABLE_SCUMM_7_8
+CharsetRendererV7::CharsetRendererV7(ScummEngine *vm) : CharsetRendererClassic(vm),
+	_spacing(vm->_useCJKMode && vm->_language != Common::JA_JPN ? 1 : 0),
+	_direction(vm->_language == Common::HE_ISR ? -1 : 1),
+	_newStyle(vm->_useCJKMode) {
+}
+
 int CharsetRendererV7::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) {
 	const byte *src = _vm->get2byteCharPtr(chr);
 	buffer += (y * pitch + x);
@@ -1987,14 +1993,16 @@ int CharsetRendererV7::draw2byte(byte *buffer, Common::Rect &clipRect, int x, in
 	return _origWidth + _spacing;
 }
 
-int CharsetRendererV7::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) {
+int CharsetRendererV7::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags, byte chr) {
 	if (!prepareDraw(chr))
 		return 0;
 	_width = getCharWidth(chr);
+	if (_direction < 0)
+		x -= _width;
 	_vm->_charsetColorMap[1] = col;
 	VirtScreen &vs = _vm->_virtscr[kMainVirtScreen];
 	drawBitsN(vs, buffer + (y + _offsY) * vs.pitch + x, _charPtr, *_fontPtr, y, _origWidth, _origHeight);
-	return _width;
+	return _direction * _width;
 }
 
 
@@ -2066,9 +2074,9 @@ int CharsetRendererNut::draw2byte(byte *buffer, Common::Rect &clipRect, int x, i
 	return _current->draw2byte(buffer, clipRect, x, y, pitch, col, chr);
 }
 
-int CharsetRendererNut::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) {
+int CharsetRendererNut::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags, byte chr) {
 	assert(_current);
-	return _current->drawChar(buffer, clipRect, x, y, pitch, col, chr);
+	return _current->drawChar(buffer, clipRect, x, y, pitch, col, flags, chr);
 }
 #endif
 
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index 28090466826..f2a6d1ee4e9 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -310,13 +310,13 @@ public:
 #ifdef ENABLE_SCUMM_7_8
 class CharsetRendererV7 : public CharsetRendererClassic, public GlyphRenderer_v7 {
 public:
-	CharsetRendererV7(ScummEngine *vm) : CharsetRendererClassic(vm), _spacing(vm->_useCJKMode && vm->_language != Common::JA_JPN ? 1 : 0), _newStyle(vm->_useCJKMode) {}
+	CharsetRendererV7(ScummEngine *vm);
 	~CharsetRendererV7() override {};
 
 	void printChar(int chr, bool ignoreCharsetMask) override { error("CharsetRendererV7::printChar(): Unexpected legacy function call"); }
 
 	int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) override;
-	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) override;
+	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags, byte chr) override;
 	int getCharWidth(uint16 chr) const override;
 	int getCharHeight(uint16 chr) const override { return ((chr & 0x80) && _vm->_useCJKMode) ? _vm->_2byteHeight : _fontHeight; }
 	int getFontHeight() const override { return _fontHeight; }
@@ -325,6 +325,7 @@ public:
 private:
 	const int _spacing;
 	const bool _newStyle;
+	const int _direction;
 };
 
 class CharsetRendererNut : public CharsetRenderer, public GlyphRenderer_v7 {
@@ -339,7 +340,7 @@ public:
 	bool newStyleWrapping() const override { return true; }
 
 	int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) override;
-	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) override;
+	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags, byte chr) override;
 
 	int getFontHeight() const override;
 	int getCharWidth(uint16 chr) const override;
diff --git a/engines/scumm/charset_v7.h b/engines/scumm/charset_v7.h
index cdadcad634f..24161e76276 100644
--- a/engines/scumm/charset_v7.h
+++ b/engines/scumm/charset_v7.h
@@ -40,7 +40,7 @@ enum TextStyleFlags {
 class GlyphRenderer_v7 {
 public:
 	virtual int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) = 0;
-	virtual int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) = 0;
+	virtual int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags, byte chr) = 0;
 	virtual int getCharWidth(uint16 chr) const = 0;
 	virtual int getCharHeight(uint16 chr) const = 0;
 	virtual int getFontHeight() const = 0;
diff --git a/engines/scumm/nut_renderer.cpp b/engines/scumm/nut_renderer.cpp
index 373b31e2fd9..e4c101e0896 100644
--- a/engines/scumm/nut_renderer.cpp
+++ b/engines/scumm/nut_renderer.cpp
@@ -39,7 +39,8 @@ NutRenderer::NutRenderer(ScummEngine *vm, const char *filename) :
 	_2byteShadowYOffsetTable(0),
 	_2byteMainColor(0),
 	_spacing(vm->_useCJKMode && vm->_language != Common::JA_JPN ? 1 : 0),
-	_2byteSteps(vm->_game.version == 8 ? 4 : 2) {
+	_2byteSteps(vm->_game.version == 8 ? 4 : 2),
+	_direction(vm->_language == Common::HE_ISR ? -1 : 1) {
 		static const int8 cjkShadowOffsetsX[4] = { -1, 0, 1, 0 };
 		static const int8 cjkShadowOffsetsY[4] = { 0, 1, 0, 0 };
 		_2byteShadowXOffsetTable = &cjkShadowOffsetsX[ARRAYSIZE(cjkShadowOffsetsX) - _2byteSteps];
@@ -367,7 +368,10 @@ void NutRenderer::drawFrame(byte *dst, int c, int x, int y) {
 	}
 }
 
-int NutRenderer::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr, bool hardcodedColors, bool smushColorMode) {
+int NutRenderer::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags, byte chr, bool hardcodedColors, bool smushColorMode) {
+	if (_direction < 0)
+		x -= _chars[chr].width;
+
 	int width = MIN((int)_chars[chr].width, clipRect.right - x);
 	int height = MIN((int)_chars[chr].height, clipRect.bottom - y);
 	int minX = x < clipRect.left ? clipRect.left - x : 0;
@@ -440,7 +444,7 @@ int NutRenderer::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, in
 			}
 		}		
 	}
-	return width;
+	return _direction * width;
 }
 
 int NutRenderer::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) {
diff --git a/engines/scumm/nut_renderer.h b/engines/scumm/nut_renderer.h
index e9958516b47..bf52f80e993 100644
--- a/engines/scumm/nut_renderer.h
+++ b/engines/scumm/nut_renderer.h
@@ -24,6 +24,7 @@
 
 #include "common/file.h"
 #include "graphics/surface.h"
+#include "scumm/charset_v7.h"
 
 namespace Scumm {
 
@@ -47,6 +48,7 @@ protected:
 	byte *_paletteMap;
 	byte _bpp;
 	byte _palette[16];
+	const bool _direction;
 
 	const int8 *_2byteShadowXOffsetTable;
 	const int8 *_2byteShadowYOffsetTable;
@@ -74,7 +76,7 @@ public:
 
 	void drawFrame(byte *dst, int c, int x, int y);
 	int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr);
-	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr, bool hardcodedColors = false, bool smushColorMode = false);
+	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags, byte chr, bool hardcodedColors = false, bool smushColorMode = false);
 
 	int getCharWidth(byte c) const;
 	int getCharHeight(byte c) const;
diff --git a/engines/scumm/smush/smush_font.h b/engines/scumm/smush/smush_font.h
index 44510f5db2a..3c103bc499d 100644
--- a/engines/scumm/smush/smush_font.h
+++ b/engines/scumm/smush/smush_font.h
@@ -51,8 +51,8 @@ private:
 		return NutRenderer::draw2byte(buffer, clipRect, x, y, pitch, _vm->_game.id == GID_CMI ? 255 : (_vm->_game.id == GID_DIG && col == -1 ? 1 : col), chr);
 	}
 
-	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, byte chr) override {
-		return NutRenderer::drawChar(buffer, clipRect, x, y, pitch, col, chr, _hardcodedFontColors, true);
+	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags, byte chr) override {
+		return NutRenderer::drawChar(buffer, clipRect, x, y, pitch, col, flags, chr, _hardcodedFontColors, true);
 	}
 
 	int getCharWidth(uint16 chr) const override { return NutRenderer::getCharWidth(chr & 0xFF); }
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 24f93d17ef9..6029dfbefd6 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -38,6 +38,7 @@ TextRenderer_v7::TextRenderer_v7(ScummEngine *vm, GlyphRenderer_v7 *gr)	:
 	_2byteCharWidth(vm->_2byteWidth),
 	_screenWidth(vm->_screenWidth),
 	_useCJKMode(vm->_useCJKMode),
+	_direction(vm->_language == Common::HE_ISR ? -1 : 1),
 	_spacing(vm->_language != Common::JA_JPN ? 1 : 0),
 	_lineBreakMarker(vm->_newLineCharacter),
 	_newStyle (gr->newStyleWrapping()),
@@ -145,10 +146,10 @@ int TextRenderer_v7::getStringHeight(const char *str, uint numBytesMax) {
 	return totalHeight + (lineHeight ? lineHeight : _gr->getFontHeight()) + 1;
 }
 
-void TextRenderer_v7::drawSubstring(const char *str, uint numBytesMax, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 &col) {
+void TextRenderer_v7::drawSubstring(const char *str, uint numBytesMax, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 &col, TextStyleFlags flags) {
 	if (_lang == Common::HE_ISR) {
 		for (int i = numBytesMax; i > 0; i--) {
-			x += _gr->drawChar(buffer, clipRect, x, y, pitch, col, str[i - 1]);
+			x += _gr->drawChar(buffer, clipRect, x, y, pitch, col, flags, str[i - 1]);
 		}
 	} else {
 		for (int i = 0; str[i] != 0 && numBytesMax; ++i) {
@@ -175,7 +176,7 @@ void TextRenderer_v7::drawSubstring(const char *str, uint numBytesMax, byte *buf
 				++i;
 				--numBytesMax;
 			} else if (str[i] != '\n' && str[i] != _lineBreakMarker) {
-				x += _gr->drawChar(buffer, clipRect, x, y, pitch, col, str[i]);
+				x += _gr->drawChar(buffer, clipRect, x, y, pitch, col, flags, str[i]);
 			}
 			--numBytesMax;
 		}
@@ -211,14 +212,24 @@ void TextRenderer_v7::drawString(const char *str, byte *buffer, Common::Rect &cl
 		if (y < clipRect.bottom) {
 			int width = getStringWidth(str + lineStart, len);
 			maxWidth = MAX<int>(maxWidth, width);
-			drawSubstring(str + lineStart, len, buffer, clipRect, (flags & kStyleAlignCenter) ? (x - width / 2) : x, y, pitch, col);
+
+			int xpos = x;
+			if (flags & kStyleAlignCenter)
+				xpos = x - _direction * width / 2;
+			else if (((flags & kStyleAlignRight) && _direction == 1) || ((flags & kStyleAlignLeft) && _direction == -1))
+				xpos = x - _direction * width;
+
+			if (!_newStyle)
+				xpos = CLIP<int>(xpos, clipRect.left, _screenWidth - width);
+
+			drawSubstring(str + lineStart, len, buffer, clipRect, _direction * width, y, pitch, col, flags);
 			y += height;
 		}
 
 		lineStart = pos + 1;
 	}
 
-	clipRect.left = (flags & kStyleAlignCenter) ? x - maxWidth / 2: x;
+	clipRect.left = (flags & kStyleAlignCenter) ? x - maxWidth / 2: ((flags & kStyleAlignRight) ? x - maxWidth : x);
 	clipRect.right = MIN<int>(clipRect.right, clipRect.left + maxWidth);
 	clipRect.top = y2;
 	clipRect.bottom = y;
@@ -337,6 +348,11 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 				x = clipRect.right - (maxWidth >> 1);
 			if (x - (maxWidth >> 1) < clipRect.left)
 				x = clipRect.left + (maxWidth >> 1);
+		} else if (flags & kStyleAlignRight) {
+			if (x < clipRect.right)
+				x = clipRect.right;
+			if (x < clipRect.left + maxWidth);
+				x = clipRect.left + maxWidth;
 		} else {
 			if (x > clipRect.right - maxWidth)
 				x = clipRect.right - maxWidth;
@@ -348,29 +364,37 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 	int y2 = y;
 
 	for (int i = 0; i < numSubstrings; i++) {
-		int xpos = (flags & kStyleAlignCenter) ? x - substrWidths[i] / 2 : x;
+		int xpos = x;
+		if (flags & kStyleAlignCenter)
+			xpos = x - _direction * substrWidths[i] / 2;
+		else if (((flags & kStyleAlignRight) && _direction == 1) || ((flags & kStyleAlignLeft) && _direction == -1))
+			xpos = x - _direction * substrWidths[i];
+
 		if (!_newStyle)
 			xpos = CLIP<int>(xpos, clipRect.left, _screenWidth - substrWidths[i]);
+
 		len = substrByteLength[i] > 0 ? substrByteLength[i] : 0;
-		drawSubstring(str + substrStart[i], len, buffer, clipRect, xpos, y, pitch, col);
+		drawSubstring(str + substrStart[i], len, buffer, clipRect, xpos, y, pitch, col, flags);
 		y += getStringHeight(str + substrStart[i], len);
 	}
 
-	clipRect.left = (flags & kStyleAlignCenter) ? x - maxWidth / 2 : x;
+	clipRect.left = (flags & kStyleAlignCenter) ? x - maxWidth / 2 : ((flags & kStyleAlignRight) ? x - maxWidth : x);
 	clipRect.right = MIN<int>(clipRect.right, clipRect.left + maxWidth);
 	clipRect.top = y2;
 	clipRect.bottom = y;
 }
 
-Common::Rect TextRenderer_v7::calcStringDimensions(const char *str, int x, int y, bool center) {
-	int wdth = getStringWidth(str);
+Common::Rect TextRenderer_v7::calcStringDimensions(const char *str, int x, int y, TextStyleFlags flags) {
+	int width = getStringWidth(str);
 	if (_gameId == GID_CMI && _useCJKMode)
 		y += 2;
 	
-	if (center)
-		x -= (wdth >> 1);
+	if (flags & kStyleAlignCenter)
+		x -= width / 2;
+	else if (flags & kStyleAlignRight)
+		x -= width;
 
-	return Common::Rect(x, y, x + wdth, y + getStringHeight(str));
+	return Common::Rect(x, y, x + width, y + getStringHeight(str));
 }
 
 void ScummEngine_v7::createTextRenderer(GlyphRenderer_v7 *gr) {
diff --git a/engines/scumm/string_v7.h b/engines/scumm/string_v7.h
index b1f76ea8ad2..9f1af4e8e74 100644
--- a/engines/scumm/string_v7.h
+++ b/engines/scumm/string_v7.h
@@ -43,7 +43,7 @@ public:
 	int getStringWidth(const char *str) { return getStringWidth(str, 100000); }
 	int getStringHeight(const char *str) { return getStringHeight(str, 100000); }
 
-	Common::Rect calcStringDimensions(const char *str, int x, int y, bool center);
+	Common::Rect calcStringDimensions(const char *str, int x, int y, TextStyleFlags flags);
 
 	static inline bool is2ByteCharacter(Common::Language lang, byte c) {
 		if (lang == Common::JA_JPN)
@@ -58,11 +58,12 @@ public:
 private:
 	int getStringWidth(const char *str, uint numBytesMax);
 	int getStringHeight(const char *str, uint numBytesMax);
-	void drawSubstring(const char *str, uint numBytesMax, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 &col);
+	void drawSubstring(const char *str, uint numBytesMax, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 &col, TextStyleFlags flags);
 
 	const Common::Language _lang;
 	const byte _gameId;
 	const bool _useCJKMode;
+	const bool _direction;
 	const int _spacing;
 	const byte _2byteCharWidth;
 	const byte _lineBreakMarker;
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 06d70b0f25c..6a49a7050cb 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -1015,7 +1015,7 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
 		_charset->setCurID(vs->charset_nr);
 
 		// Compute the text rect
-		vs->oldRect = vs->curRect = _textV7->calcStringDimensions((const char*)msg, vs->origLeft, vs->curRect.top, vs->center);
+		vs->oldRect = vs->curRect = _textV7->calcStringDimensions((const char*)msg, vs->origLeft, vs->curRect.top, vs->center ? kStyleAlignCenter : kStyleAlignLeft);
 		
 		const int maxWidth = /*_language == Common::HE_ISR ? vs->curRect.right + 1 :*/ _screenWidth - vs->curRect.left;
 		int finalWidth = maxWidth;


Commit: acb665c10970d53db5e6cdf5ccf5f2261d679813
    https://github.com/scummvm/scummvm/commit/acb665c10970d53db5e6cdf5ccf5f2261d679813
Author: athrxx (athrxx at scummvm.org)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: (SCUMM7/8) - cleanup (text) verb drawing and Hebrew right-to-left drawing

(SCUMM7/8 only)

Changed paths:
    engines/scumm/insane/insane.cpp
    engines/scumm/nut_renderer.h
    engines/scumm/saveload.cpp
    engines/scumm/script_v8.cpp
    engines/scumm/smush/smush_player.cpp
    engines/scumm/string_v7.cpp
    engines/scumm/string_v7.h
    engines/scumm/verbs.cpp


diff --git a/engines/scumm/insane/insane.cpp b/engines/scumm/insane/insane.cpp
index 99b6f283fbe..c21a360d591 100644
--- a/engines/scumm/insane/insane.cpp
+++ b/engines/scumm/insane/insane.cpp
@@ -1294,14 +1294,18 @@ void Insane::smlayer_showStatusMsg(int32 arg_0, byte *renderBitmap, int32 codecp
 
 	assert(sf != NULL);
 
+	if (_vm->_language == Common::HE_ISR && !(flags & kStyleAlignCenter)) {
+		flags |= kStyleAlignRight;
+		pos_x = _player->_width - 1 - pos_x;
+	}
+	TextStyleFlags flg = (TextStyleFlags)(flags & 7);
 	// flags:
 	// bit 0 - center                  0x01
 	// bit 1 - not used (align right)  0x02
 	// bit 2 - word wrap               0x04
 	// bit 3 - switchable              0x08
 	// bit 4 - fill background         0x10
-	TextStyleFlags flg = (TextStyleFlags)(flags & 7);
-	if ((flg & kStyleWordWrap) || _vm->_language == Common::HE_ISR) {
+	if (flg & kStyleWordWrap) {
 		Common::Rect clipRect(0, 0, _player->_width, _player->_height);
 		sf->drawStringWrap(str, renderBitmap, clipRect, pos_x, pos_y, color, flg);
 	} else {
diff --git a/engines/scumm/nut_renderer.h b/engines/scumm/nut_renderer.h
index bf52f80e993..95aaa415094 100644
--- a/engines/scumm/nut_renderer.h
+++ b/engines/scumm/nut_renderer.h
@@ -48,7 +48,7 @@ protected:
 	byte *_paletteMap;
 	byte _bpp;
 	byte _palette[16];
-	const bool _direction;
+	const int _direction;
 
 	const int8 *_2byteShadowXOffsetTable;
 	const int8 *_2byteShadowYOffsetTable;
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 690fc4f80e8..4fd074dfeef 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -821,8 +821,8 @@ static void syncWithSerializer(Common::Serializer &s, ObjectData &od) {
 	s.syncAsByte(od.flags, VER(46));
 }
 
-static void syncWithSerializer(Common::Serializer &s, VerbSlot &vs, bool isISR) {
-	s.syncAsSint16LE(!isISR ? vs.curRect.left : vs.origLeft, VER(8));
+static void syncWithSerializer(Common::Serializer &s, VerbSlot &vs, bool isV7orISR) {
+	s.syncAsSint16LE(!isV7orISR ? vs.curRect.left : vs.origLeft, VER(8));
 	s.syncAsSint16LE(vs.curRect.top, VER(8));
 	s.syncAsSint16LE(vs.curRect.right, VER(8));
 	s.syncAsSint16LE(vs.curRect.bottom, VER(8));
@@ -844,15 +844,15 @@ static void syncWithSerializer(Common::Serializer &s, VerbSlot &vs, bool isISR)
 	s.syncAsByte(vs.center, VER(8));
 	s.syncAsByte(vs.prep, VER(8));
 	s.syncAsUint16LE(vs.imgindex, VER(8));
-	if (isISR && s.isLoading() && s.getVersion() >= 8)
+	if (isV7orISR && s.isLoading() && s.getVersion() >= 8)
 		vs.curRect.left = vs.origLeft;
 }
 
-static void syncWithSerializerNonISR(Common::Serializer &s, VerbSlot &vs) {
+static void syncWithSerializerDef(Common::Serializer &s, VerbSlot &vs) {
 	syncWithSerializer(s, vs, false);
 }
 
-static void syncWithSerializerISR(Common::Serializer &s, VerbSlot &vs) {
+static void syncWithSerializerV7orISR(Common::Serializer &s, VerbSlot &vs) {
 	syncWithSerializer(s, vs, true);
 }
 
@@ -1239,7 +1239,7 @@ void ScummEngine::saveLoadWithSerializer(Common::Serializer &s) {
 	//
 	// Save/load misc stuff
 	//
-	s.syncArray(_verbs, _numVerbs, _language != Common::HE_ISR ? syncWithSerializerNonISR : syncWithSerializerISR);
+	s.syncArray(_verbs, _numVerbs, (_game.version < 7 && _language != Common::HE_ISR) ? syncWithSerializerDef : syncWithSerializerV7orISR);
 	s.syncArray(vm.nest, 16, syncWithSerializer);
 	s.syncArray(_sentence, 6, syncWithSerializer);
 	s.syncArray(_string, 6, syncWithSerializer);
diff --git a/engines/scumm/script_v8.cpp b/engines/scumm/script_v8.cpp
index fd6df11d658..2325ba54d64 100644
--- a/engines/scumm/script_v8.cpp
+++ b/engines/scumm/script_v8.cpp
@@ -977,10 +977,7 @@ void ScummEngine_v8::o8_verbOps() {
 		break;
 	case 0x9A:		// SO_VERB_AT Set verb (X,Y) placement
 		vs->curRect.top = pop();
-		if (_language == Common::HE_ISR)
-			vs->curRect.right = _screenWidth - 1 - pop();
-		else
-			vs->origLeft = pop();
+		vs->origLeft = pop();
 		break;
 	case 0x9B:		// SO_VERB_ON Turn verb on
 		vs->curmode = 1;
diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp
index 914b1e03742..06fa7653e40 100644
--- a/engines/scumm/smush/smush_player.cpp
+++ b/engines/scumm/smush/smush_player.cpp
@@ -661,6 +661,12 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 	// changes on the fly will be ignored for Smush texts, since our code design does
 	// not permit it and the feature isn't used anyway).
 
+	if (_vm->_language == Common::HE_ISR && !(flags & kStyleAlignCenter)) {
+		flags |= kStyleAlignRight;
+		pos_x = _width - 1 - pos_x;
+	}
+
+	TextStyleFlags flg = (TextStyleFlags)(flags & 7);
 	// flags:
 	// bit 0 - center                  0x01
 	// bit 1 - not used (align right)  0x02
@@ -671,8 +677,8 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 	// bit 6 - vertical fix (COMI)     0x40        (COMI handles this in the printing method, but I haven't seen a case where it is used)
 	// bit 7 - skip ^ codes (COMI)     0x80        (should be irrelevant for Smush, we strip these commands anyway)
 	// bit 8 - no vertical fix (COMI)  0x100       (COMI handles this in the printing method, but I haven't seen a case where it is used)
-	TextStyleFlags flg = (TextStyleFlags)(flags & 7);
-	if ((flg & kStyleWordWrap) || _vm->_language == Common::HE_ISR) {
+
+	if (flg & kStyleWordWrap) {
 		// COMI has to do it all a bit different, of course. SCUMM7 games immediately render the text from here and actually use the clipping data
 		// provided by the text resource. COMI does not render directly, but enqueues a blast string (which is then drawn through the usual main
 		// loop routines). During that process the rect data will get dumped and replaced with the following default values. It's hard to tell
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 6029dfbefd6..a121c481ecf 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -39,6 +39,7 @@ TextRenderer_v7::TextRenderer_v7(ScummEngine *vm, GlyphRenderer_v7 *gr)	:
 	_screenWidth(vm->_screenWidth),
 	_useCJKMode(vm->_useCJKMode),
 	_direction(vm->_language == Common::HE_ISR ? -1 : 1),
+	_rtlCenteredOffset(vm->_language == Common::HE_ISR ? 1 : 0),
 	_spacing(vm->_language != Common::JA_JPN ? 1 : 0),
 	_lineBreakMarker(vm->_newLineCharacter),
 	_newStyle (gr->newStyleWrapping()),
@@ -147,39 +148,33 @@ int TextRenderer_v7::getStringHeight(const char *str, uint numBytesMax) {
 }
 
 void TextRenderer_v7::drawSubstring(const char *str, uint numBytesMax, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 &col, TextStyleFlags flags) {
-	if (_lang == Common::HE_ISR) {
-		for (int i = numBytesMax; i > 0; i--) {
-			x += _gr->drawChar(buffer, clipRect, x, y, pitch, col, flags, str[i - 1]);
-		}
-	} else {
-		for (int i = 0; str[i] != 0 && numBytesMax; ++i) {
-			if (_newStyle && str[i] == '^') {
-				if (str[i + 1] == 'f') {
-					_gr->setFont(str[i + 3] - '0');
-					i += 3;
-					numBytesMax -= 4;
-					continue;
-				} else if (str[i + 1] == 'c') {
-					col = str[i + 4] - '0' + 10 *(str[i + 3] - '0');
-					i += 4;
-					numBytesMax -= 5;
-					continue;
-				} else if (str[i + 1] == 'l') {
-					i++;
-					numBytesMax -= 2;
-					continue;
-				}
+	for (int i = 0; str[i] != 0 && numBytesMax; ++i) {
+		if (_newStyle && str[i] == '^') {
+			if (str[i + 1] == 'f') {
+				_gr->setFont(str[i + 3] - '0');
+				i += 3;
+				numBytesMax -= 4;
+				continue;
+			} else if (str[i + 1] == 'c') {
+				col = str[i + 4] - '0' + 10 *(str[i + 3] - '0');
+				i += 4;
+				numBytesMax -= 5;
+				continue;
+			} else if (str[i + 1] == 'l') {
+				i++;
+				numBytesMax -= 2;
+				continue;
 			}
+		}
 
-			if (is2ByteCharacter(_lang, str[i])) {
-				x += _gr->draw2byte(buffer, clipRect, x, y, pitch, col, (byte)str[i] + 256 * (byte)str[i + 1]);
-				++i;
-				--numBytesMax;
-			} else if (str[i] != '\n' && str[i] != _lineBreakMarker) {
-				x += _gr->drawChar(buffer, clipRect, x, y, pitch, col, flags, str[i]);
-			}
+		if (is2ByteCharacter(_lang, str[i])) {
+			x += _gr->draw2byte(buffer, clipRect, x, y, pitch, col, (byte)str[i] + 256 * (byte)str[i + 1]);
+			++i;
 			--numBytesMax;
+		} else if (str[i] != '\n' && str[i] != _lineBreakMarker) {
+			x += _gr->drawChar(buffer, clipRect, x, y, pitch, col, flags, str[i]);
 		}
+		--numBytesMax;
 	}
 }
 
@@ -215,21 +210,25 @@ void TextRenderer_v7::drawString(const char *str, byte *buffer, Common::Rect &cl
 
 			int xpos = x;
 			if (flags & kStyleAlignCenter)
-				xpos = x - _direction * width / 2;
-			else if (((flags & kStyleAlignRight) && _direction == 1) || ((flags & kStyleAlignLeft) && _direction == -1))
+				xpos = x - _direction * width / 2 + (_rtlCenteredOffset & width);
+			else if (((flags & kStyleAlignRight) && _direction == 1) || (!(flags & kStyleAlignRight) && _direction == -1))
+				// The original interpreter apparently does not expect a right-to-left written language when the kStyleAlignRight flag is set.
+				// It just right-aligns a left-to-right string. So we now move xpos to the left like the original interpreter would if it is a
+				// left-to-right string, but leave it on the right in case of a right-to-left string (and vice versa for right-to-left strings
+				// with kStyleAlignLeft flag).
 				xpos = x - _direction * width;
 
 			if (!_newStyle)
-				xpos = CLIP<int>(xpos, clipRect.left, _screenWidth - width);
+				xpos = (_direction == 1) ? CLIP<int>(xpos, clipRect.left, _screenWidth - width) : CLIP<int>(xpos, clipRect.left + width, _screenWidth - 1);
 
-			drawSubstring(str + lineStart, len, buffer, clipRect, _direction * width, y, pitch, col, flags);
+			drawSubstring(str + lineStart, len, buffer, clipRect, xpos, y, pitch, col, flags);
 			y += height;
 		}
 
 		lineStart = pos + 1;
 	}
 
-	clipRect.left = (flags & kStyleAlignCenter) ? x - maxWidth / 2: ((flags & kStyleAlignRight) ? x - maxWidth : x);
+	clipRect.left = (flags & kStyleAlignCenter) ? x - maxWidth / 2 : ((flags & kStyleAlignRight) ? x - maxWidth : x);
 	clipRect.right = MIN<int>(clipRect.right, clipRect.left + maxWidth);
 	clipRect.top = y2;
 	clipRect.bottom = y;
@@ -349,7 +348,7 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 			if (x - (maxWidth >> 1) < clipRect.left)
 				x = clipRect.left + (maxWidth >> 1);
 		} else if (flags & kStyleAlignRight) {
-			if (x < clipRect.right)
+			if (x > clipRect.right)
 				x = clipRect.right;
 			if (x < clipRect.left + maxWidth);
 				x = clipRect.left + maxWidth;
@@ -366,12 +365,16 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 	for (int i = 0; i < numSubstrings; i++) {
 		int xpos = x;
 		if (flags & kStyleAlignCenter)
-			xpos = x - _direction * substrWidths[i] / 2;
-		else if (((flags & kStyleAlignRight) && _direction == 1) || ((flags & kStyleAlignLeft) && _direction == -1))
+			xpos = x - _direction * substrWidths[i] / 2 + (_rtlCenteredOffset & substrWidths[i]);
+		else if (((flags & kStyleAlignRight) && _direction == 1) || (!(flags & kStyleAlignRight) && _direction == -1))
+			// The original interpreter apparently does not expect a right-to-left written language when the kStyleAlignRight flag is set.
+			// It just right-aligns a left-to-right string. So we now move xpos to the left like the original interpreter would if it is a
+			// left-to-right string, but leave it on the right in case of a right-to-left string (and vice versa for right-to-left strings
+			// with kStyleAlignLeft flag).
 			xpos = x - _direction * substrWidths[i];
 
 		if (!_newStyle)
-			xpos = CLIP<int>(xpos, clipRect.left, _screenWidth - substrWidths[i]);
+			xpos = (_direction == 1) ? CLIP<int>(xpos, clipRect.left, _screenWidth - substrWidths[i]) : CLIP<int>(xpos, clipRect.left + substrWidths[i], _screenWidth - 1);
 
 		len = substrByteLength[i] > 0 ? substrByteLength[i] : 0;
 		drawSubstring(str + substrStart[i], len, buffer, clipRect, xpos, y, pitch, col, flags);
@@ -440,6 +443,12 @@ void ScummEngine_v7::drawBlastTexts() {
 
 		_charset->setCurID(_blastTextQueue[i].charset);
 
+		// If a Hebrew String comes up that is still marked as kStyleAlignLeft we fix it here...
+		if (_language == Common::HE_ISR && !(bt.flags & (kStyleAlignCenter | kStyleAlignRight))) {
+			bt.flags = (TextStyleFlags)(bt.flags | kStyleAlignRight);
+			bt.xpos = _screenWidth - 1 - bt.xpos;
+		}
+
 		if (bt.flags & kStyleWordWrap) {
 			bt.rect = _wrappedTextClipRect;
 
diff --git a/engines/scumm/string_v7.h b/engines/scumm/string_v7.h
index 9f1af4e8e74..85d9b3c63a9 100644
--- a/engines/scumm/string_v7.h
+++ b/engines/scumm/string_v7.h
@@ -63,7 +63,8 @@ private:
 	const Common::Language _lang;
 	const byte _gameId;
 	const bool _useCJKMode;
-	const bool _direction;
+	const int _direction;
+	const int _rtlCenteredOffset;
 	const int _spacing;
 	const byte _2byteCharWidth;
 	const byte _lineBreakMarker;
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 6a49a7050cb..57e640a4c04 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -994,7 +994,15 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
 		else if (mode && vs->hicolor)
 			color = vs->hicolor;
 
-		TextStyleFlags flags = vs->center ? kStyleAlignCenter : kStyleAlignLeft;
+		TextStyleFlags flags = kStyleAlignLeft;
+		int xpos = vs->origLeft;
+
+		if (vs->center) {
+			flags = kStyleAlignCenter;
+		} else if (_language == Common::HE_ISR) {
+			flags = kStyleAlignRight;
+			xpos = _screenWidth - 1 - vs->origLeft;
+		}
 
 		const byte *msg = getResourceAddress(rtVerb, verb);
 		if (!msg)
@@ -1015,9 +1023,9 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
 		_charset->setCurID(vs->charset_nr);
 
 		// Compute the text rect
-		vs->oldRect = vs->curRect = _textV7->calcStringDimensions((const char*)msg, vs->origLeft, vs->curRect.top, vs->center ? kStyleAlignCenter : kStyleAlignLeft);
+		vs->oldRect = vs->curRect = _textV7->calcStringDimensions((const char*)msg, xpos, vs->curRect.top, flags);
 		
-		const int maxWidth = /*_language == Common::HE_ISR ? vs->curRect.right + 1 :*/ _screenWidth - vs->curRect.left;
+		const int maxWidth = _screenWidth - vs->curRect.left;
 		int finalWidth = maxWidth;
 
 		if (_game.version == 8 && _textV7->getStringWidth((const char*)buf) > maxWidth) {
@@ -1036,30 +1044,12 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
 				--len;
 			}
 
-			enqueueText(tmpBuf, vs->origLeft, vs->curRect.top, color, vs->charset_nr, flags);
-			enqueueText(&msg[len + 1], vs->origLeft, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, flags);
+			enqueueText(tmpBuf, xpos, vs->curRect.top, color, vs->charset_nr, flags);
+			enqueueText(&msg[len + 1], xpos, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, flags);
 			vs->curRect.right = vs->curRect.left + finalWidth;
-			vs->curRect.bottom += _verbLineSpacing;
-			/*
-			int16 leftPos = vs->curRect.left;
-			if (_language == Common::HE_ISR)
-				vs->curRect.left = vs->origLeft = leftPos = vs->curRect.right - _charset->getStringWidth(0, tmpBuf);
-			else
-				vs->curRect.right = vs->curRect.left + _charset->getStringWidth(0, tmpBuf);
-			enqueueText(tmpBuf, leftPos, vs->curRect.top, color, vs->charset_nr, vs->center);
-			if (len >= 0) {
-				if (_language == Common::HE_ISR)
-					leftPos = vs->curRect.right - _charset->getStringWidth(0, &msg[len + 1]);
-				enqueueText(&msg[len + 1], leftPos, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, vs->center);
-				vs->curRect.bottom += _verbLineSpacing;
-			}*/
-		} else {/*
-			if (_language == Common::HE_ISR)
-				vs->curRect.left = vs->origLeft = vs->curRect.right - textWidth;
-			else
-				vs->curRect.right = vs->curRect.left + textWidth;
-			enqueueText(msg, vs->curRect.left, vs->curRect.top, color, vs->charset_nr, vs->center);*/
-			enqueueText(msg, vs->origLeft, vs->curRect.top, color, vs->charset_nr, flags);
+			vs->curRect.bottom += _verbLineSpacing;			
+		} else {
+			enqueueText(msg, xpos, vs->curRect.top, color, vs->charset_nr, flags);
 		}
 		_charset->setCurID(oldID);
 	}


Commit: 2e249f1d0097461bd16019924e23f263952b7748
    https://github.com/scummvm/scummvm/commit/2e249f1d0097461bd16019924e23f263952b7748
Author: athrxx (athrxx at scummvm.org)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: (SCUMM7/8) - reduce deprecated code

- remove version7/8 clauses from code parts that these versions no longer use
- also fix typo/bug (semicolon after if clause)

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/charset.h
    engines/scumm/string.cpp
    engines/scumm/string_v7.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index aa0bb4f6b5d..100626b82ca 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -462,9 +462,7 @@ int CharsetRenderer::getStringWidth(int arg, const byte *text) {
 	int code = (_vm->_game.heversion >= 80) ? 127 : 64;
 
 	while ((chr = text[pos++]) != 0) {
-		if (_vm->_game.version == 7 && chr == _vm->_newLineCharacter)
-			continue;
-		else if (chr == '\n' || chr == '\r' || chr == _vm->_newLineCharacter)
+		if (chr == '\n' || chr == '\r' || chr == _vm->_newLineCharacter)
 			break;
 
 		if (_vm->_game.heversion >= 72) {
@@ -687,11 +685,7 @@ int CharsetRendererV3::getCharWidth(uint16 chr) const {
 void CharsetRendererPC::enableShadow(bool enable) {
 	_shadowColor = 0;
 	_enableShadow = enable;
-
-	if (_vm->_game.version >= 7 && _vm->_useCJKMode)
-		_shadowType = kHorizontalShadowType;
-	else
-		_shadowType = kNormalShadowType;
+	_shadowType = kNormalShadowType;
 }
 
 void CharsetRendererPC::drawBits1(Graphics::Surface &dest, int x, int y, const byte *src, int drawTop, int width, int height) {
@@ -1174,9 +1168,6 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr,
 bool CharsetRendererClassic::prepareDraw(uint16 chr) {
 	bool is2byte = (chr >= 256 && _vm->_useCJKMode);
 	if (is2byte) {
-		if (_vm->_game.version >= 7)
-			enableShadow(true);
-
 		_charPtr = _vm->get2byteCharPtr(chr);
 		_width = _origWidth = _vm->_2byteWidth;
 		_height = _origHeight = _vm->_2byteHeight;
@@ -1972,6 +1963,7 @@ CharsetRendererV7::CharsetRendererV7(ScummEngine *vm) : CharsetRendererClassic(v
 }
 
 int CharsetRendererV7::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) {
+	// I am aware of not doing anything with the clipRect here, but I currently see no need to upgrade the old rendering with that.
 	const byte *src = _vm->get2byteCharPtr(chr);
 	buffer += (y * pitch + x);
 	_origWidth = _vm->_2byteWidth;
@@ -1994,6 +1986,7 @@ int CharsetRendererV7::draw2byte(byte *buffer, Common::Rect &clipRect, int x, in
 }
 
 int CharsetRendererV7::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags, byte chr) {
+	// I am aware of not doing anything with the clipRect here, but I currently see no need to upgrade the old rendering with that.
 	if (!prepareDraw(chr))
 		return 0;
 	_width = getCharWidth(chr);
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index f2a6d1ee4e9..007c08c698c 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -313,7 +313,7 @@ public:
 	CharsetRendererV7(ScummEngine *vm);
 	~CharsetRendererV7() override {};
 
-	void printChar(int chr, bool ignoreCharsetMask) override { error("CharsetRendererV7::printChar(): Unexpected legacy function call"); }
+	void printChar(int, bool) override { error("CharsetRendererV7::printChar(): Unexpected call to deprecated function"); }
 
 	int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) override;
 	int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags, byte chr) override;
@@ -333,7 +333,7 @@ public:
 	CharsetRendererNut(ScummEngine *vm);
 	~CharsetRendererNut() override;
 
-	void printChar(int chr, bool ignoreCharsetMask) override { error("CharsetRendererNut::printChar(): Unexpected legacy function call"); }
+	void printChar(int, bool) override { error("CharsetRendererNut::printChar(): Unexpected call to deprecated function"); }
 
 	void setCurID(int32 id) override;
 	int setFont(int id) override;
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index a0cdc460651..674c334f45a 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -163,7 +163,7 @@ bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) {
 			endLoop = true;
 			break;
 		case 3:
-			_haveMsg = (_game.version >= 7) ? 1 : 0xFF;
+			_haveMsg = 0xFF;
 			_keepText = false;
 			_msgCount = 0;
 			endLoop = true;
@@ -433,16 +433,6 @@ void ScummEngine::fakeBidiString(byte *ltext, bool ignoreVerb) const {
 
 void ScummEngine::CHARSET_1() {
 	Actor *a;
-#ifdef ENABLE_SCUMM_7_8
-	byte subtitleBuffer[200];
-	byte *subtitleLine = subtitleBuffer;
-	Common::Point subtitlePos;
-
-	if (_game.version >= 7) {
-		((ScummEngine_v7 *)this)->processSubtitleQueue();
-	}
-#endif
-
 	if (_game.heversion >= 70 && _haveMsg == 3) {
 		stopTalk();
 		return;
@@ -543,20 +533,12 @@ void ScummEngine::CHARSET_1() {
 	_talkDelay = (VAR_DEFAULT_TALK_DELAY != 0xFF) ? VAR(VAR_DEFAULT_TALK_DELAY) : 60;
 
 	if (!_keepText) {
-		if (_game.version >= 7) {
-#ifdef ENABLE_SCUMM_7_8
-			((ScummEngine_v7 *)this)->clearSubtitleQueue();
-			_nextLeft = _string[0].xpos;
-			_nextTop = _string[0].ypos + _screenTop;
-#endif
-		} else {
 #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
 			if (_game.platform == Common::kPlatformFMTowns)
 				towns_restoreCharsetBg();
 			else
 #endif
 				restoreCharsetBg();
-		}
 		_msgCount = 0;
 	} else if (_game.version <= 2) {
 		_talkDelay += _msgCount * _defaultTalkDelay;
@@ -597,19 +579,13 @@ void ScummEngine::CHARSET_1() {
 	while (handleNextCharsetCode(a, &c)) {
 		if (c == 0) {
 			// End of text reached, set _haveMsg accordingly
-			_haveMsg = (_game.version >= 7) ? 2 : 1;
+			_haveMsg = 1;
 			_keepText = false;
 			_msgCount = 0;
 			break;
 		}
 
 		if (c == 13) {
-/*#ifdef ENABLE_SCUMM_7_8
-			if (_game.version >= 7 && subtitleLine != subtitleBuffer) {
-				((ScummEngine_v7 *)this)->addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID());
-				subtitleLine = subtitleBuffer;
-			}
-#endif*/
 			if (!newLine())
 				break;
 			continue;
@@ -635,44 +611,32 @@ void ScummEngine::CHARSET_1() {
 			drawTextBox = true;
 		}
 
-		if (_game.version >= 7) {
-#ifdef ENABLE_SCUMM_7_8
-			if (subtitleLine == subtitleBuffer) {
-				subtitlePos.x = _charset->_left;
-				// BlastText position is relative to the top of the screen, adjust y-coordinate
-				subtitlePos.y = _charset->_top - _screenTop;
+		if (c & 0x80 && _useCJKMode) {
+			if (is2ByteCharacter(_language, c)) {
+				byte *buffer = _charsetBuffer + _charsetBufPos;
+				c += *buffer++ * 256; //LE
+				_charsetBufPos = buffer - _charsetBuffer;
 			}
-			*subtitleLine++ = c;
-			*subtitleLine = '\0';
-#endif
+		}
+		if (_game.version <= 3) {
+			_charset->printChar(c, false);
+			_msgCount += 1;
 		} else {
-			if (c & 0x80 && _useCJKMode) {
-				if (is2ByteCharacter(_language, c)) {
-					byte *buffer = _charsetBuffer + _charsetBufPos;
-					c += *buffer++ * 256; //LE
-					_charsetBufPos = buffer - _charsetBuffer;
-				}
-			}
-			if (_game.version <= 3) {
-				_charset->printChar(c, false);
-				_msgCount += 1;
+			if (_game.features & GF_16BIT_COLOR) {
+				// HE games which use sprites for subtitles
+			} else if (_game.heversion >= 60 && !ConfMan.getBool("subtitles") && _sound->isSoundRunning(1)) {
+				// Special case for HE games
+			} else if (_game.id == GID_LOOM && !ConfMan.getBool("subtitles") && (_sound->pollCD())) {
+				// Special case for Loom (CD), since it only uses CD audio.for sound
+			} else if (!ConfMan.getBool("subtitles") && (!_haveActorSpeechMsg || _mixer->isSoundHandleActive(*_sound->_talkChannelHandle))) {
+				// Subtitles are turned off, and there is a voice version
+				// of this message -> don't print it.
 			} else {
-				if (_game.features & GF_16BIT_COLOR) {
-					// HE games which use sprites for subtitles
-				} else if (_game.heversion >= 60 && !ConfMan.getBool("subtitles") && _sound->isSoundRunning(1)) {
-					// Special case for HE games
-				} else if (_game.id == GID_LOOM && !ConfMan.getBool("subtitles") && (_sound->pollCD())) {
-					// Special case for Loom (CD), since it only uses CD audio.for sound
-				} else if (!ConfMan.getBool("subtitles") && (!_haveActorSpeechMsg || _mixer->isSoundHandleActive(*_sound->_talkChannelHandle))) {
-					// Subtitles are turned off, and there is a voice version
-					// of this message -> don't print it.
-				} else {
-					_charset->printChar(c, false);
-				}
+				_charset->printChar(c, false);
 			}
-			_nextLeft = _charset->_left;
-			_nextTop = _charset->_top;
 		}
+		_nextLeft = _charset->_left;
+		_nextTop = _charset->_top;
 
 		if (drawTextBox)
 			mac_drawIndy3TextBox();
@@ -689,12 +653,6 @@ void ScummEngine::CHARSET_1() {
 	if (_game.platform == Common::kPlatformFMTowns && (c == 0 || c == 2 || c == 3))
 		memcpy(&_curStringRect, &_charset->_str, sizeof(Common::Rect));
 #endif
-/*
-#ifdef ENABLE_SCUMM_7_8
-	if (_game.version >= 7 && subtitleLine != subtitleBuffer) {
-		((ScummEngine_v7 *)this)->addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID());
-	}
-#endif*/
 }
 
 void ScummEngine::drawString(int a, const byte *msg) {
@@ -710,7 +668,7 @@ void ScummEngine::drawString(int a, const byte *msg) {
 
 	convertMessageToString(msg, buf, sizeof(buf));
 
-	if (_game.version >= 4 && _game.version < 7 && _game.heversion == 0 && _language == Common::HE_ISR) {
+	if (_game.version >= 4 && _game.heversion == 0 && _language == Common::HE_ISR) {
 		fakeBidiString(buf, false);
 	}
 
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index a121c481ecf..6101ef9f209 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -89,10 +89,12 @@ int TextRenderer_v7::getStringWidth(const char *str, uint numBytesMax) {
 		} else if (!_newStyle && *str == '\xff') {
 			++str;
 			--numBytesMax;
-			if (*str == 0 || *str == 3 || *str == 9 || *str == 1 || *str == 2 /*|| *str == 8*/)
+			if (*str == 0 || *str == 3 || *str == 9 || *str == 1 || *str == 2)
 				return width;
-			// No handling for this, atm, SCUMM7 does not have these anyway
-			assert(*str != 8);
+			// No handling for this, we shouldn't arrive here. FT still has the
+			// type 10 commmands for actor speech, but we should have handled them
+			// already...
+			assert(0);
 		} else if (*str != '\r' && !(_newStyle && *str == _lineBreakMarker)) {
 			width += _gr->getCharWidth((uint8)*str);
 		}
@@ -350,7 +352,7 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 		} else if (flags & kStyleAlignRight) {
 			if (x > clipRect.right)
 				x = clipRect.right;
-			if (x < clipRect.left + maxWidth);
+			if (x < clipRect.left + maxWidth)
 				x = clipRect.left + maxWidth;
 		} else {
 			if (x > clipRect.right - maxWidth)
@@ -545,11 +547,6 @@ void ScummEngine_v7::clearSubtitleQueue() {
 }
 
 void ScummEngine_v7::CHARSET_1() {
-	if (_game.id == GID_FT) {
-		ScummEngine::CHARSET_1();
-		return;
-	}
-
 	processSubtitleQueue();
 
 	if (!_haveMsg)


Commit: dcae67025e6f308c139bc0f92bfa037a2390b210
    https://github.com/scummvm/scummvm/commit/dcae67025e6f308c139bc0f92bfa037a2390b210
Author: athrxx (athrxx at scummvm.org)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: fix build

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/charset.h
    engines/scumm/string_v7.h


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 100626b82ca..0f59c02c92b 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1618,7 +1618,7 @@ void CharsetRendererMac::setCurID(int32 id) {
 	_curId = id;
 }
 
-int CharsetRendererMac::getStringWidth(int arg, const byte *text, uint strLenMax) {
+int CharsetRendererMac::getStringWidth(int arg, const byte *text) {
 	int pos = 0;
 	int width = 0;
 	int chr;
@@ -1639,7 +1639,7 @@ int CharsetRendererMac::getStringWidth(int arg, const byte *text, uint strLenMax
 	return width / 2;
 }
 
-int CharsetRendererMac::getDrawWidthIntern(uint16 chr) {
+int CharsetRendererMac::getDrawWidthIntern(uint16 chr) const {
 	return _macFonts[_curId].getCharWidth(chr);
 }
 
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index 007c08c698c..9513c86c478 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -283,7 +283,7 @@ protected:
 	int _lastTop;
 
 
-	int getDrawWidthIntern(uint16 chr);
+	int getDrawWidthIntern(uint16 chr) const;
 
 	void printCharInternal(int chr, int color, bool shadow, int x, int y);
 	void printCharToTextBox(int chr, int color, int x, int y);
diff --git a/engines/scumm/string_v7.h b/engines/scumm/string_v7.h
index 85d9b3c63a9..c142b7ff12d 100644
--- a/engines/scumm/string_v7.h
+++ b/engines/scumm/string_v7.h
@@ -50,7 +50,7 @@ public:
 			return (c >= 0x80 && c <= 0x9F) || (c >= 0xE0 && c <= 0xFD);
 		else if (lang == Common::KO_KOR)
 			return (c >= 0xB0 && c <= 0xD0);
-		else if (lang == Common::ZH_TWN || lang == Common::ZH_CNA)
+		else if (lang == Common::ZH_TWN || lang == Common::ZH_CHN)
 			return (c >= 0x80);
 		return false;
 	}


Commit: ba0cbd2dc8787ec1fd226499ce704a7bf88274e3
    https://github.com/scummvm/scummvm/commit/ba0cbd2dc8787ec1fd226499ce704a7bf88274e3
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: add support for FT with the new text handling

Changed paths:
  R engines/scumm/smush/smush_font.cpp
    engines/scumm/charset_v7.h
    engines/scumm/string_v7.cpp
    engines/scumm/string_v7.h


diff --git a/engines/scumm/charset_v7.h b/engines/scumm/charset_v7.h
index 24161e76276..2c3926a0f1f 100644
--- a/engines/scumm/charset_v7.h
+++ b/engines/scumm/charset_v7.h
@@ -4,10 +4,10 @@
  * are too numerous to list here. Please refer to the COPYRIGHT
  * file distributed with this source distribution.
  *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -15,8 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
diff --git a/engines/scumm/smush/smush_font.cpp b/engines/scumm/smush/smush_font.cpp
deleted file mode 100644
index cfcb485d0e4..00000000000
--- a/engines/scumm/smush/smush_font.cpp
+++ /dev/null
@@ -1,368 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-
-#include "common/file.h"
-#include "common/str.h"
-#include "scumm/scumm.h"
-#include "scumm/util.h"
-
-#include "scumm/smush/smush_font.h"
-
-namespace Scumm {
-
-SmushFont::SmushFont(ScummEngine *vm, const char *filename, bool use_original_colors, bool new_colors) :
-	NutRenderer(vm, filename),
-	_color(-1),
-	_new_colors(new_colors),
-	_original(use_original_colors) {
-}
-
-int SmushFont::getStringWidth(const char *str, uint numBytesMax) {
-	assert(str);
-	int maxWidth = 0;
-	int width = 0;
-
-	if (!numBytesMax)
-		return 0;
-
-	while (*str && numBytesMax) {
-		// No treatment of the ^ commands here, since they're handled/removed in handleTextResource().
-		if (is2ByteCharacter(_vm->_language, *str)) {
-			width += _vm->_2byteWidth + (_vm->_language != Common::JA_JPN ? 1 : 0);
-			++str;
-			--numBytesMax;
-		} else if (*str == '\n') {
-			maxWidth = MAX<int>(width, maxWidth);
-			width = 0;
-		} else if (*str != '\r' && *str != _vm->_newLineCharacter) {
-			width += getCharWidth(*str);
-		}
-		++str;
-		--numBytesMax;
-	}
-
-	return MAX<int>(width, maxWidth);
-}
-
-int SmushFont::getStringHeight(const char *str, uint numBytesMax) {
-	assert(str);
-	int totalHeight = 0;
-	int lineHeight = 0;
-
-	if (!numBytesMax)
-		return 0;
-
-	while (*str && numBytesMax) {
-		// No treatment of the ^ commands here, since they're handled/removed in handleTextResource().
-		if (*str == '\n') {
-			totalHeight += (lineHeight ? lineHeight : _fontHeight) + 1;
-			lineHeight = 0;
-		} else if (*str != '\r' && *str != _vm->_newLineCharacter) {
-			lineHeight = MAX<int>(lineHeight, getCharHeight(*str));
-			if (is2ByteCharacter(_vm->_language, *str)) {
-				++str;
-				--numBytesMax;
-			}
-		}
-		++str;
-		--numBytesMax;
-	}
-
-	return totalHeight + (lineHeight ? lineHeight : _fontHeight) + 1;
-}
-
-int SmushFont::drawChar(byte *buffer, int dst_width, int x, int y, byte chr) {
-	int w = _chars[chr].width;
-	int h = _chars[chr].height;
-	const byte *src = unpackChar(chr);
-	byte *dst = buffer + dst_width * y + x;
-
-	assert(dst_width == _vm->_screenWidth);
-
-	if (_original) {
-		for (int j = 0; j < h; j++) {
-			for (int i = 0; i < w; i++) {
-				int8 value = *src++;
-				if (value != _chars[chr].transparency)
-					dst[i] = value;
-			}
-			dst += dst_width;
-		}
-	} else {
-		char color = (_color != -1) ? _color : 1;
-		if (_new_colors) {
-			for (int j = 0; j < h; j++) {
-				for (int i = 0; i < w; i++) {
-					int8 value = *src++;
-					if (value == -color) {
-						dst[i] = 0xFF;
-					} else if (value == -31) {
-						dst[i] = 0;
-					} else if (value != _chars[chr].transparency) {
-						dst[i] = value;
-					}
-				}
-				dst += dst_width;
-			}
-		} else {
-			for (int j = 0; j < h; j++) {
-				for (int i = 0; i < w; i++) {
-					int8 value = *src++;
-					if (value == 1) {
-						dst[i] = color;
-					} else if (value != _chars[chr].transparency) {
-						dst[i] = 0;
-					}
-				}
-				dst += dst_width;
-			}
-		}
-	}
-	return w;
-}
-
-int SmushFont::draw2byte(byte *buffer, int dst_width, int x, int y, int idx) {
-	int w = _vm->_2byteWidth;
-	int h = _vm->_2byteHeight;
-	const byte *src = _vm->get2byteCharPtr(idx);
-	byte bits = 0;
-
-	char color = (_color != -1) ? _color : 1;
-
-	if (_new_colors)
-		color = (char)0xff;
-
-	if (_vm->_game.id == GID_FT)
-		color = 1;
-
-	enum ShadowMode {
-		kNone,
-		kNormalShadowMode,
-		kCJKv7ShadowMode,
-		kCJKv8ShadowMode
-	};
-
-	ShadowMode shadowMode = _vm->_useCJKMode ? (_vm->_game.version == 8 ? kCJKv8ShadowMode : kCJKv7ShadowMode) : kNone;
-
-	int shadowOffsetXTable[4] = { -1, 0, 1, 0 };
-	int shadowOffsetYTable[4] = { 0, 1, 0, 0 };
-	int shadowOffsetColorTable[4] = { 0, 0, 0, color };
-
-	const byte *origSrc = src;
-	for (int shadowIdx = (shadowMode == kCJKv8ShadowMode) ? 0 : (shadowMode == kCJKv7ShadowMode ? 2 : 3); shadowIdx < 4; shadowIdx++) {
-		int offX = x + shadowOffsetXTable[shadowIdx];
-		int offY = y + shadowOffsetYTable[shadowIdx];
-		byte drawColor = shadowOffsetColorTable[shadowIdx];
-
-		src = origSrc;
-		byte *dst = buffer + dst_width * offY + offX;
-
-		for (int j = 0; j < h; j++) {
-			for (int i = 0; i < w; i++) {
-				if (offX + i < 0)
-					continue;
-				if ((i % 8) == 0)
-					bits = *src++;
-				if (bits & revBitMask(i % 8)) {
-					if (shadowMode == kNormalShadowMode) {
-						dst[i + 1] = 0;
-						dst[dst_width + i] = 0;
-						dst[dst_width + i + 1] = 0;
-					}
-					dst[i] = drawColor;
-				}
-			}
-			dst += dst_width;
-		}
-	}
-	return w + 1;
-}
-
-void SmushFont::drawSubstring(const char *str, uint numBytesMax, byte *buffer, int dst_width, int x, int y) {
-	if (_vm->_language == Common::HE_ISR) {
-		for (int i = numBytesMax; i > 0; i--) {
-			x += drawChar(buffer, dst_width, x, y, str[i - 1]);
-		}
-	} else {
-		for (int i = 0; str[i] != 0 && numBytesMax; ++i) {
-			if (is2ByteCharacter(_vm->_language, str[i])) {
-				x += draw2byte(buffer, dst_width, x, y, (byte)str[i] + 256 * (byte)str[i + 1]);
-				++i;
-				--numBytesMax;
-			} else if (str[i] != '\n' && str[i] != _vm->_newLineCharacter) {
-				x += drawChar(buffer, dst_width, x, y, str[i]);
-			}
-			--numBytesMax;
-		}
-	}
-}
-
-#define MAX_STRINGS		80
-
-
-void SmushFont::drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, bool center) {
-	debugC(DEBUG_SMUSH, "SmushFont::drawString(str: '%s', x: %d, y: %d, clipRect: (%d, %d, %d, %d), center: %d)", str, x, y, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, center);
-
-	int totalLen = (int)strlen(str);
-	int lineStart = 0;
-
-	// COMI always does this for CJK strings (before any other possible yPos fixes).
-	if (_vm->_game.id == GID_CMI) {
-		if (_vm->_useCJKMode)
-			y += 2;
-		// No idea whether it is actually used. We currently don't handle this flag.
-		/*if (flags & 0x40)
-			y -= (getStringHeight(str, totalLen) / 2);*/
-	}
-
-	for (int pos = 0; pos <= totalLen; ++pos) {
-		if (str[pos] != '\0' && str[pos] != '\n')
-			continue;
-
-		int len = pos - lineStart;
-		int height = getStringHeight(str + lineStart, len);
-		if (y < clipRect.bottom) {
-			drawSubstring(str + lineStart, len, buffer, _vm->_screenWidth, center ? (x - getStringWidth(str + lineStart, len) / 2) : x, y);
-			y += height;
-		}
-
-		lineStart = pos + 1;
-	}
-}
-
-void SmushFont::drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, bool center) {
-	debugC(DEBUG_SMUSH, "SmushFont::drawStringWrap(str: '%s', x: %d, y: %d, clipRect: (%d, %d, %d, %d), center: %d)", str, x, y, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, center);
-	// This implementation is from COMI. Things are done a bit differently than in the older implementations.
-	// In particular, the older version would insert '\0' chars into the string to cut off the sub strings
-	// before calling getStringWidth(), getStringHeight() or drawSubstring() and replace these chars with the
-	// original values afterwards. COMI allows a byte length limitation in all the functions so that the sub
-	// string length can be passed and no cut off '\0' chars are needed.
-
-	int len = (int)strlen(str);
-	Common::String spaceSeparators(Common::String::format(" %c", (char)_vm->_newLineCharacter));
-	Common::String breakSeparators(Common::String::format(" \n%c", (char)_vm->_newLineCharacter));
-
-	int16 substrByteLength[MAX_STRINGS];
-	memset(substrByteLength, 0, sizeof(substrByteLength));
-	int16 substrWidths[MAX_STRINGS];
-	memset(substrWidths, 0, sizeof(substrWidths));
-	int16 substrStart[MAX_STRINGS];
-	memset(substrStart, 0, sizeof(substrStart));
-
-	int16 numSubstrings = 0;
-	int height = 0;
-	int lastSubstrHeight = 0;
-	int maxWidth = 0;
-	int curWidth = 0;
-	int curPos = -1;
-
-	// COMI does this for CJK strings (before any other possible yPos fixes, see lines 343 - 355).
-	if (_vm->_game.id == GID_CMI && _vm->_useCJKMode)
-		y += 2;
-
-	while (curPos < len) {
-		int textStart = curPos + 1;
-		while (str[textStart] && spaceSeparators.contains(str[textStart]))
-			++textStart;
-
-		int separatorWidth = curPos > 0 ? getStringWidth(str + curPos, textStart - curPos) : 0;
-
-		int nextSeparatorPos = textStart;
-		while (!breakSeparators.contains(str[nextSeparatorPos])) {
-			if (++nextSeparatorPos == len)
-				break;
-		}
-
-		int wordWidth = getStringWidth(str + textStart, nextSeparatorPos - textStart);
-		int newWidth = curWidth + separatorWidth + wordWidth;
-
-		if (curWidth && newWidth > clipRect.width()) {
-			if (numSubstrings < MAX_STRINGS) {
-				substrWidths[numSubstrings] = curWidth;
-				substrByteLength[numSubstrings] = curPos - substrStart[numSubstrings];
-				numSubstrings++;
-			}
-			newWidth = wordWidth;
-			substrStart[numSubstrings] = textStart;
-		}
-		curWidth = newWidth;
-
-		curPos = nextSeparatorPos;
-		if (!spaceSeparators.contains(str[curPos])) {
-			// This one is only triggered by '\n' (which frequently happens in COMI/English).
-			if (numSubstrings < MAX_STRINGS) {
-				substrWidths[numSubstrings] = curWidth;
-				substrByteLength[numSubstrings] = curPos - substrStart[numSubstrings];
-				numSubstrings++;
-				substrStart[numSubstrings] = curPos + 1;
-			}
-			curWidth = 0;
-		}
-	}
-
-	if (curWidth && numSubstrings < MAX_STRINGS) {
-		substrWidths[numSubstrings] = curWidth;
-		substrByteLength[numSubstrings] = curPos - substrStart[numSubstrings];
-		numSubstrings++;
-	}
-
-	for (int i = 0; i < numSubstrings; ++i) {
-		maxWidth = MAX<int>(maxWidth, substrWidths[i]);
-		lastSubstrHeight = substrByteLength[i] > 0 ? getStringHeight(str + substrStart[i], substrByteLength[i]) : 0;
-		height += lastSubstrHeight;
-	}
-
-	// I have verified these y-corrections for DIG (English and Chinese), COMI (English and Chinese) and FT (English).
-	// In COMI there seem to be more text flags which we don't support and for which I haven't seen use cases yet. I
-	// put some commented-out code in here as a reminder...
-	int clipHeight = height + lastSubstrHeight / 2;
-
-	/*if (_vm->_game.id == GID_CMI && (flags & 0x40))
-		y -= (lastSubstrHeight / 2);*/
-
-	if (y > clipRect.bottom - clipHeight /*&& !(_vm->_game.id == GID_CMI && (flags & 0x100))*/)
-		y = clipRect.bottom - clipHeight;
-
-	if (y < clipRect.top)
-		y = clipRect.top;
-
-	if (center) {
-		if (x + (maxWidth >> 1) > clipRect.right)
-			x = clipRect.right - (maxWidth >> 1);
-		if (x - (maxWidth >> 1) < clipRect.left)
-			x = clipRect.left + (maxWidth >> 1);
-	} else {
-		if (x > clipRect.right - maxWidth)
-			x = clipRect.right - maxWidth;
-		if (x < clipRect.left)
-			x = clipRect.left;
-	}
-
-	for (int i = 0; i < numSubstrings; i++) {
-		int xpos = center ? x - substrWidths[i] / 2 : x;
-		len = substrByteLength[i] > 0 ? substrByteLength[i] : 0;
-		drawSubstring(str + substrStart[i], len, buffer, _vm->_screenWidth, xpos, y);
-		y += getStringHeight(str + substrStart[i], len);
-	}
-}
-
-} // End of namespace Scumm
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 6101ef9f209..c94e161ad1d 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -4,10 +4,10 @@
  * are too numerous to list here. Please refer to the COPYRIGHT
  * file distributed with this source distribution.
  *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -15,8 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
@@ -183,6 +182,7 @@ void TextRenderer_v7::drawSubstring(const char *str, uint numBytesMax, byte *buf
 #define SCUMM7_MAX_STRINGS		80
 
 void TextRenderer_v7::drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags) {
+
 	debugC(DEBUG_GENERAL, "TextRenderer_v7::drawString(str: '%s', x: %d, y: %d, col: %d, clipRect: (%d, %d, %d, %d), flags: 0x%02x)", str, x, y, col, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, flags);
 
 	int totalLen = (int)strlen(str);
@@ -393,7 +393,7 @@ Common::Rect TextRenderer_v7::calcStringDimensions(const char *str, int x, int y
 	int width = getStringWidth(str);
 	if (_gameId == GID_CMI && _useCJKMode)
 		y += 2;
-	
+
 	if (flags & kStyleAlignCenter)
 		x -= width / 2;
 	else if (flags & kStyleAlignRight)
@@ -507,15 +507,22 @@ void ScummEngine_v8::printString(int m, const byte *msg) {
 #pragma mark -
 
 void ScummEngine_v7::processSubtitleQueue() {
+	bool usingOldSystem = (_game.id == GID_FT);
 	for (int i = 0; i < _subtitleQueuePos; ++i) {
 		SubtitleText *st = &_subtitleQueue[i];
 		if (!st->actorSpeechMsg && (!ConfMan.getBool("subtitles") || VAR(VAR_VOICE_MODE) == 0))
 			// no subtitles and there's a speech variant of the message, don't display the text
 			continue;
-		int flags = st->wrap ? kStyleWordWrap : 0;
-		if (st->center)
-			flags |= kStyleAlignCenter;
-		enqueueText(st->text, st->xpos, st->ypos, st->color, st->charset, (TextStyleFlags)flags);
+		if (usingOldSystem) {
+			if (st->center || VAR(VAR_VOICE_MODE)) {
+				enqueueText(st->text, st->xpos, st->ypos, st->color, st->charset, (TextStyleFlags)0);
+			}
+		} else {
+			int flags = st->wrap ? kStyleWordWrap : 0;
+			if (st->center)
+				flags |= kStyleAlignCenter;
+			enqueueText(st->text, st->xpos, st->ypos, st->color, st->charset, (TextStyleFlags)flags);
+		}
 	}
 }
 
@@ -549,6 +556,12 @@ void ScummEngine_v7::clearSubtitleQueue() {
 void ScummEngine_v7::CHARSET_1() {
 	processSubtitleQueue();
 
+	bool usingOldSystem = (_game.id == GID_FT);
+
+	byte subtitleBuffer[200];
+	byte *subtitleLine = subtitleBuffer;
+	Common::Point subtitlePos;
+
 	if (!_haveMsg)
 		return;
 
@@ -566,15 +579,41 @@ void ScummEngine_v7::CHARSET_1() {
 		_string[0].ypos = a->getPos().y - a->getElevation() - _screenTop;
 		s = a->_scaley * a->_talkPosY / 255;
 		_string[0].ypos += (a->_talkPosY - s) / 2 + s;
-	}
 
+		if (usingOldSystem) {
+			if (_string[0].ypos > _screenHeight - 40)
+				_string[0].ypos = _screenHeight - 40;
+
+			if (_string[0].ypos < 1)
+				_string[0].ypos = 1;
+
+			if (_string[0].xpos < 80)
+				_string[0].xpos = 80;
+
+			if (_string[0].xpos > _screenWidth - 80)
+				_string[0].xpos = _screenWidth - 80;
+		}
+	}
 	_charset->setColor(_charsetColor);
-	_charset->setCurID(_string[0].charset);
+
+	if (usingOldSystem) {
+		_charset->_top = _string[0].ypos + _screenTop;
+		_charset->_startLeft = _charset->_left = _string[0].xpos;
+		_charset->_right = _string[0].right;
+		_charset->_center = _string[0].center;
+		memcpy(_charsetColorMap, _charsetData[_charset->getCurID()], 4);
+	}
+
+	if (usingOldSystem && a && a->_charset) {
+		_charset->setCurID(a->_charset);
+	} else {
+		_charset->setCurID(_string[0].charset);
+	}
 
 	if (_talkDelay)
 		return;
 
-	if (VAR(VAR_HAVE_MSG)) {
+	if ((!usingOldSystem && VAR(VAR_HAVE_MSG)) || (usingOldSystem && _haveMsg != 1)) {
 		if ((_sound->_sfxMode & 2) == 0) {
 			stopTalk();
 		}
@@ -585,20 +624,92 @@ void ScummEngine_v7::CHARSET_1() {
 		a->runActorTalkScript(a->_talkStartFrame);
 	}
 
-	if (!_keepText)
+	int tmpNextLeft = _string[0].xpos;
+
+	if (!_keepText) {
 		clearSubtitleQueue();
+		if (usingOldSystem) {
+			_nextLeft = _string[0].xpos;
+			_nextTop = _string[0].ypos + _screenTop;
+		}
+	} else {
+		tmpNextLeft = _nextLeft;
+	}
+
+	if (usingOldSystem) {
+		int maxwidth = _charset->_right - tmpNextLeft - 1;
+		if (_charset->_center) {
+			if (maxwidth > _nextLeft)
+				maxwidth = _nextLeft;
+			maxwidth *= 2;
+		}
+
+		_charset->addLinebreaks(0, _charsetBuffer + _charsetBufPos, 0, maxwidth);
+
+		if (_charset->_center) {
+			_nextLeft -= _charset->getStringWidth(0, _charsetBuffer + _charsetBufPos) / 2;
+			if (_nextLeft <= 0)
+				_nextLeft = 0;
+		}
+
+		int c = 0;
+
+		while (handleNextCharsetCode(a, &c)) {
+			if (c == 0) {
+				// End of text
+				_haveMsg =  2;
+				_keepText = false;
+				break;
+			}
+
+			if (c == 13) {
+				// New line
+				if (subtitleLine != subtitleBuffer) {
+					addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID(), false, false);
+					subtitleLine = subtitleBuffer;
+				}
+
+				if (!newLine())
+					break;
+				continue;
+			}
+
+			_charset->_left = _nextLeft;
+			_charset->_top = _nextTop;
+
+			if (subtitleLine == subtitleBuffer) {
+				subtitlePos.x = _charset->_left;
+				// BlastText position is relative to the top of the screen, adjust y-coordinate
+				subtitlePos.y = _charset->_top - _screenTop;
+			}
+			*subtitleLine++ = c;
+			*subtitleLine = '\0';
+
+			_talkDelay += (int)VAR(VAR_CHARINC);
+		}
 
-	_talkDelay = VAR(VAR_DEFAULT_TALK_DELAY);
-	int newPos = _charsetBufPos;
-	while (_charsetBuffer[newPos++])
-		_talkDelay += VAR(VAR_CHARINC);
+		_haveMsg = 1;
+		_keepText = false;
 
-	Common::Point subtitlePos(_string[0].xpos, _string[0].ypos);
-	addSubtitleToQueue(_charsetBuffer + _charsetBufPos, subtitlePos, _charsetColor, _charset->getCurID(), _string[0].center, _string[0].wrapping);
-	_charsetBufPos = newPos;
+		if (subtitleLine != subtitleBuffer) {
+			addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID(), false, false);
+		}
+	} else {
+		_talkDelay = VAR(VAR_DEFAULT_TALK_DELAY);
+		int newPos = _charsetBufPos;
+		while (_charsetBuffer[newPos++])
+			_talkDelay += VAR(VAR_CHARINC);
+
+		subtitlePos.x = _string[0].xpos;
+		subtitlePos.y = _string[0].ypos;
+
+		addSubtitleToQueue(_charsetBuffer + _charsetBufPos, subtitlePos, _charsetColor, _charset->getCurID(), _string[0].center, _string[0].wrapping);
+		_charsetBufPos = newPos;
+
+		_haveMsg = VAR(VAR_HAVE_MSG) = (_game.version == 8 && _string[0].no_talk_anim) ? 2 : 1;
+		_keepText = false;
+	}
 
-	_haveMsg = VAR(VAR_HAVE_MSG) = (_game.version == 8 && _string[0].no_talk_anim) ? 2 : 1;
-	_keepText = false;
 	_string[0] = saveStr;
 }
 
diff --git a/engines/scumm/string_v7.h b/engines/scumm/string_v7.h
index c142b7ff12d..25fbd213c42 100644
--- a/engines/scumm/string_v7.h
+++ b/engines/scumm/string_v7.h
@@ -4,10 +4,10 @@
  * are too numerous to list here. Please refer to the COPYRIGHT
  * file distributed with this source distribution.
  *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -15,8 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
 


Commit: 5963241222af824ba79c6b3621bfe28556847e07
    https://github.com/scummvm/scummvm/commit/5963241222af824ba79c6b3621bfe28556847e07
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: FT: Fix text bugs

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/string_v7.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 0f59c02c92b..1c471c8d558 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -456,7 +456,7 @@ int CharsetRendererClassic::getCharWidth(uint16 chr) const {
 
 int CharsetRenderer::getStringWidth(int arg, const byte *text) {
 	int pos = 0;
-	int width = 1;
+	int width = (_vm->_game.id == GID_FT) ? 0 : 1;
 	int chr;
 	int oldID = getCurID();
 	int code = (_vm->_game.heversion >= 80) ? 127 : 64;
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index c94e161ad1d..6b3ec30b698 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -688,9 +688,6 @@ void ScummEngine_v7::CHARSET_1() {
 			_talkDelay += (int)VAR(VAR_CHARINC);
 		}
 
-		_haveMsg = 1;
-		_keepText = false;
-
 		if (subtitleLine != subtitleBuffer) {
 			addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID(), false, false);
 		}


Commit: ede23b4a5e43dfb23157774536ef7c9dc8954418
    https://github.com/scummvm/scummvm/commit/ede23b4a5e43dfb23157774536ef7c9dc8954418
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: DIG (demo): Fix crash caused by unhandled escape codes

Changed paths:
    engines/scumm/string_v7.cpp


diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 6b3ec30b698..637a706fbc4 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -255,8 +255,8 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 	if (!_newStyle) {
 		Common::String invalidChars("@\xff\x03\x09\x01\x02\x08");
 		for (int i = 0; i < len; ++i)
-		if (invalidChars.contains(str[i]))
-			assert(true == false);
+			if (invalidChars.contains(str[i]))
+				assert(true == false);
 	}
 
 	int16 substrByteLength[SCUMM7_MAX_STRINGS];
@@ -556,7 +556,7 @@ void ScummEngine_v7::clearSubtitleQueue() {
 void ScummEngine_v7::CHARSET_1() {
 	processSubtitleQueue();
 
-	bool usingOldSystem = (_game.id == GID_FT);
+	bool usingOldSystem = (_game.id == GID_FT) || (_game.id == GID_DIG && _game.features & GF_DEMO);
 
 	byte subtitleBuffer[200];
 	byte *subtitleLine = subtitleBuffer;


Commit: 69022c5060f081cc5d021ab6dac49d9692bf6f98
    https://github.com/scummvm/scummvm/commit/69022c5060f081cc5d021ab6dac49d9692bf6f98
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: DIG (demo): Fix escape code 3 handling

Changed paths:
    engines/scumm/string.cpp


diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 674c334f45a..9d4e11d2c2f 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -163,7 +163,7 @@ bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) {
 			endLoop = true;
 			break;
 		case 3:
-			_haveMsg = 0xFF;
+			_haveMsg = _game.version == 7 ? 1 : 0xFF;
 			_keepText = false;
 			_msgCount = 0;
 			endLoop = true;


Commit: 31fca3be6d0d3d4468f49b335828f7aed4d8aed4
    https://github.com/scummvm/scummvm/commit/31fca3be6d0d3d4468f49b335828f7aed4d8aed4
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: FT: Fix crash related to text handling

Changed paths:
    engines/scumm/string_v7.cpp


diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 637a706fbc4..8108ac9e4ad 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -220,8 +220,17 @@ void TextRenderer_v7::drawString(const char *str, byte *buffer, Common::Rect &cl
 				// with kStyleAlignLeft flag).
 				xpos = x - _direction * width;
 
-			if (!_newStyle)
-				xpos = (_direction == 1) ? CLIP<int>(xpos, clipRect.left, _screenWidth - width) : CLIP<int>(xpos, clipRect.left + width, _screenWidth - 1);
+			if (!_newStyle) {
+				int amax = _screenWidth - width;
+				int amin = (_direction == 1) ? clipRect.left : clipRect.left + width;
+				// Full Throttle has several lines which can fail the amin <= amax assertion in our CLIP()
+				// function; this is because said lines have a width higher than _screenWidth.
+				// Doing this restores the correct and accurate behaviour for those lines.
+				if (amin > amax) {
+					amax = amin;
+				}
+				xpos = CLIP<int>(xpos, amin, amax);
+			}
 
 			drawSubstring(str + lineStart, len, buffer, clipRect, xpos, y, pitch, col, flags);
 			y += height;


Commit: 9f2e0f6d451fed3f3ab570d6be72deb6c151b38a
    https://github.com/scummvm/scummvm/commit/9f2e0f6d451fed3f3ab570d6be72deb6c151b38a
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: Clean up test assertions

Changed paths:
    engines/scumm/string_v7.cpp


diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 8108ac9e4ad..057164d3523 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -90,10 +90,7 @@ int TextRenderer_v7::getStringWidth(const char *str, uint numBytesMax) {
 			--numBytesMax;
 			if (*str == 0 || *str == 3 || *str == 9 || *str == 1 || *str == 2)
 				return width;
-			// No handling for this, we shouldn't arrive here. FT still has the
-			// type 10 commmands for actor speech, but we should have handled them
-			// already...
-			assert(0);
+
 		} else if (*str != '\r' && !(_newStyle && *str == _lineBreakMarker)) {
 			width += _gr->getCharWidth((uint8)*str);
 		}
@@ -257,15 +254,13 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
 	Common::String spaceSeparators(Common::String::format(" %c", (char)_lineBreakMarker));
 	Common::String breakSeparators(Common::String::format(" \n%c", (char)_lineBreakMarker));
 
-	// This wouldn't work properly (at least not without careful extra work) with old style escape codes.
-	// I doubt very much that any SCUMM7 game would have them, since they don't always work properly when
-	// combined with speech playback and they also would be difficult to maintain with language bundles.
-	// Let's check for these, so I'll definitely notice during my tests...
+	// We have already handled the escape codes used by FT and DIG (demo),
+	// so the following assertion is there just for good measure.
 	if (!_newStyle) {
 		Common::String invalidChars("@\xff\x03\x09\x01\x02\x08");
-		for (int i = 0; i < len; ++i)
-			if (invalidChars.contains(str[i]))
-				assert(true == false);
+		for (int i = 0; i < len; ++i) {
+			assert(!invalidChars.contains(str[i]));
+		}
 	}
 
 	int16 substrByteLength[SCUMM7_MAX_STRINGS];


Commit: 9468d24958ca7924e5e8d3779d1a0fc22a526410
    https://github.com/scummvm/scummvm/commit/9468d24958ca7924e5e8d3779d1a0fc22a526410
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: FT/DIG: Fix line spacing accuracy

Changed paths:
    engines/scumm/string_v7.cpp


diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 057164d3523..6dfd446d6dc 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -142,7 +142,7 @@ int TextRenderer_v7::getStringHeight(const char *str, uint numBytesMax) {
 		--numBytesMax;
 	}
 
-	return totalHeight + (lineHeight ? lineHeight : _gr->getFontHeight()) + 1;
+	return totalHeight + (lineHeight ? lineHeight : _gr->getFontHeight()) + (_newStyle ? 1 : 0);
 }
 
 void TextRenderer_v7::drawSubstring(const char *str, uint numBytesMax, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 &col, TextStyleFlags flags) {


Commit: bd21f18d0cb6c3377d21ec9ebee5167aafe99565
    https://github.com/scummvm/scummvm/commit/bd21f18d0cb6c3377d21ec9ebee5167aafe99565
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: DIG (demo): Improve text accuracy
This is done just by adapting everything FT does to DIG demo, since they share the same exact code

Changed paths:
    engines/scumm/actor.cpp
    engines/scumm/string_v7.cpp


diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index d4eeae4212a..3694d02f512 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -2861,13 +2861,14 @@ void ScummEngine::resetV1ActorTalkColor() {
 void ScummEngine_v7::actorTalk(const byte *msg) {
 	Actor *a;
 	bool stringWrap = false;
+	bool usingOldSystem = (_game.id == GID_FT) || (_game.id == GID_DIG && _game.features & GF_DEMO);
 
 	convertMessageToString(msg, _charsetBuffer, sizeof(_charsetBuffer));
 
 	// Play associated speech, if any
 	playSpeech((byte *)_lastStringTag);
 
-	if (_game.id == GID_DIG || _game.id == GID_CMI) {
+	if (!usingOldSystem) {
 		if (VAR(VAR_HAVE_MSG)) {
 			if (_game.id == GID_DIG && _roomResource == 58 && msg[0] == ' ' && !msg[1])
 				return;
@@ -2900,15 +2901,18 @@ void ScummEngine_v7::actorTalk(const byte *msg) {
 	_charsetBufPos = 0;
 	_talkDelay = 0;
 	_haveMsg = 1;
-	if (_game.id == GID_FT)
+	if (usingOldSystem)
 		VAR(VAR_HAVE_MSG) = 0xFF;
-	_haveActorSpeechMsg = (_game.id == GID_FT) ? true : (!_sound->isSoundRunning(kTalkSoundID));
-	if (_game.id == GID_DIG || _game.id == GID_CMI) {
+	_haveActorSpeechMsg = usingOldSystem ? true : (!_sound->isSoundRunning(kTalkSoundID));
+
+	if (!usingOldSystem) {
 		stringWrap = _string[0].wrapping;
 		_string[0].wrapping = true;
 	}
+
 	CHARSET_1();
-	if (_game.id == GID_DIG || _game.id == GID_CMI) {
+
+	if (!usingOldSystem) {
 		if (_game.version == 8)
 			VAR(VAR_HAVE_MSG) = (_string[0].no_talk_anim) ? 2 : 1;
 		else
@@ -3042,7 +3046,7 @@ void ScummEngine::stopTalk() {
 			((ActorHE *)a)->_heTalking = false;
 	}
 
-	if (_game.id == GID_DIG || _game.id == GID_CMI) {
+	if ((_game.id == GID_DIG && !(_game.features & GF_DEMO)) || _game.id == GID_CMI) {
 		setTalkingActor(0);
 		VAR(VAR_HAVE_MSG) = 0;
 	} else if (_game.heversion >= 60) {
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 6dfd446d6dc..793b08b5d73 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -511,7 +511,7 @@ void ScummEngine_v8::printString(int m, const byte *msg) {
 #pragma mark -
 
 void ScummEngine_v7::processSubtitleQueue() {
-	bool usingOldSystem = (_game.id == GID_FT);
+	bool usingOldSystem = (_game.id == GID_FT) || (_game.id == GID_DIG && _game.features & GF_DEMO);
 	for (int i = 0; i < _subtitleQueuePos; ++i) {
 		SubtitleText *st = &_subtitleQueue[i];
 		if (!st->actorSpeechMsg && (!ConfMan.getBool("subtitles") || VAR(VAR_VOICE_MODE) == 0))


Commit: c312cf8e7ef2334ed26b7b26eb77836672ed8341
    https://github.com/scummvm/scummvm/commit/c312cf8e7ef2334ed26b7b26eb77836672ed8341
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: FT (demo): Handle escape sequence 3 properly

Changed paths:
    engines/scumm/string.cpp


diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 9d4e11d2c2f..325aadccdf6 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -163,7 +163,7 @@ bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) {
 			endLoop = true;
 			break;
 		case 3:
-			_haveMsg = _game.version == 7 ? 1 : 0xFF;
+			_haveMsg = _game.version == 7 && !(_game.id == GID_FT && _game.features & GF_DEMO) ? 1 : 0xFF;
 			_keepText = false;
 			_msgCount = 0;
 			endLoop = true;


Commit: d3f756eedc1b91c8d56f42476c8501b55d3dfc20
    https://github.com/scummvm/scummvm/commit/d3f756eedc1b91c8d56f42476c8501b55d3dfc20
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: Remove whitespace in nut_renderer.cpp

Changed paths:
    engines/scumm/nut_renderer.cpp


diff --git a/engines/scumm/nut_renderer.cpp b/engines/scumm/nut_renderer.cpp
index e4c101e0896..8a97407c158 100644
--- a/engines/scumm/nut_renderer.cpp
+++ b/engines/scumm/nut_renderer.cpp
@@ -442,7 +442,7 @@ int NutRenderer::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, in
 				src += clipWdth;
 				dst += pitch;
 			}
-		}		
+		}
 	}
 	return _direction * width;
 }


Commit: 47270487b6ee50c352618780cb303ee92646177c
    https://github.com/scummvm/scummvm/commit/47270487b6ee50c352618780cb303ee92646177c
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: Implement correct behavior for old style text overflow

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/string_v7.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 1c471c8d558..d0b63657d80 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1986,16 +1986,44 @@ int CharsetRendererV7::draw2byte(byte *buffer, Common::Rect &clipRect, int x, in
 }
 
 int CharsetRendererV7::drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags, byte chr) {
-	// I am aware of not doing anything with the clipRect here, but I currently see no need to upgrade the old rendering with that.
 	if (!prepareDraw(chr))
 		return 0;
+
 	_width = getCharWidth(chr);
+
 	if (_direction < 0)
 		x -= _width;
+
+	int width = MIN(_origWidth, clipRect.right - x);
+	int height = MIN(_origHeight, clipRect.bottom - y);
+
 	_vm->_charsetColorMap[1] = col;
-	VirtScreen &vs = _vm->_virtscr[kMainVirtScreen];
-	drawBitsN(vs, buffer + (y + _offsY) * vs.pitch + x, _charPtr, *_fontPtr, y, _origWidth, _origHeight);
-	return _direction * _width;
+	byte *cmap = _vm->_charsetColorMap;
+	const byte *src = _charPtr;
+	byte *dst = buffer + (y + _offsY) * pitch + x;
+	uint8 bpp = *_fontPtr;
+	byte bits = *src++;
+	byte numbits = 8;
+	pitch -= _origWidth;
+
+	while (height--) {
+		for (int dx = x; dx < x + _origWidth; ++dx) {
+			byte color = (bits >> (8 - bpp)) & 0xFF;
+			if (color && dx >= 0 && dx < x + width && y >= 0)
+				*dst = cmap[color];
+			dst++;
+			bits <<= bpp;
+			numbits -= bpp;
+			if (numbits == 0) {
+				bits = *src++;
+				numbits = 8;
+			}
+		}
+		dst += pitch;
+		++y;
+	}
+
+	return _direction * width;
 }
 
 
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 793b08b5d73..375d9f2677c 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -217,18 +217,6 @@ void TextRenderer_v7::drawString(const char *str, byte *buffer, Common::Rect &cl
 				// with kStyleAlignLeft flag).
 				xpos = x - _direction * width;
 
-			if (!_newStyle) {
-				int amax = _screenWidth - width;
-				int amin = (_direction == 1) ? clipRect.left : clipRect.left + width;
-				// Full Throttle has several lines which can fail the amin <= amax assertion in our CLIP()
-				// function; this is because said lines have a width higher than _screenWidth.
-				// Doing this restores the correct and accurate behaviour for those lines.
-				if (amin > amax) {
-					amax = amin;
-				}
-				xpos = CLIP<int>(xpos, amin, amax);
-			}
-
 			drawSubstring(str + lineStart, len, buffer, clipRect, xpos, y, pitch, col, flags);
 			y += height;
 		}


Commit: 1408d6f7e361c175f9bd5b4eed79cfb14ffdfea6
    https://github.com/scummvm/scummvm/commit/1408d6f7e361c175f9bd5b4eed79cfb14ffdfea6
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: FT: Fix missing _talkDelay setup

Changed paths:
    engines/scumm/string_v7.cpp


diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 375d9f2677c..f9e6130f652 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -629,6 +629,8 @@ void ScummEngine_v7::CHARSET_1() {
 	}
 
 	if (usingOldSystem) {
+		_talkDelay = VAR(VAR_DEFAULT_TALK_DELAY);
+
 		int maxwidth = _charset->_right - tmpNextLeft - 1;
 		if (_charset->_center) {
 			if (maxwidth > _nextLeft)


Commit: 6fd84288a2ef653d90aa6dde87abef624a95fa3f
    https://github.com/scummvm/scummvm/commit/6fd84288a2ef653d90aa6dde87abef624a95fa3f
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: Fix typo in string_v7.h

Changed paths:
    engines/scumm/string_v7.h


diff --git a/engines/scumm/string_v7.h b/engines/scumm/string_v7.h
index 25fbd213c42..9ff57c1b078 100644
--- a/engines/scumm/string_v7.h
+++ b/engines/scumm/string_v7.h
@@ -20,7 +20,7 @@
  */
 
 #ifndef SCUMM_STRING_V7_H
-#define SCUMM_STRINGV_7_H
+#define SCUMM_STRING_V7_H
 
 #ifdef ENABLE_SCUMM_7_8
 


Commit: bab5ff89b4e48ac261f240739b9a7e7be13490ad
    https://github.com/scummvm/scummvm/commit/bab5ff89b4e48ac261f240739b9a7e7be13490ad
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: Remove empty line

Changed paths:
    engines/scumm/string_v7.cpp


diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index f9e6130f652..13df4ab5864 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -179,7 +179,6 @@ void TextRenderer_v7::drawSubstring(const char *str, uint numBytesMax, byte *buf
 #define SCUMM7_MAX_STRINGS		80
 
 void TextRenderer_v7::drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags) {
-
 	debugC(DEBUG_GENERAL, "TextRenderer_v7::drawString(str: '%s', x: %d, y: %d, col: %d, clipRect: (%d, %d, %d, %d), flags: 0x%02x)", str, x, y, col, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, flags);
 
 	int totalLen = (int)strlen(str);


Commit: b15ae51f8b668714b9cb6f9afd8da2b70bfc8d0a
    https://github.com/scummvm/scummvm/commit/b15ae51f8b668714b9cb6f9afd8da2b70bfc8d0a
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: Fix missing virtual dtor warning

Changed paths:
    engines/scumm/charset_v7.h


diff --git a/engines/scumm/charset_v7.h b/engines/scumm/charset_v7.h
index 2c3926a0f1f..c1699471579 100644
--- a/engines/scumm/charset_v7.h
+++ b/engines/scumm/charset_v7.h
@@ -38,6 +38,8 @@ enum TextStyleFlags {
 
 class GlyphRenderer_v7 {
 public:
+	virtual ~GlyphRenderer_v7() {};
+
 	virtual int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) = 0;
 	virtual int drawChar(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags, byte chr) = 0;
 	virtual int getCharWidth(uint16 chr) const = 0;


Commit: 755ba94486e80022d9544bd57f1075b7cad1e419
    https://github.com/scummvm/scummvm/commit/755ba94486e80022d9544bd57f1075b7cad1e419
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: Remove no-op statements

Changed paths:
    engines/scumm/string_v7.cpp


diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 13df4ab5864..25df648fbeb 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -466,8 +466,6 @@ void ScummEngine_v7::drawBlastTexts() {
 
 		bt.rect.top += _screenTop;
 		bt.rect.bottom += _screenTop;
-		bt.rect.left;
-		bt.rect.right;
 		markRectAsDirty(vs->number, bt.rect);
 	}
 }


Commit: 123e6d10b63d12f26082abd329485f3d824560f9
    https://github.com/scummvm/scummvm/commit/123e6d10b63d12f26082abd329485f3d824560f9
Author: athrxx (athrxx at scummvm.org)
Date: 2022-04-08T19:53:44+02:00

Commit Message:
SCUMM: (COMI/CJK) - fix verb drawing regression

Changed paths:
    engines/scumm/verbs.cpp


diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 57e640a4c04..309ee8e78e1 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -996,6 +996,7 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
 
 		TextStyleFlags flags = kStyleAlignLeft;
 		int xpos = vs->origLeft;
+		int ypos = vs->curRect.top;
 
 		if (vs->center) {
 			flags = kStyleAlignCenter;
@@ -1023,7 +1024,7 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
 		_charset->setCurID(vs->charset_nr);
 
 		// Compute the text rect
-		vs->oldRect = vs->curRect = _textV7->calcStringDimensions((const char*)msg, xpos, vs->curRect.top, flags);
+		vs->curRect = _textV7->calcStringDimensions((const char*)msg, xpos, vs->curRect.top, flags);
 		
 		const int maxWidth = _screenWidth - vs->curRect.left;
 		int finalWidth = maxWidth;
@@ -1044,13 +1045,16 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
 				--len;
 			}
 
-			enqueueText(tmpBuf, xpos, vs->curRect.top, color, vs->charset_nr, flags);
-			enqueueText(&msg[len + 1], xpos, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, flags);
+			enqueueText(tmpBuf, xpos, ypos, color, vs->charset_nr, flags);
+			enqueueText(&msg[len + 1], xpos, ypos + _verbLineSpacing, color, vs->charset_nr, flags);
 			vs->curRect.right = vs->curRect.left + finalWidth;
 			vs->curRect.bottom += _verbLineSpacing;			
 		} else {
-			enqueueText(msg, xpos, vs->curRect.top, color, vs->charset_nr, flags);
+			enqueueText(msg, xpos, ypos, color, vs->charset_nr, flags);
 		}
+
+		vs->oldRect = vs->curRect;
+		vs->curRect.top = ypos;
 		_charset->setCurID(oldID);
 	}
 }




More information about the Scummvm-git-logs mailing list