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(clipRect.top, y), _width - 1, _height); - return _width; + drawBits1(vs, x + vs.xstart, y, _charPtr, MAX(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(width, maxWidth); - width = 0; - } else if (*text != '\r' && *text != _vm->_newLineCharacter) { - width += getCharWidth(*text); - } - - ++text; - --strLenMax; - } - - return MAX(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(x + shadowOffsetXTable[shadowIdx], clipRect.left); - int offY = MAX(y + shadowOffsetYTable[shadowIdx], clipRect.top); - byte drawColor = shadowOffsetColorTable[shadowIdx]; + + for (int step = 0; step < _2byteSteps; ++step) { + int offX = MAX(x + _2byteShadowXOffsetTable[step], clipRect.left); + int offY = MAX(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; + + return width + _spacing; } -/* -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(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; - } -} - -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(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(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); + Common::Point subtitlePos(_string[0].xpos, _string[0].ypos); + addSubtitleToQueue(_charsetBuffer + _charsetBufPos, subtitlePos, _charsetColor, _charset->getCurID(), _string[0].center, _string[0].wrapping); + _charsetBufPos = newPos; - 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; + _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;