[Scummvm-git-logs] scummvm master -> 30f4fd049ee662eb4db7e57a66fe7198354f0578

athrxx athrxx at scummvm.org
Sat Oct 24 16:00:59 UTC 2020


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

Summary:
b476e69b9d SCUMM: (DIG/CJK) - fix 2byte font glyph shadows
09a9f4b158 SCUMM: (DIG/CJK) - fix 'invalid escape code' error in Chinese intro
afef3d71cb SCUMM: (DIG/CJK) - fix blast text rendering and positioning
9ff197ef8b SCUMM: (DIG/CJK) - fix CJK Smush font drawing
651eca6abb SCUMM: (SMUSH) - fix text flags
c4d3cc6c18 SCUMM: (DIG/CJK) - fix regression in text positioning
c5216c6367 SCUMM: (DIG/CJK) - add specific text positioning fix from disasm
e665730d3d SCUMM: (DIG/CJK) - fix bug in charset rendering
1266fdeb0d SCUMM: (COMI/CJK) - fix smush font glyph shadows
4a7b720a31 SCUMM: whitespace
7b3b47024b SCUMM: (COMI/CJK) - fix blast text shading and positioning
30f4fd049e SCUMM: (SMUSH) - fix text coordinates and wrapping


Commit: b476e69b9d26c62377e716830fc99fdb2b055c1e
    https://github.com/scummvm/scummvm/commit/b476e69b9d26c62377e716830fc99fdb2b055c1e
Author: athrxx (athrxx at scummvm.org)
Date: 2020-10-24T17:58:28+02:00

Commit Message:
SCUMM: (DIG/CJK) - fix 2byte font glyph shadows

