Index: scumm/actor.cpp
===================================================================
--- scumm/actor.cpp	(revision 26291)
+++ scumm/actor.cpp	(working copy)
@@ -1850,6 +1850,7 @@
 #ifndef DISABLE_SCUMM_7_8
 void ScummEngine_v7::actorTalk(const byte *msg) {
 	Actor *a;
+	bool stringWrap;
 
 	convertMessageToString(msg, _charsetBuffer, sizeof(_charsetBuffer));
 
@@ -1882,9 +1883,15 @@
 	if (_game.version == 7)
 		VAR(VAR_HAVE_MSG) = 0xFF;
 	_haveActorSpeechMsg = true;
+	if (_game.version == 8) {
+		stringWrap = _string[0].wrapping;
+		_string[0].wrapping = true;
+	}
 	CHARSET_1();
-	if (_game.version == 8)
+	if (_game.version == 8) {
 		VAR(VAR_HAVE_MSG) = (_string[0].no_talk_anim) ? 2 : 1;
+		_string[0].wrapping = stringWrap;
+	}
 }
 #endif
 
Index: scumm/charset.h
===================================================================
--- scumm/charset.h	(revision 26291)
+++ scumm/charset.h	(working copy)
@@ -72,7 +72,8 @@
 	virtual void printChar(int chr, bool ignoreCharsetMask) = 0;
 	virtual void drawChar(int chr, const Graphics::Surface &s, int x, int y) {}
 
-	int getStringWidth(int a, const byte *str);
+	int getStringWidth(int a, const byte *text);
+	int getStringHeight(const byte *text);
 	void addLinebreaks(int a, byte *str, int pos, int maxwidth);
 	void translateColor();
 
Index: scumm/string.cpp
===================================================================
--- scumm/string.cpp	(revision 26291)
+++ scumm/string.cpp	(working copy)
@@ -409,8 +409,9 @@
 
 void ScummEngine::CHARSET_1() {
 	Actor *a;
+	StringTab saveStr;
 #ifndef DISABLE_SCUMM_7_8
-	byte subtitleBuffer[200];
+	byte subtitleBuffer[2048];
 	byte *subtitleLine = subtitleBuffer;
 	Common::Point subtitlePos;
 
@@ -437,45 +438,60 @@
 	if (getTalkingActor() != 0xFF)
 		a = derefActorSafe(getTalkingActor(), "CHARSET_1");
 
-	if (a && _string[0].overhead != 0) {
-		int s;
+	if (_game.version == 8) {
+		saveStr = _string[0];
+		if (a && _string[0].overhead) {
+			int s;
 
-		_string[0].xpos = a->getPos().x - virtscr[0].xstart;
-		_string[0].ypos = a->getPos().y - a->getElevation() - _screenTop;
+			_string[0].xpos = a->getPos().x - virtscr[0].xstart;
+			s = a->_scalex * a->_talkPosX / 255;
+			_string[0].xpos += (a->_talkPosX - s) / 2 + s;
 
-		if (_game.version <= 5) {
+			_string[0].ypos = a->getPos().y - a->getElevation() - _screenTop;
+			s = a->_scaley * a->_talkPosY / 255;
+			_string[0].ypos += (a->_talkPosY - s) / 2 + s;
+		}
+	} else {
+		if (a && _string[0].overhead) {
+			int s;
 
-			if (VAR(VAR_V5_TALK_STRING_Y) < 0) {
-				s = (a->_scaley * (int)VAR(VAR_V5_TALK_STRING_Y)) / 0xFF;
-				_string[0].ypos += (int)(((VAR(VAR_V5_TALK_STRING_Y) - s) / 2) + s);
+			_string[0].xpos = a->getPos().x - virtscr[0].xstart;
+			_string[0].ypos = a->getPos().y - a->getElevation() - _screenTop;
+
+			if (_game.version <= 5) {
+
+				if (VAR(VAR_V5_TALK_STRING_Y) < 0) {
+					s = (a->_scaley * (int)VAR(VAR_V5_TALK_STRING_Y)) / 0xFF;
+					_string[0].ypos += (int)(((VAR(VAR_V5_TALK_STRING_Y) - s) / 2) + s);
+				} else {
+					_string[0].ypos = (int)VAR(VAR_V5_TALK_STRING_Y);
+				}
+
 			} else {
-				_string[0].ypos = (int)VAR(VAR_V5_TALK_STRING_Y);
+				s = a->_scalex * a->_talkPosX / 0xFF;
+				_string[0].xpos += ((a->_talkPosX - s) / 2) + s;
+
+				s = a->_scaley * a->_talkPosY / 0xFF;
+				_string[0].ypos += ((a->_talkPosY - s) / 2) + s;
+
+				if (_string[0].ypos > _screenHeight - 40)
+					_string[0].ypos = _screenHeight - 40;
 			}
 
-		} else {
-			s = a->_scalex * a->_talkPosX / 0xFF;
-			_string[0].xpos += ((a->_talkPosX - s) / 2) + s;
+			if (_string[0].ypos < 1)
+				_string[0].ypos = 1;
 
-			s = a->_scaley * a->_talkPosY / 0xFF;
-			_string[0].ypos += ((a->_talkPosY - s) / 2) + s;
-
-			if (_string[0].ypos > _screenHeight - 40)
-				_string[0].ypos = _screenHeight - 40;
+			if (_string[0].xpos < 80)
+				_string[0].xpos = 80;
+			if (_string[0].xpos > _screenWidth - 80)
+				_string[0].xpos = _screenWidth - 80;
 		}
 
-		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->_top = _string[0].ypos + _screenTop;
+		_charset->_startLeft = _charset->_left = _string[0].xpos;
+		_charset->_right = _string[0].right;
+		_charset->_center = _string[0].center;
 	}
-
-	_charset->_top = _string[0].ypos + _screenTop;
-	_charset->_startLeft = _charset->_left = _string[0].xpos;
-	_charset->_right = _string[0].right;
-	_charset->_center = _string[0].center;
 	_charset->setColor(_charsetColor);
 
 	if (a && a->_charset)
@@ -522,6 +538,108 @@
 		}
 	}
 
+	_charset->_disableOffsX = _charset->_firstChar = !_keepText;
+
+	// XXX turn CHARSET_1 into a virtual method and overload it for V8
+	// XXX add StringTab.wrapping to the saveload data
+	if (_game.version == 8) {
+		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) {
+					((ScummEngine_v7 *)this)->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) {
+						((ScummEngine_v7 *)this)->addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID());
+						subtitlePos.y += _charset->getFontHeight();
+					}
+					subtitleLine = subtitleBuffer;
+				} else {
+					*subtitleLine++ = code;
+				}
+				*subtitleLine = '\0';
+				if (code == 0) {
+					break;
+				}
+			}
+		}
+		_haveMsg = 2;
+		_keepText = false;
+		_string[0] = saveStr;
+		return;
+	}
+
 	if (_game.version > 3) {
 		int maxwidth = _charset->_right - _string[0].xpos - 1;
 		if (_charset->_center) {
@@ -539,8 +657,6 @@
 			_nextLeft = 0;
 	}
 
-	_charset->_disableOffsX = _charset->_firstChar = !_keepText;
-
 	int c = 0;
 	while (handleNextCharsetCode(a, &c)) {
 		if (c == 0) {
Index: scumm/scumm.h
===================================================================
--- scumm/scumm.h	(revision 26291)
+++ scumm/scumm.h	(working copy)
@@ -243,6 +243,7 @@
 	bool center;
 	bool overhead;
 	bool no_talk_anim;
+	bool wrapping;
 };
 
 struct StringTab : StringSlot {
Index: scumm/charset.cpp
===================================================================
--- scumm/charset.cpp	(revision 26291)
+++ scumm/charset.cpp	(working copy)
@@ -348,6 +348,44 @@
 	return width;
 }
 
+int CharsetRenderer::getStringHeight(const byte *text) {
+	int pos = 0;
+	int height = 0;
+	byte chr;
+	int oldID = getCurID();
+
+	while ((chr = text[pos++]) != 0) {
+		if (chr == '\n' || chr == '\r')
+			break;
+		if (chr == '@')
+			continue;
+		if (chr == 255 || (_vm->_game.version <= 6 && chr == 254)) {
+			chr = text[pos++];
+			if (chr == 3) { // 'WAIT'
+				height += getFontHeight();
+				break;
+			}
+			if (chr == 10 || chr == 21 || chr == 12 || chr == 13) {
+				pos += 2;
+				continue;
+			}
+			if (chr == 9 || chr == 1 || chr == 2) { // 'Newline'
+				height += getFontHeight();
+				continue;
+			}
+			if (chr == 14) {
+				int set = text[pos] | (text[pos + 1] << 8);
+				pos += 2;
+				setCurID(set);
+				continue;
+			}
+		}
+	}
+
+	setCurID(oldID);
+	return height + getFontHeight();
+}
+
 void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
 	int lastspace = -1;
 	int curw = 1;
Index: scumm/script_v8.cpp
===================================================================
--- scumm/script_v8.cpp	(revision 26291)
+++ scumm/script_v8.cpp	(working copy)
@@ -488,7 +488,7 @@
 		_string[m].charset = pop();
 		break;
 	case 0xCE:		// SO_PRINT_LEFT
-		_string[m].center = false;
+		_string[m].wrapping = false;
 		_string[m].overhead = false;
 		break;
 	case 0xCF:		// SO_PRINT_OVERHEAD
@@ -503,7 +503,8 @@
 		_scriptPointer += resStrLen(_scriptPointer) + 1;
 		break;
 	case 0xD2:		// SO_PRINT_WRAP Set print wordwrap
-		//debug(0, "decodeParseString: SO_PRINT_WRAP");
+		_string[m].wrapping = true;
+		_string[m].overhead = false;
 		break;
 	default:
 		error("decodeParseString: default case 0x%x", b);