All CJK versions use the same shading. The original code hardly ever diverges for the different languages (mostly for accessing the character bitmap data from the font files).

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/smush/smush_font.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index b89ce446c2..60d36597b4 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -313,7 +313,7 @@ int CharsetRendererCommon::getFontHeight() {
 int CharsetRendererClassic::getCharWidth(uint16 chr) {
 	int spacing = 0;
 
- 	if (_vm->_useCJKMode && chr >= 0x80)
+	if (_vm->_useCJKMode && chr >= 0x80)
 		return _vm->_2byteWidth / 2;
 
 	int offs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
@@ -523,7 +523,7 @@ void CharsetRendererPC::enableShadow(bool enable) {
 	_shadowColor = 0;
 	_enableShadow = enable;
 
-	if (_vm->_game.version >= 7 && _vm->_language == Common::KO_KOR)
+	if (_vm->_game.version >= 7 && _vm->_useCJKMode)
 		_shadowType = kHorizontalShadowType;
 	else
 		_shadowType = kNormalShadowType;
@@ -909,7 +909,7 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr,
 bool CharsetRendererClassic::prepareDraw(uint16 chr) {
 	bool is2byte = (chr >= 256 && _vm->_useCJKMode);
 	if (is2byte) {
-		if (_vm->_language == Common::KO_KOR)
+		if (_vm->_game.version >= 7)
 			enableShadow(true);
 
 		_charPtr = _vm->get2byteCharPtr(chr);
@@ -923,6 +923,8 @@ bool CharsetRendererClassic::prepareDraw(uint16 chr) {
 		}
 
 		return true;
+	} else {
+		enableShadow(false);
 	}
 
 	uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
@@ -1405,7 +1407,7 @@ CharsetRendererTownsClassic::CharsetRendererTownsClassic(ScummEngine *vm) : Char
 int CharsetRendererTownsClassic::getCharWidth(uint16 chr) {
 	int spacing = 0;
 
- 	if (_vm->_useCJKMode) {
+	if (_vm->_useCJKMode) {
 		if ((chr & 0xff00) == 0xfd00) {
 			chr &= 0xff;
 		} else if (chr >= 256) {
diff --git a/engines/scumm/smush/smush_font.cpp b/engines/scumm/smush/smush_font.cpp
index 46a2722715..12722cfc34 100644
--- a/engines/scumm/smush/smush_font.cpp
+++ b/engines/scumm/smush/smush_font.cpp
@@ -130,27 +130,31 @@ int SmushFont::draw2byte(byte *buffer, int dst_width, int x, int y, int idx) {
 	enum ShadowMode {
 		kNone,
 		kNormalShadowMode,
-		kKoreanV7ShadowMode,
+		kCJKv7ShadowMode,
 		kKoreanV8ShadowMode
 	};
 
 	ShadowMode shadowMode = kNone;
 
-	if (_vm->_language == Common::KO_KOR) {
-		if (_vm->_game.version == 8)
+	if (_vm->_useCJKMode) {
+		// TODO: Check Chinese and Japanese COMI
+		// For now the kKoreanV8ShadowMode is limited to Korean, because it isn't known yet
+		// how Chinese and Japanese COMI is supposed to look like (I suspect that this gets
+		// rendered the same way, just as it is done in DIG).
+		if (_vm->_game.version == 8 && _vm->_language == Common::KO_KOR)
 			shadowMode = kKoreanV8ShadowMode;
-		else
-			shadowMode = kKoreanV7ShadowMode;
+		else if (_vm->_game.version != 8)
+			shadowMode = kCJKv7ShadowMode;
 	}
 
-	int shadowOffsetXTable[4] = {-1, 0, 1, 0};
-	int shadowOffsetYTable[4] = {0, 1, 0, 0};
-	int shadowOffsetColorTable[4] = {0, 0, 0, color};
+	int shadowOffsetXTable[4] = { -1, 0, 1, 0 };
+	int shadowOffsetYTable[4] = { 0, 1, 0, 0 };
+	int shadowOffsetColorTable[4] = { 0, 0, 0, color };
 
 	int shadowIdx = 3;
 	if (shadowMode == kKoreanV8ShadowMode)
 		shadowIdx = 0;
-	else if (shadowMode == kKoreanV7ShadowMode)
+	else if (shadowMode == kCJKv7ShadowMode)
 		shadowIdx = 2;
 
 	const byte *origSrc = src;


Commit: 09a9f4b1587665c763f098b89fbabd62f848a326
    https://github.com/scummvm/scummvm/commit/09a9f4b1587665c763f098b89fbabd62f848a326
Author: athrxx (athrxx at scummvm.org)
Date: 2020-10-24T17:58:28+02:00

Commit Message:
SCUMM: (DIG/CJK) - fix 'invalid escape code' error in Chinese intro

(the '^' char could occasionally appear as the second byte in a 2byte character)

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


diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index 5c4baced8b..c5cf001a28 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -41,7 +41,6 @@ static inline bool checkSJISCode(byte c) {
 	return false;
 }
 
-
 class CharsetRenderer {
 public:
 
diff --git a/engines/scumm/smush/smush_font.h b/engines/scumm/smush/smush_font.h
index 9cfea5a26d..e81600ca98 100644
--- a/engines/scumm/smush/smush_font.h
+++ b/engines/scumm/smush/smush_font.h
@@ -47,6 +47,16 @@ public:
 	void setColor(byte c) { _color = c; }
 	void drawString    (const char *str, byte *buffer, int dst_width, int dst_height, int x, int y, bool center);
 	void drawStringWrap(const char *str, byte *buffer, int dst_width, int dst_height, int x, int y, int left, int right, 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_CNA)
+			return (c >= 0x80);
+		return false;
+	}
 };
 
 } // End of namespace Scumm
diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp
index da558aad9c..5b8e01ecca 100644
--- a/engines/scumm/smush/smush_player.cpp
+++ b/engines/scumm/smush/smush_player.cpp
@@ -613,6 +613,8 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 					error("invalid escape code in text string");
 				}
 			} else {
+				if (SmushFont::is2ByteCharacter(_vm->_language, *sptr))
+					*sptr2++ = *sptr++;
 				*sptr2++ = *sptr++;
 			}
 		}


Commit: afef3d71cb825cbb7eb33e51477bb5e321785c1a
    https://github.com/scummvm/scummvm/commit/afef3d71cb825cbb7eb33e51477bb5e321785c1a
Author: athrxx (athrxx at scummvm.org)
Date: 2020-10-24T17:58:28+02:00

Commit Message:
SCUMM: (DIG/CJK) - fix blast text rendering and positioning

- fix getStringWidth()
- fix string translation
- skip special newline character when drawing text

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 60d36597b4..96699bba45 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -331,8 +331,11 @@ int CharsetRenderer::getStringWidth(int arg, const byte *text) {
 	int code = (_vm->_game.heversion >= 80) ? 127 : 64;
 
 	while ((chr = text[pos++]) != 0) {
-		if (chr == '\n' || chr == '\r' || chr == _vm->_newLineCharacter)
+		if (_vm->_game.version == 7 && chr == _vm->_newLineCharacter)
+			continue;
+		else if (chr == '\n' || chr == '\r' || chr == _vm->_newLineCharacter)
 			break;
+			
 		if (_vm->_game.heversion >= 72) {
 			if (chr == code) {
 				chr = text[pos++];
@@ -488,7 +491,7 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
 				if (_vm->_language == Common::KO_KOR || _vm->_language == Common::ZH_TWN) {
 					curw++;
 				}
-			} else {
+			} else if (chr != _vm->_newLineCharacter) {
 				curw += getCharWidth(chr);
 			}
 		} else {
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index c5cf001a28..2c52f81924 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -41,6 +41,16 @@ static inline bool checkSJISCode(byte c) {
 	return false;
 }
 
+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;
+}
+
 class CharsetRenderer {
 public:
 
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 8a2ef97f4b..7d7953c29f 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -180,7 +180,7 @@ void ScummEngine_v6::drawBlastTexts() {
 
 				// Some localizations may override colors
 				// See credits in Chinese COMI
-				if (_game.id == GID_CMI &&	_language == Common::ZH_TWN &&
+				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');
@@ -191,7 +191,7 @@ void ScummEngine_v6::drawBlastTexts() {
 					}
 				}
 
-				if (c != 0 && c != 0xFF && c != '\n') {
+				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
@@ -1302,7 +1302,7 @@ int ScummEngine::convertMessageToString(const byte *msg, byte *dst, int dstSize)
 				num += (_game.version == 8) ? 4 : 2;
 			}
 		} else {
-			if ((chr != '@') || (_game.id == GID_CMI && _language == Common::ZH_TWN) ||
+			if ((chr != '@') || (_game.version >= 7 && is2ByteCharacter(_language, lastChr)) ||
 				(_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN) ||
 				(_game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN && checkSJISCode(lastChr))) {
 				*dst++ = chr;


Commit: 9ff197ef8bb95998d831a51a7d586fd628613925
    https://github.com/scummvm/scummvm/commit/9ff197ef8bb95998d831a51a7d586fd628613925
Author: athrxx (athrxx at scummvm.org)
Date: 2020-10-24T17:58:28+02:00

Commit Message:
SCUMM: (DIG/CJK) - fix CJK Smush font drawing

- fix character spacing, vertical placement, clipping etc.
- in particular I rewrote SmushFont::drawStringWrap() and modified getStringWidth() and getStringHeight() to match COMI disasm (fully compatible with DIG, but a bit nicer)
- this actually also fixes some slightly misplaced English (or other standard font language) strings

Changed paths:
    engines/scumm/nut_renderer.cpp
    engines/scumm/nut_renderer.h
    engines/scumm/smush/smush_font.cpp
    engines/scumm/smush/smush_font.h


diff --git a/engines/scumm/nut_renderer.cpp b/engines/scumm/nut_renderer.cpp
index d8672c473c..3a53f2726e 100644
--- a/engines/scumm/nut_renderer.cpp
+++ b/engines/scumm/nut_renderer.cpp
@@ -32,6 +32,7 @@ NutRenderer::NutRenderer(ScummEngine *vm, const char *filename) :
 	_vm(vm),
 	_numChars(0),
 	_maxCharSize(0),
+	_fontHeight(0),
 	_charBuffer(0),
 	_decodedData(0) {
 	memset(_chars, 0, sizeof(_chars));
@@ -125,8 +126,8 @@ void NutRenderer::loadFont(const char *filename) {
 	for (l = 0; l < _numChars; l++) {
 		offset += READ_BE_UINT32(dataSrc + offset + 4) + 16;
 		int width = READ_LE_UINT16(dataSrc + offset + 14);
-		int height = READ_LE_UINT16(dataSrc + offset + 16);
-		int size = width * height;
+		_fontHeight = READ_LE_UINT16(dataSrc + offset + 16);
+		int size = width * _fontHeight;
 		decodedLength += size;
 		if (size > _maxCharSize)
 			_maxCharSize = size;
diff --git a/engines/scumm/nut_renderer.h b/engines/scumm/nut_renderer.h
index 47458b9f77..a85f99403f 100644
--- a/engines/scumm/nut_renderer.h
+++ b/engines/scumm/nut_renderer.h
@@ -41,6 +41,7 @@ protected:
 	ScummEngine *_vm;
 	int _numChars;
 	int _maxCharSize;
+	int _fontHeight;
 	byte *_charBuffer;
 	byte *_decodedData;
 	byte *_paletteMap;
diff --git a/engines/scumm/smush/smush_font.cpp b/engines/scumm/smush/smush_font.cpp
index 12722cfc34..f26727cce9 100644
--- a/engines/scumm/smush/smush_font.cpp
+++ b/engines/scumm/smush/smush_font.cpp
@@ -37,30 +37,58 @@ SmushFont::SmushFont(ScummEngine *vm, const char *filename, bool use_original_co
 	_original(use_original_colors) {
 }
 
-int SmushFont::getStringWidth(const char *str) {
+int SmushFont::getStringWidth(const char *str, uint numBytesMax) {
 	assert(str);
-
+	int maxWidth = 0;
 	int width = 0;
-	while (*str) {
-		if (*str & 0x80 && _vm->_useCJKMode) {
-			width += _vm->_2byteWidth + 1;
-			str += 2;
-		} else
-			width += getCharWidth(*str++);
+
+	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 width;
+
+	return MAX<int>(width, maxWidth);
 }
 
-int SmushFont::getStringHeight(const char *str) {
+int SmushFont::getStringHeight(const char *str, uint numBytesMax) {
 	assert(str);
-
-	int height = 0;
-	while (*str) {
-		int charHeight = getCharHeight(*str++);
-		if (height < charHeight)
-			height = charHeight;
+	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 height;
+
+	return totalHeight + (lineHeight ? lineHeight : _fontHeight) + 1;
 }
 
 int SmushFont::drawChar(byte *buffer, int dst_width, int x, int y, byte chr) {
@@ -189,7 +217,7 @@ int SmushFont::draw2byte(byte *buffer, int dst_width, int x, int y, int idx) {
 	return w + 1;
 }
 
-void SmushFont::drawSubstring(const char *str, byte *buffer, int dst_width, int x, int y) {
+void SmushFont::drawSubstring(const char *str, uint numBytesMax, byte *buffer, int dst_width, int x, int y) {
 	// This happens in the Full Throttle intro. I don't know if our
 	// text-drawing functions are buggy, or if this function is supposed
 	// to have to check for it.
@@ -197,125 +225,164 @@ void SmushFont::drawSubstring(const char *str, byte *buffer, int dst_width, int
 		x = 0;
 
 	if (_vm->_language == Common::HE_ISR) {
-		for (int i = strlen(str); i >= 0; i--) {
+		for (int i = strlen(str); i >= 0 && numBytesMax; i--) {
 			x += drawChar(buffer, dst_width, x, y, str[i]);
+			--numBytesMax;
 		}
 	} else {
-		for (int i = 0; str[i] != 0; i++) {
-			if ((byte)str[i] >= 0x80 && _vm->_useCJKMode) {
-				x += draw2byte(buffer, dst_width, x, y, (byte)str[i] + 256 * (byte)str[i+1]);
-				i++;
-			} 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_WORDS	60
+#define MAX_STRINGS		80
 
 
 void SmushFont::drawString(const char *str, byte *buffer, int dst_width, int dst_height, int x, int y, bool center) {
 	debugC(DEBUG_SMUSH, "SmushFont::drawString(%s, %d, %d, %d)", str, x, y, center);
 
-	while (str) {
-		char line[256];
-		char separators[] = "\n ";
+	int totalLen = (int)strlen(str);
+	int lineStart = 0;
 
-		if (_vm->_language == Common::ZH_TWN)
-			separators[1] = '!';
-		else
-			separators[1] = '\0';
+	// This can be found i COMI. No idea whether it is actually used. We currently don't handle these flags.
+	/*if (_vm->_game.id == GID_CMI && (flags & 0x40))
+		y -= (getStringHeight(str, totalLen) / 2);*/
 
-		const char *pos = strpbrk(str, separators);
-		if (pos) {
-			memcpy(line, str, pos - str - 1);
-			line[pos - str - 1] = 0;
-			str = pos + 1;
-		} else {
-			strcpy(line, str);
-			str = 0;
+	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 < dst_height) {
+			drawSubstring(str + lineStart, len, buffer, dst_width, center ? (x - getStringWidth(str + lineStart, len) / 2) : x, y);
+			y += height;
 		}
-		drawSubstring(line, buffer, dst_width, center ? (x - getStringWidth(line) / 2) : x, y);
-		y += getStringHeight(line);
+
+		lineStart = pos + 1;
 	}
 }
 
 void SmushFont::drawStringWrap(const char *str, byte *buffer, int dst_width, int dst_height, int x, int y, int left, int right, bool center) {
 	debugC(DEBUG_SMUSH, "SmushFont::drawStringWrap(%s, %d, %d, %d, %d, %d)", str, x, y, left, right, 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.
 	const int width = right - left;
-	Common::String s(str);
-	char *words[MAX_WORDS];
-	int word_count = 0;
-	char separators[] = " \t\r\n ";
-
-	if (_vm->_language == Common::ZH_TWN)
-		separators[4] = '!';
-	else
-		separators[4] = '\0';
-
-	Common::String::iterator tmp = s.begin();
-	while (tmp) {
-		assert(word_count < MAX_WORDS);
-		words[word_count++] = tmp;
-		tmp = strpbrk(tmp, separators);
-		if (tmp == 0)
-			break;
-		*tmp++ = 0;
-	}
-
-	int i = 0, max_width = 0, height = 0, line_count = 0;
+	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;
 
-	char *substrings[MAX_WORDS];
-	int substr_widths[MAX_WORDS];
-	const int space_width = getCharWidth(' ');
+	while (curPos < len) {
+		int textStart = curPos + 1;
+		while (str[textStart] && spaceSeparators.contains(str[textStart]))
+			++textStart;
 
-	i = 0;
-	while (i < word_count) {
-		char *substr = words[i++];
-		int substr_width = getStringWidth(substr);
+		int separatorWidth = curPos > 0 ? getStringWidth(str + curPos, textStart - curPos) : 0;
 
-		while (i < word_count) {
-			int word_width = getStringWidth(words[i]);
-			if ((substr_width + space_width + word_width) >= width)
+		int nextSeparatorPos = textStart;
+		while (!breakSeparators.contains(str[nextSeparatorPos])) {
+			if (++nextSeparatorPos == len)
 				break;
-			substr_width += word_width + space_width;
-			*(words[i]-1) = ' ';	// Convert 0 byte back to space
-			i++;
 		}
 
-		substrings[line_count] = substr;
-		substr_widths[line_count++] = substr_width;
-		if (max_width < substr_width)
-			max_width = substr_width;
-		height += getStringHeight(substr);
+		int wordWidth = getStringWidth(str + textStart, nextSeparatorPos - textStart);
+		int newWidth = curWidth + separatorWidth + wordWidth;
+
+		if (newWidth > 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[nextSeparatorPos])) {
+			// This one is only triggered by '\n' (which frequently happens in COMI/English).
+			if (numSubstrings < MAX_STRINGS) {
+				substrWidths[numSubstrings] = curWidth;
+				substrByteLength[numSubstrings] = nextSeparatorPos - substrStart[numSubstrings];
+				numSubstrings++;
+				substrStart[numSubstrings] = nextSeparatorPos + 1;
+			}
+			curWidth = 0;
+		}
+	}
+
+	if (curWidth && numSubstrings < MAX_STRINGS) {
+		substrWidths[numSubstrings] = curWidth;
+		substrByteLength[numSubstrings] = curPos - substrStart[numSubstrings];
+		numSubstrings++;
 	}
 
-	if (y > dst_height - height) {
-		y = dst_height - height;
+	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 this for DIG and COMI (English and Chinese), so I limit this fix to that. In
+	// COMI this is actually more complicated, since there seem to be more text flags which we don't
+	// support. E. g. for a flag of 0x40 we'd substract another (lastHeight / 2) from y here (without
+	// any condition). And for flag 0x100 we'd actually skip this step. No idea yet whether the flags
+	// are relevant (they can't be too relevant or someone would have implemented them, but I'll check).
+	int clipHeight = height;
+	if (_vm->_game.id == GID_DIG || (_vm->_game.id == GID_CMI))
+		clipHeight += (lastSubstrHeight / 2);
+
+	/*if (_vm->_game.id == GID_CMI && (flags & 0x40))
+		y -= (lastSubstrHeight / 2);*/
+
+	if (y > dst_height - clipHeight /*&& !(_vm->_game.id == GID_CMI && (flags & 0x100))*/)
+		y = dst_height - clipHeight;
+
 	if (center) {
-		max_width = (max_width + 1) / 2;
+		maxWidth = (maxWidth + 1) / 2;
 		x = left + width / 2;
 
-		if (x < left + max_width)
-			x = left + max_width;
-		if (x > right - max_width)
-			x = right - max_width;
-
-		for (i = 0; i < line_count; i++) {
-			drawSubstring(substrings[i], buffer, dst_width, x - substr_widths[i] / 2, y);
-			y += getStringHeight(substrings[i]);
-		}
+		if (x < left + maxWidth)
+			x = left + maxWidth;
+		if (x > right - maxWidth)
+			x = right - maxWidth;
 	} else {
-		if (x > dst_width - max_width)
-			x = dst_width - max_width;
+		if (x > dst_width - maxWidth)
+			x = dst_width - maxWidth;
+	}
 
-		for (i = 0; i < line_count; i++) {
-			drawSubstring(substrings[i], buffer, dst_width, x, y);
-			y += getStringHeight(substrings[i]);
-		}
+	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, dst_width, xpos, y);
+		y += getStringHeight(str + substrStart[i], len);
 	}
 }
 
diff --git a/engines/scumm/smush/smush_font.h b/engines/scumm/smush/smush_font.h
index e81600ca98..e731fd109d 100644
--- a/engines/scumm/smush/smush_font.h
+++ b/engines/scumm/smush/smush_font.h
@@ -35,11 +35,11 @@ protected:
 	bool _original;
 
 
-	int getStringWidth(const char *str);
-	int getStringHeight(const char *str);
+	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, byte *buffer, int dst_width, int x, int y);
+	void drawSubstring(const char *str, uint numBytesMax, byte *buffer, int dst_width, int x, int y);
 
 public:
 	SmushFont(ScummEngine *vm, const char *filename, bool use_original_colors, bool new_colors);


Commit: 651eca6abbfa92b62053ccd9ebc12bc5051f9880
    https://github.com/scummvm/scummvm/commit/651eca6abbfa92b62053ccd9ebc12bc5051f9880
Author: athrxx (athrxx at scummvm.org)
Date: 2020-10-24T17:58:28+02:00

Commit Message:
SCUMM: (SMUSH) - fix text flags

- Flag 8 is just for the subtitles. This is actually handled correctly in line 538/539.
- Flag 4 is for the wrapping. Most cases I have seen set both 4 and 8, so the visible changes are subtle.

Changed paths:
    engines/scumm/smush/smush_player.cpp


diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp
index 5b8e01ecca..570ff73998 100644
--- a/engines/scumm/smush/smush_player.cpp
+++ b/engines/scumm/smush/smush_player.cpp
@@ -630,18 +630,23 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 	}
 
 	// flags:
-	// bit 0 - center       1
-	// bit 1 - not used     2
-	// bit 2 - ???          4
-	// bit 3 - wrap around  8
-	switch (flags & 9) {
+	// 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
+	// bit 5 - outline/shadow          0x20        (apparently only set by the text renderer itself, not from the smush data)
+	// bit 6 - vertical fix (COMI)     0x40
+	// bit 7 - skip ^ codes (COMI)     0x80
+	// bit 8 - no vertical fix (COMI)  0x100
+	switch (flags & 5) {
 	case 0:
 		sf->drawString(str, _dst, _width, _height, pos_x, pos_y, false);
 		break;
 	case 1:
 		sf->drawString(str, _dst, _width, _height, pos_x, MAX(pos_y, top), true);
 		break;
-	case 8:
+	case 4:
 		// FIXME: Is 'right' the maximum line width here, just
 		// as it is in the next case? It's used several times
 		// in The Dig's intro, where 'left' and 'right' are
@@ -649,7 +654,7 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 		// handle that correctly.
 		sf->drawStringWrap(str, _dst, _width, _height, pos_x, MAX(pos_y, top), left, right, false);
 		break;
-	case 9:
+	case 5:
 		// In this case, the 'right' parameter is actually the
 		// maximum line width. This explains why it's sometimes
 		// smaller than 'left'.


Commit: c4d3cc6c185a0d49b0324e116da900cbe0c674d5
    https://github.com/scummvm/scummvm/commit/c4d3cc6c185a0d49b0324e116da900cbe0c674d5
Author: athrxx (athrxx at scummvm.org)
Date: 2020-10-24T17:58:28+02:00

Commit Message:
SCUMM: (DIG/CJK) - fix regression in text positioning

CJK text in DIG was 2 pixels off vertically due to 4b13c33b.
COMI actually has a y-offset of 2 (not 7) for CJK fonts, but it is applied at a different location.

Changed paths:
    engines/scumm/smush/smush_font.cpp


diff --git a/engines/scumm/smush/smush_font.cpp b/engines/scumm/smush/smush_font.cpp
index f26727cce9..5c94f708f2 100644
--- a/engines/scumm/smush/smush_font.cpp
+++ b/engines/scumm/smush/smush_font.cpp
@@ -193,8 +193,7 @@ int SmushFont::draw2byte(byte *buffer, int dst_width, int x, int y, int idx) {
 		byte drawColor = shadowOffsetColorTable[shadowIdx];
 
 		src = origSrc;
-
-		byte *dst = buffer + dst_width * (offY + (_vm->_game.id == GID_CMI ? 7 : (_vm->_game.id == GID_DIG ? 2 : 0))) + offX;
+		byte *dst = buffer + dst_width * offY + offX;
 
 		for (int j = 0; j < h; j++) {
 			for (int i = 0; i < w; i++) {
@@ -252,9 +251,14 @@ void SmushFont::drawString(const char *str, byte *buffer, int dst_width, int dst
 	int totalLen = (int)strlen(str);
 	int lineStart = 0;
 
-	// This can be found i COMI. No idea whether it is actually used. We currently don't handle these flags.
-	/*if (_vm->_game.id == GID_CMI && (flags & 0x40))
-		y -= (getStringHeight(str, totalLen) / 2);*/
+	// 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')
@@ -298,6 +302,10 @@ void SmushFont::drawStringWrap(const char *str, byte *buffer, int dst_width, int
 	int curWidth = 0;
 	int curPos = -1;
 
+	// COMI does this for CJK strings (before any other possible yPos fixes, see lines 348 - 356).
+	if (_vm->_game.id == GID_CMI && _vm->_useCJKMode)
+		y += 2;
+
 	while (curPos < len) {
 		int textStart = curPos + 1;
 		while (str[textStart] && spaceSeparators.contains(str[textStart]))


Commit: c5216c63677c930f346ace026b1d82c7ce9395a0
    https://github.com/scummvm/scummvm/commit/c5216c63677c930f346ace026b1d82c7ce9395a0
Author: athrxx (athrxx at scummvm.org)
Date: 2020-10-24T17:58:29+02:00

Commit Message:
SCUMM: (DIG/CJK) - add specific text positioning fix from disasm

This is basically a hack, but the original does it just like that. It ensures that certain CJK message strings at the bottom of the screen won't get cut off.

Changed paths:
    engines/scumm/string.cpp


diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 7d7953c29f..2cdda4c4c2 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -129,6 +129,14 @@ void ScummEngine_v6::enqueueText(const byte *text, int x, int y, byte color, byt
 	BlastText &bt = _blastTextQueue[_blastTextQueuePos++];
 	assert(_blastTextQueuePos <= 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. COMI doesn't have anything like that.
+		if (_game.id == GID_DIG && x == 160 && y == 189 && charset == 3)
+			y -= 6;
+	}
+
 	convertMessageToString(text, bt.text, sizeof(bt.text));
 	bt.xpos = x;
 	bt.ypos = y;


Commit: e665730d3d2dee6f09f94a6dbcc6585efa47127d
    https://github.com/scummvm/scummvm/commit/e665730d3d2dee6f09f94a6dbcc6585efa47127d
Author: athrxx (athrxx at scummvm.org)
Date: 2020-10-24T17:58:29+02:00

Commit Message:
SCUMM: (DIG/CJK) - fix bug in charset rendering

This fixes the bug shown in the fourth row of screenshots in ticket no. 11656 (DIG Asteroid scene). That bug could basically appear in any other scene which uses the xstart offset.

Changed paths:
    engines/scumm/charset.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 96699bba45..9a3d21bc93 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -868,7 +868,7 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr,
 		}
 
 		if (is2byte && _vm->_game.platform != Common::kPlatformFMTowns)
-			drawBits1(dstSurface, _left, drawTop, charPtr, drawTop, origWidth, origHeight);
+			drawBits1(dstSurface, _left + vs->xstart, drawTop, charPtr, drawTop, origWidth, origHeight);
 		else
 			drawBitsN(dstSurface, dstPtr, charPtr, *_fontPtr, drawTop, origWidth, origHeight);
 


Commit: 1266fdeb0da432fac44bdd9d9963bfdcf88d8fac
    https://github.com/scummvm/scummvm/commit/1266fdeb0da432fac44bdd9d9963bfdcf88d8fac
Author: athrxx (athrxx at scummvm.org)
Date: 2020-10-24T17:58:29+02:00

Commit Message:
SCUMM: (COMI/CJK) - fix smush font glyph shadows

I have confirmed that this type of shadow (one shadow pixel to the left, one to the right and one to the bottom) is drawn for all CJK characters in COMI.

Changed paths:
    engines/scumm/smush/smush_font.cpp


diff --git a/engines/scumm/smush/smush_font.cpp b/engines/scumm/smush/smush_font.cpp
index 5c94f708f2..5e46699c51 100644
--- a/engines/scumm/smush/smush_font.cpp
+++ b/engines/scumm/smush/smush_font.cpp
@@ -159,35 +159,17 @@ int SmushFont::draw2byte(byte *buffer, int dst_width, int x, int y, int idx) {
 		kNone,
 		kNormalShadowMode,
 		kCJKv7ShadowMode,
-		kKoreanV8ShadowMode
+		kCJKv8ShadowMode
 	};
 
-	ShadowMode shadowMode = kNone;
-
-	if (_vm->_useCJKMode) {
-		// TODO: Check Chinese and Japanese COMI
-		// For now the kKoreanV8ShadowMode is limited to Korean, because it isn't known yet
-		// how Chinese and Japanese COMI is supposed to look like (I suspect that this gets
-		// rendered the same way, just as it is done in DIG).
-		if (_vm->_game.version == 8 && _vm->_language == Common::KO_KOR)
-			shadowMode = kKoreanV8ShadowMode;
-		else if (_vm->_game.version != 8)
-			shadowMode = kCJKv7ShadowMode;
-	}
+	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 };
 
-	int shadowIdx = 3;
-	if (shadowMode == kKoreanV8ShadowMode)
-		shadowIdx = 0;
-	else if (shadowMode == kCJKv7ShadowMode)
-		shadowIdx = 2;
-
 	const byte *origSrc = src;
-
-	for (; shadowIdx < 4; shadowIdx++) {
+	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];


Commit: 4a7b720a31e60b71bad38630afc9a0089df07acb
    https://github.com/scummvm/scummvm/commit/4a7b720a31e60b71bad38630afc9a0089df07acb
Author: athrxx (athrxx at scummvm.org)
Date: 2020-10-24T17:58:29+02:00

Commit Message:
SCUMM: whitespace

Changed paths:
    engines/scumm/actor.cpp
    engines/scumm/charset.cpp
    engines/scumm/gfx.cpp
    engines/scumm/imuse/drivers/amiga.cpp
    engines/scumm/imuse/drivers/amiga.h
    engines/scumm/imuse/imuse.cpp
    engines/scumm/imuse/imuse_part.cpp
    engines/scumm/object.cpp


diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index 18b3251fbb..cb951d5071 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -755,9 +755,9 @@ void Actor::startWalkActor(int destX, int destY, int dir) {
 
 	} else if (_vm->_game.version <= 2) {
 		_moving = (_moving & ~(MF_LAST_LEG | MF_IN_LEG)) | MF_NEW_LEG;
- 	} else {
- 		_moving = (_moving & MF_IN_LEG) | MF_NEW_LEG;
- 	}
+	} else {
+		_moving = (_moving & MF_IN_LEG) | MF_NEW_LEG;
+	}
 
 }
 
@@ -1071,8 +1071,8 @@ UpdateActorDirection:;
 				directionUpdate();
 				animateActor(newDirToOldDir(_facing));
 
-				// FIXME: During the hands-free-demo in the library (room 5), Purple Tentacle gets stuck following Sandy due to the corner of the stairs, 
-				//        This is due to distance, and walkbox gap/layout. This works fine with the original engine, because it 'brute forces' 
+				// FIXME: During the hands-free-demo in the library (room 5), Purple Tentacle gets stuck following Sandy due to the corner of the stairs,
+				//        This is due to distance, and walkbox gap/layout. This works fine with the original engine, because it 'brute forces'
 				//        another pixel move in the walk direction before giving up, allowing us to move enough pixels to hit the next walkbox.
 				//        Why this fails with the return is because script-10 is executing a 'walkActorToActor' every cycle, which restarts the movement process
 				//        As a work around, we implement the original engine behaviour only for Purple Tentacle in the Demo. Doing this for other actors
diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 9a3d21bc93..5869cff2e2 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -335,7 +335,7 @@ int CharsetRenderer::getStringWidth(int arg, const byte *text) {
 			continue;
 		else if (chr == '\n' || chr == '\r' || chr == _vm->_newLineCharacter)
 			break;
-			
+
 		if (_vm->_game.heversion >= 72) {
 			if (chr == code) {
 				chr = text[pos++];
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 35d6a1e276..a8478aacce 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -1023,7 +1023,7 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) {
 	VirtScreen *vs;
 	byte *screenBuf;
 
- 	if (rect.top < 0)
+	if (rect.top < 0)
 		rect.top = 0;
 	if (rect.left >= rect.right || rect.top >= rect.bottom)
 		return;
diff --git a/engines/scumm/imuse/drivers/amiga.cpp b/engines/scumm/imuse/drivers/amiga.cpp
index 9e819939c6..cd6039ba88 100644
--- a/engines/scumm/imuse/drivers/amiga.cpp
+++ b/engines/scumm/imuse/drivers/amiga.cpp
@@ -175,7 +175,7 @@ private:
 	IMuseDriver_Amiga *_driver;
 };
 
-SoundChannel_Amiga::SoundChannel_Amiga(IMuseDriver_Amiga *driver, int id, Instrument_Amiga *instruments) : _driver(driver), _id(id), _instruments(instruments), 
+SoundChannel_Amiga::SoundChannel_Amiga(IMuseDriver_Amiga *driver, int id, Instrument_Amiga *instruments) : _driver(driver), _id(id), _instruments(instruments),
 	_assign(0), _next(0), _prev(0), _sustain(false), _note(0) {
 	assert(id > -1 && id < 4);
 	_channels[id] = this;
@@ -194,7 +194,7 @@ SoundChannel_Amiga::~SoundChannel_Amiga() {
 	delete[] _volTable;
 	_volTable = 0;
 }
- 
+
 SoundChannel_Amiga *SoundChannel_Amiga::allocate(int prio) {
 	SoundChannel_Amiga *res = 0;
 
@@ -276,10 +276,10 @@ void SoundChannel_Amiga::noteOn(byte note, byte volume, byte program, int8 trans
 	_driver->disableChannel(_id);
 	setVelocity(0, 0);
 	setVolume(volume);
-	
+
 	if (s->type > 1)
 		return;
-	
+
 	uint16 period = calculatePeriod(pitchBend + ((_note + transpose) << 7), s->baseNote, s->rate);
 
 	if (s->type == 1) {
@@ -412,7 +412,7 @@ void SoundChannel_Amiga::setVelocity(uint8 velo, int delay) {
 	} else {
 		_driver->setChannelVolume(_id, _volTable[(_ioUnit.volume << 5) + velo]);
 		_ioUnit.currentLevel = _ioUnit.fadeTargetLevel = velo;
-		_ioUnit.fadeLevelMod = 0;		
+		_ioUnit.fadeLevelMod = 0;
 	}
 }
 
@@ -751,7 +751,7 @@ void IMuseDriver_Amiga::interrupt() {
 	if (!_isOpen)
 		return;
 
-	for (_ticker += _internalTempo; _ticker >= _baseTempo; _ticker -= _baseTempo) {	
+	for (_ticker += _internalTempo; _ticker >= _baseTempo; _ticker -= _baseTempo) {
 		updateParser();
 		updateSounds();
 	}
@@ -831,12 +831,12 @@ void IMuseDriver_Amiga::loadInstrument(int program) {
 
 		if (size <= 0)
 			break;
-			
+
 		size -= 38;
 		Instrument_Amiga::Samples *s = &_instruments[program].samples[block];
 		ims.seek(594 + offset + header[block], SEEK_SET);
 		int8 *buf = new int8[size];
-		
+
 		s->rate = ims.readUint16BE();
 		s->baseNote = ims.readUint16BE();
 		s->noteRangeMin = ims.readSint16BE();
diff --git a/engines/scumm/imuse/drivers/amiga.h b/engines/scumm/imuse/drivers/amiga.h
index 29dc5d4e07..f76a27178d 100644
--- a/engines/scumm/imuse/drivers/amiga.h
+++ b/engines/scumm/imuse/drivers/amiga.h
@@ -68,7 +68,7 @@ private:
 
 	Audio::Mixer *_mixer;
 	Audio::SoundHandle _soundHandle;
-	
+
 	int32 _ticker;
 	bool _isOpen;
 
diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp
index af91a77c23..61d24b67f7 100644
--- a/engines/scumm/imuse/imuse.cpp
+++ b/engines/scumm/imuse/imuse.cpp
@@ -697,7 +697,7 @@ bool IMuseInternal::startSound_internal(int sound, int offset) {
 	//
 	// Tunes involved
 	// 100 - Captain Dread's map
-	// 113 - Guard Kiosk / Mardi Grass 
+	// 113 - Guard Kiosk / Mardi Grass
 	// 115 - Map of Booty Island / Ville de la Booty
 	// 118 - Ville de la Booty
 	//
diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp
index 1614076ee0..1495e869a7 100644
--- a/engines/scumm/imuse/imuse_part.cpp
+++ b/engines/scumm/imuse/imuse_part.cpp
@@ -145,7 +145,7 @@ void Part::set_pan(int8 pan) {
 
 void Part::set_transpose(int8 transpose) {
 	_transpose = transpose;
-	
+
 	if (_se->_isAmiga) {
 		// The Amiga version does a check like this. While this is probably a bug (a signed int8 can never be 128),
 		// the playback depends on this being implemented exactly like in the original driver. I found this bug with
@@ -155,7 +155,7 @@ void Part::set_transpose(int8 transpose) {
 	} else {
 		_transpose_eff = (_transpose == -128) ? 0 : transpose_clamp(_transpose + _player->getTranspose(), -24, 24);
 		sendPitchBend();
-	}	
+	}
 }
 
 void Part::sustain(bool value) {
@@ -368,9 +368,9 @@ void Part::sendPitchBend() {
 	// so we'll do the scaling ourselves.
 	if (_player->_se->isNativeMT32())
 		bend = bend * _pitchbend_factor / 12;
-	
+
 	// We send the transpose value separately for Amiga (which is more like the original handles this).
-	// Some rhythm instruments depend on this. 
+	// Some rhythm instruments depend on this.
 	int8 transpose = _se->_isAmiga ? 0 : _transpose_eff;
 	_mc->pitchBend(clamp(bend + (_detune_eff * 64 / 12) + (transpose * 8192 / 12), -8192, 8191));
 }
@@ -384,7 +384,7 @@ void Part::sendTranspose() {
 	// such functions.
 	if (!_se->_isAmiga)
 		return;
-	
+
 	_mc->transpose(_transpose_eff);
 }
 
diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp
index 1581eb32a9..b373a0a9f4 100644
--- a/engines/scumm/object.cpp
+++ b/engines/scumm/object.cpp
@@ -113,7 +113,7 @@ void ScummEngine::setOwnerOf(int obj, int owner) {
 		return;
 
 	// WORKAROUND for bug #6802: assert() was triggered in freddi2.
- 	// Bug is in room 39. Problem is script 10, in the localvar2==78 case;
+	// Bug is in room 39. Problem is script 10, in the localvar2==78 case;
 	// this only sets the obj id if var198 is non-zero, but in the asserting
 	// case, it is obj 0. That means two setOwnerOf calls are made with obj 0.
 	// The correct setOwnerOf calls are made afterwards, so just ignoring this


Commit: 7b3b47024ba9de9e29247a6d9427c2d4375717cb
    https://github.com/scummvm/scummvm/commit/7b3b47024ba9de9e29247a6d9427c2d4375717cb
Author: athrxx (athrxx at scummvm.org)
Date: 2020-10-24T17:58:29+02:00

Commit Message:
SCUMM: (COMI/CJK) - fix blast text shading and positioning

- COMI adds a y-Offset of 2 in CJK mode.
- The shadowed glyphs are used for all CJK font drawing, not only Korean. Also, the char height has to be adjusted by one pixel for the shadow.

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


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 5869cff2e2..d914d7dedb 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1280,8 +1280,11 @@ void CharsetRendererNut::printChar(int chr, bool ignoreCharsetMask) {
 	int height = _current->getCharHeight(chr);
 
 	bool is2byte = chr >= 256 && _vm->_useCJKMode;
-	if (is2byte)
+	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;
diff --git a/engines/scumm/nut_renderer.cpp b/engines/scumm/nut_renderer.cpp
index 3a53f2726e..9f6954ab84 100644
--- a/engines/scumm/nut_renderer.cpp
+++ b/engines/scumm/nut_renderer.cpp
@@ -405,24 +405,10 @@ void NutRenderer::draw2byte(const Graphics::Surface &s, int c, int x, int y, byt
 		return;
 	}
 
-	enum ShadowMode {
-		kNone,
-		kKoreanV8ShadowMode
-	};
-
-	ShadowMode shadowMode = kNone;
-
-	if (_vm->_language == Common::KO_KOR && _vm->_game.version == 8) {
-		shadowMode = kKoreanV8ShadowMode;
-	}
-
 	int shadowOffsetXTable[4] = {-1, 0, 1, 0};
 	int shadowOffsetYTable[4] = {0, 1, 0, 0};
 	int shadowOffsetColorTable[4] = {0, 0, 0, color};
-
-	int shadowIdx = 3;
-	if (shadowMode == kKoreanV8ShadowMode)
-		shadowIdx = 0;
+	int shadowIdx = (_vm->_useCJKMode && _vm->_game.id == GID_CMI) ? 0 : 3;
 
 	const byte *origSrc = src;
 
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 2cdda4c4c2..a92867aa4e 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -132,9 +132,12 @@ void ScummEngine_v6::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
-		// process. COMI doesn't have anything like that.
+		// 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;
 	}
 
 	convertMessageToString(text, bt.text, sizeof(bt.text));


Commit: 30f4fd049ee662eb4db7e57a66fe7198354f0578
    https://github.com/scummvm/scummvm/commit/30f4fd049ee662eb4db7e57a66fe7198354f0578
Author: athrxx (athrxx at scummvm.org)
Date: 2020-10-24T17:58:29+02:00

Commit Message:
SCUMM: (SMUSH) - fix text coordinates and wrapping

Some fixes from disasm to have the text look pixel-exact like the original.

Some lines in COMI are still off a bit by 2 pixels to the left or to the right. Whilst I doubt that anyone would notice it I'll try to fix that, too.

Changed paths:
    engines/scumm/insane/insane.cpp
    engines/scumm/smush/smush_font.cpp
    engines/scumm/smush/smush_font.h
    engines/scumm/smush/smush_player.cpp


diff --git a/engines/scumm/insane/insane.cpp b/engines/scumm/insane/insane.cpp
index c0ba9ea6c8..bcbf1331da 100644
--- a/engines/scumm/insane/insane.cpp
+++ b/engines/scumm/insane/insane.cpp
@@ -1262,10 +1262,8 @@ void Insane::smlayer_showStatusMsg(int32 arg_0, byte *renderBitmap, int32 codecp
 					   int32 flags, const char *formatString, const char *strng) {
 	SmushFont *sf = _player->getFont(0);
 	int color = 1;
-	int32 top = 0;
 	char *str = NULL, *string;
 	int len = strlen(formatString) + strlen(strng) + 16;
-
 	string = (char *)malloc(len);
 	str = string;
 
@@ -1299,23 +1297,20 @@ void Insane::smlayer_showStatusMsg(int32 arg_0, byte *renderBitmap, int32 codecp
 	sf->setColor(color);
 
 	// flags:
-	// bit 0 - center       1
-	// bit 1 - not used     2
-	// bit 2 - ???          4
-	// bit 3 - wrap around  8
-	switch (flags) {
-	case 0:
-		sf->drawString(str, renderBitmap, _player->_width, _player->_height, pos_x, pos_y, false);
-		break;
-	case 1:
-		sf->drawString(str, renderBitmap, _player->_width, _player->_height, pos_x, MAX(pos_y, top), true);
-		break;
-	case 5:
-		sf->drawStringWrap(str, renderBitmap, _player->_width, _player->_height, pos_x, pos_y, 10, 300, true);
-		break;
-	default:
-		error("Insane::smlayer_showStatusMsg. Not handled flags: %d", 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
+	if (flags & 4) {
+		Common::Rect clipRect(0, 0, _player->_width, _player->_height);
+		sf->drawStringWrap(str, renderBitmap, clipRect, pos_x, pos_y, flags & 1);
+	} else {
+		Common::Rect clipRect(10, 0, 310, _player->_height);
+		sf->drawString(str, renderBitmap, clipRect, pos_x, pos_y, flags & 1);
 	}
+
+
 	free (string);
 }
 
diff --git a/engines/scumm/smush/smush_font.cpp b/engines/scumm/smush/smush_font.cpp
index 5e46699c51..f63e8b66ae 100644
--- a/engines/scumm/smush/smush_font.cpp
+++ b/engines/scumm/smush/smush_font.cpp
@@ -77,7 +77,7 @@ int SmushFont::getStringHeight(const char *str, uint numBytesMax) {
 		if (*str == '\n') {
 			totalHeight += (lineHeight ? lineHeight : _fontHeight) + 1;
 			lineHeight = 0;
-		} else if (*str != '\r' || *str != _vm->_newLineCharacter) {
+		} else if (*str != '\r' && *str != _vm->_newLineCharacter) {
 			lineHeight = MAX<int>(lineHeight, getCharHeight(*str));
 			if (is2ByteCharacter(_vm->_language, *str)) {
 				++str;
@@ -227,8 +227,8 @@ void SmushFont::drawSubstring(const char *str, uint numBytesMax, byte *buffer, i
 #define MAX_STRINGS		80
 
 
-void SmushFont::drawString(const char *str, byte *buffer, int dst_width, int dst_height, int x, int y, bool center) {
-	debugC(DEBUG_SMUSH, "SmushFont::drawString(%s, %d, %d, %d)", str, x, y, center);
+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;
@@ -248,8 +248,8 @@ void SmushFont::drawString(const char *str, byte *buffer, int dst_width, int dst
 
 		int len = pos - lineStart;
 		int height = getStringHeight(str + lineStart, len);
-		if (y < dst_height) {
-			drawSubstring(str + lineStart, len, buffer, dst_width, center ? (x - getStringWidth(str + lineStart, len) / 2) : x, y);
+		if (y < clipRect.bottom) {
+			drawSubstring(str + lineStart, len, buffer, _vm->_screenWidth, center ? (x - getStringWidth(str + lineStart, len) / 2) : x, y);
 			y += height;
 		}
 
@@ -257,15 +257,14 @@ void SmushFont::drawString(const char *str, byte *buffer, int dst_width, int dst
 	}
 }
 
-void SmushFont::drawStringWrap(const char *str, byte *buffer, int dst_width, int dst_height, int x, int y, int left, int right, bool center) {
-	debugC(DEBUG_SMUSH, "SmushFont::drawStringWrap(%s, %d, %d, %d, %d, %d)", str, x, y, left, right, center);
-
+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.
-	const int width = right - left;
+
 	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));
@@ -284,7 +283,7 @@ void SmushFont::drawStringWrap(const char *str, byte *buffer, int dst_width, int
 	int curWidth = 0;
 	int curPos = -1;
 
-	// COMI does this for CJK strings (before any other possible yPos fixes, see lines 348 - 356).
+	// 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;
 
@@ -304,7 +303,7 @@ void SmushFont::drawStringWrap(const char *str, byte *buffer, int dst_width, int
 		int wordWidth = getStringWidth(str + textStart, nextSeparatorPos - textStart);
 		int newWidth = curWidth + separatorWidth + wordWidth;
 
-		if (newWidth > width) {
+		if (curWidth && newWidth > clipRect.width()) {
 			if (numSubstrings < MAX_STRINGS) {
 				substrWidths[numSubstrings] = curWidth;
 				substrByteLength[numSubstrings] = curPos - substrStart[numSubstrings];
@@ -316,13 +315,13 @@ void SmushFont::drawStringWrap(const char *str, byte *buffer, int dst_width, int
 		curWidth = newWidth;
 
 		curPos = nextSeparatorPos;
-		if (!spaceSeparators.contains(str[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] = nextSeparatorPos - substrStart[numSubstrings];
+				substrByteLength[numSubstrings] = curPos - substrStart[numSubstrings];
 				numSubstrings++;
-				substrStart[numSubstrings] = nextSeparatorPos + 1;
+				substrStart[numSubstrings] = curPos + 1;
 			}
 			curWidth = 0;
 		}
@@ -340,38 +339,34 @@ void SmushFont::drawStringWrap(const char *str, byte *buffer, int dst_width, int
 		height += lastSubstrHeight;
 	}
 
-	// I have verified this for DIG and COMI (English and Chinese), so I limit this fix to that. In
-	// COMI this is actually more complicated, since there seem to be more text flags which we don't
-	// support. E. g. for a flag of 0x40 we'd substract another (lastHeight / 2) from y here (without
-	// any condition). And for flag 0x100 we'd actually skip this step. No idea yet whether the flags
-	// are relevant (they can't be too relevant or someone would have implemented them, but I'll check).
-	int clipHeight = height;
-	if (_vm->_game.id == GID_DIG || (_vm->_game.id == GID_CMI))
-		clipHeight += (lastSubstrHeight / 2);
+	// 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 > dst_height - clipHeight /*&& !(_vm->_game.id == GID_CMI && (flags & 0x100))*/)
-		y = dst_height - clipHeight;
+	if (y > clipRect.bottom - clipHeight /*&& !(_vm->_game.id == GID_CMI && (flags & 0x100))*/)
+		y = clipRect.bottom - clipHeight;
 
-	if (center) {
-		maxWidth = (maxWidth + 1) / 2;
-		x = left + width / 2;
+	if (y < clipRect.top)
+		y = clipRect.top;
 
-		if (x < left + maxWidth)
-			x = left + maxWidth;
-		if (x > right - maxWidth)
-			x = right - maxWidth;
+	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 > dst_width - maxWidth)
-			x = dst_width - maxWidth;
+		if (x > clipRect.right - maxWidth)
+			x = clipRect.right - maxWidth;
 	}
 
 	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, dst_width, xpos, y);
+		drawSubstring(str + substrStart[i], len, buffer, _vm->_screenWidth, xpos, y);
 		y += getStringHeight(str + substrStart[i], len);
 	}
 }
diff --git a/engines/scumm/smush/smush_font.h b/engines/scumm/smush/smush_font.h
index e731fd109d..c870424ec2 100644
--- a/engines/scumm/smush/smush_font.h
+++ b/engines/scumm/smush/smush_font.h
@@ -45,8 +45,8 @@ 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, int dst_width, int dst_height, int x, int y, bool center);
-	void drawStringWrap(const char *str, byte *buffer, int dst_width, int dst_height, int x, int y, int left, int right, bool center);
+	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)
diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp
index 570ff73998..2858066687 100644
--- a/engines/scumm/smush/smush_player.cpp
+++ b/engines/scumm/smush/smush_player.cpp
@@ -24,6 +24,7 @@
 #include "common/file.h"
 #include "common/system.h"
 #include "common/util.h"
+#include "common/rect.h"
 
 #include "audio/mixer.h"
 
@@ -513,8 +514,8 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 	int flags = b.readSint16LE();
 	int left = b.readSint16LE();
 	int top = b.readSint16LE();
-	int right = b.readSint16LE();
-	/*int32 height =*/ b.readSint16LE();
+	int width = b.readSint16LE();
+	int height = b.readSint16LE();
 	/*int32 unk2 =*/ b.readUint16LE();
 
 	const char *str;
@@ -561,18 +562,18 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 	while (str[0] == '^') {
 		switch (str[1]) {
 		case 'f':
-			{
-				int id = str[3] - '0';
-				str += 4;
-				sf = getFont(id);
-			}
-			break;
+		{
+			int id = str[3] - '0';
+			str += 4;
+			sf = getFont(id);
+		}
+		break;
 		case 'c':
-			{
-				color = str[4] - '0' + 10 *(str[3] - '0');
-				str += 5;
-			}
-			break;
+		{
+			color = str[4] - '0' + 10 *(str[3] - '0');
+			str += 5;
+		}
+		break;
 		default:
 			error("invalid escape code in text string");
 		}
@@ -636,35 +637,27 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
 	// bit 3 - switchable              0x08
 	// bit 4 - fill background         0x10
 	// bit 5 - outline/shadow          0x20        (apparently only set by the text renderer itself, not from the smush data)
-	// bit 6 - vertical fix (COMI)     0x40
-	// bit 7 - skip ^ codes (COMI)     0x80
-	// bit 8 - no vertical fix (COMI)  0x100
-	switch (flags & 5) {
-	case 0:
-		sf->drawString(str, _dst, _width, _height, pos_x, pos_y, false);
-		break;
-	case 1:
-		sf->drawString(str, _dst, _width, _height, pos_x, MAX(pos_y, top), true);
-		break;
-	case 4:
-		// FIXME: Is 'right' the maximum line width here, just
-		// as it is in the next case? It's used several times
-		// in The Dig's intro, where 'left' and 'right' are
-		// always 0 and 321 respectively, and apparently we
-		// handle that correctly.
-		sf->drawStringWrap(str, _dst, _width, _height, pos_x, MAX(pos_y, top), left, right, false);
-		break;
-	case 5:
-		// In this case, the 'right' parameter is actually the
-		// maximum line width. This explains why it's sometimes
-		// smaller than 'left'.
-		//
-		// Note that in The Dig's "Spacetime Six" movie it's
-		// 621. I have no idea what that means.
-		sf->drawStringWrap(str, _dst, _width, _height, pos_x, MAX(pos_y, top), left, MIN(left + right, _width), true);
-		break;
-	default:
-		error("SmushPlayer::handleTextResource. Not handled flags: %d", flags);
+	// 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) {
+		// 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
+		// whether this is on purpose or not (the text looks not necessarily better or worse, just different), so we follow the original...
+		if (_vm->_game.id == GID_CMI) {
+			left = top = 10;
+			width = _width - 20;
+			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);
+	} 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);
 	}
 
 	free(string);




More information about the Scummvm-git-logs mailing list