diff options
author | Martin Kiewitz | 2014-11-01 23:36:29 +0100 |
---|---|---|
committer | Martin Kiewitz | 2014-11-01 23:36:29 +0100 |
commit | c9e74d6d54bf59b3587eea0785ce31df5d46615b (patch) | |
tree | e3512f63946589013111e3f0328903a678036765 | |
parent | c4e40fc83fa79ed14f9a5dd434b952d5b3e4eb56 (diff) | |
download | scummvm-rg350-c9e74d6d54bf59b3587eea0785ce31df5d46615b.tar.gz scummvm-rg350-c9e74d6d54bf59b3587eea0785ce31df5d46615b.tar.bz2 scummvm-rg350-c9e74d6d54bf59b3587eea0785ce31df5d46615b.zip |
SCI: text16 GetLongest() changes fixes bug #5334
also fixes bug #5159
also fixes Japanese line wrapping (hopefully)
-rw-r--r-- | engines/sci/graphics/text16.cpp | 177 | ||||
-rw-r--r-- | engines/sci/graphics/text16.h | 2 |
2 files changed, 114 insertions, 65 deletions
diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp index 808ce45d0d..66cff422ae 100644 --- a/engines/sci/graphics/text16.cpp +++ b/engines/sci/graphics/text16.cpp @@ -143,7 +143,8 @@ int16 GfxText16::CodeProcessing(const char *&text, GuiResourceId orgFontId, int1 return textCodeSize; } -static const uint16 text16_punctuationSjis[] = { +// Has actually punctuation and characters in it, that may not be the first in a line +static const uint16 text16_shiftJIS_punctuation[] = { 0x9F82, 0xA182, 0xA382, 0xA582, 0xA782, 0xC182, 0xA782, 0xC182, 0xE182, 0xE382, 0xE582, 0xEC82, 0x4083, 0x4283, 0x4483, 0x4683, 0x4883, 0x6283, 0x8383, 0x8583, 0x8783, 0x8E83, 0x9583, 0x9683, 0x5B81, 0x4181, 0x4281, 0x7681, 0x7881, 0x4981, 0x4881, 0 @@ -151,10 +152,21 @@ static const uint16 text16_punctuationSjis[] = { // return max # of chars to fit maxwidth with full words, does not include // breaking space -int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgFontId) { +// Also adjusts text pointer to the new position for the caller +// +// Special cases in games: +// Laura Bow 2 - Credits in the game menu - all the text lines start with spaces (bug #5159) +// Act 6 Coroner questionaire - the text of all control buttons has trailing spaces +// "Detective Ryan Hanrahan O'Riley" contains even more spaces (bug #5334) +// Conquests of Camelot - talking with Cobb - one text box of the dialogue contains a longer word, +// that will be broken into 2 lines (bug #5159) +int16 GfxText16::GetLongest(const char *&textPtr, int16 maxWidth, GuiResourceId orgFontId) { uint16 curChar = 0; - int16 maxChars = 0, curCharCount = 0; - uint16 width = 0; + const char *textStartPtr = textPtr; + const char *lastSpacePtr = NULL; + int16 lastSpaceCharCount = 0; + int16 curCharCount = 0, resultCharCount = 0; + uint16 curWidth = 0, tempWidth = 0; GuiResourceId previousFontId = GetFontId(); int16 previousPenColor = _ports->_curPort->penClr; @@ -162,35 +174,34 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF if (!_font) return 0; - while (width <= maxWidth) { - curChar = (*(const byte *)text++); + while (1) { + curChar = (*(const byte *)textPtr); if (_font->isDoubleByte(curChar)) { - curChar |= (*(const byte *)text++) << 8; - curCharCount++; + curChar |= (*(const byte *)textPtr + 1) << 8; } switch (curChar) { case 0x7C: if (getSciVersion() >= SCI_VERSION_1_1) { - curCharCount++; - curCharCount += CodeProcessing(text, orgFontId, previousPenColor, false); + curCharCount++; textPtr++; + curCharCount += CodeProcessing(textPtr, orgFontId, previousPenColor, false); continue; } break; // We need to add 0xD, 0xA and 0xD 0xA to curCharCount and then exit - // which means, we split text like - // 'Mature, experienced software analyst available.' 0xD 0xA - // 'Bug installation a proven speciality. "No version too clean."' (normal game text, this is from lsl2) - // and 0xA '-------' 0xA (which is the official sierra subtitle separator) + // which means, we split text like for example + // - 'Mature, experienced software analyst available.' 0xD 0xA + // 'Bug installation a proven speciality. "No version too clean."' (normal game text, this is from lsl2) + // - 0xA '-------' 0xA (which is the official sierra subtitle separator) (found in multilingual versions) // Sierra did it the same way. case 0xD: // Check, if 0xA is following, if so include it as well - if ((*(const unsigned char *)text) == 0xA) - curCharCount++; + if ((*(const byte *)textPtr + 1) == 0xA) + curCharCount++; textPtr++; // it's meant to pass through here case 0xA: case 0x9781: // this one is used by SQ4/japanese as line break as well - curCharCount++; + curCharCount++; textPtr++; // and it's also meant to pass through here case 0: SetFont(previousFontId); @@ -198,55 +209,90 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF return curCharCount; case ' ': - maxChars = curCharCount; // return count up to (but not including) breaking space + lastSpaceCharCount = curCharCount; // return count up to (but not including) breaking space + lastSpacePtr = textPtr + 1; // remember position right after the current space break; } - // Sometimes this can go off the screen, like for example bug #3040161. - // However, we only perform this for non-Japanese games, as these require - // special handling, done after this loop. - if (width + _font->getCharWidth(curChar) > maxWidth && g_sci->getLanguage() != Common::JA_JPN) + tempWidth += _font->getCharWidth(curChar); + + // Width is too large? -> break out + if (tempWidth > maxWidth) break; - width += _font->getCharWidth(curChar); - curCharCount++; - } - // Text without spaces, probably Kanji/Japanese - if (maxChars == 0) { - maxChars = curCharCount; + // still fits, remember width + curWidth = tempWidth; - uint16 nextChar; + // go to next character + curCharCount++; textPtr++; + if (curChar > 0xFF) { + // Double-Byte + curCharCount++; textPtr++; + } + } - // We remove the last char only, if maxWidth was actually equal width - // before adding the last char. Otherwise we won't get the same cutting - // as in sierra pc98 sci. - if (maxWidth == (width - _font->getCharWidth(curChar))) { - maxChars--; - if (curChar > 0xFF) - maxChars--; - nextChar = curChar; - } else { - nextChar = (*(const byte *)text++); - if (_font->isDoubleByte(nextChar)) - nextChar |= (*(const byte *)text++) << 8; - } - // sierra checked the following character against a punctuation kanji table - if (nextChar > 0xFF) { - // if the character is punctuation, we go back one character - uint nonBreakingNr = 0; - while (text16_punctuationSjis[nonBreakingNr]) { - if (text16_punctuationSjis[nonBreakingNr] == nextChar) { - maxChars--; - if (curChar > 0xFF) - maxChars--; // go back 2 chars, when last char was double byte + if (lastSpaceCharCount) { + // Break and at least one space was found before that + resultCharCount = lastSpaceCharCount; + + // additionally skip over all spaces, that are following that space, but don't count them for displaying purposes + textPtr = lastSpacePtr; + while (*textPtr == ' ') + textPtr++; + + } else { + // Break without spaces found, we split the very first word - may also be Kanji/Japanese + if (curChar > 0xFF) { + // current charracter is Japanese + + // PC-9801 SCI actually added the last character, which shouldn't fit anymore, still onto the + // screen in case maxWidth wasn't fully reached with the last character + if (maxWidth == curWidth) { + curCharCount -= 2; textPtr -= 2; + if (textPtr < textStartPtr) + error("Seeking back went too far, data corruption?"); + + curChar = (*(const byte *)textPtr); + if (!_font->isDoubleByte(curChar)) + error("Non double byte while seeking back"); + curChar |= (*(const byte *)textPtr + 1) << 8; + } + + // But it also checked, if the current character is not inside a punctuation table and it even + // went backwards in case it found multiple ones inside that table. + uint nonBreakingPos = 0; + + while (1) { + // Look up if character shouldn't be the first on a new line + nonBreakingPos = 0; + while (text16_shiftJIS_punctuation[nonBreakingPos]) { + if (text16_shiftJIS_punctuation[nonBreakingPos] == curChar) + break; + nonBreakingPos++; + } + if (!text16_shiftJIS_punctuation[nonBreakingPos]) { + // character is fine break; } - nonBreakingNr++; + // Character is not acceptable, seek backward in the text + curCharCount -= 2; textPtr -= 2; + if (textPtr < textStartPtr) + error("Seeking back went too far, data corruption?"); + + curChar = (*(const byte *)textPtr); + if (!_font->isDoubleByte(curChar)) + error("Non double byte while seeking back"); + curChar |= (*(const byte *)textPtr + 1) << 8; } + // include the current character + curCharCount += 2; textPtr += 2; } + + // We split the word in that case + resultCharCount = curCharCount; } SetFont(previousFontId); _ports->penColor(previousPenColor); - return maxChars; + return resultCharCount; } void GfxText16::Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont) { @@ -328,15 +374,16 @@ int16 GfxText16::Size(Common::Rect &rect, const char *text, GuiResourceId fontId // rect.right=found widest line with RTextWidth and GetLongest // rect.bottom=num. lines * GetPointSize rect.right = (maxWidth ? maxWidth : 192); - const char *curPos = text; - while (*curPos) { - charCount = GetLongest(curPos, rect.right, fontId); + const char *curTextPos = text; // in work position for GetLongest() + const char *curTextLine = text; // starting point of current line + while (*curTextPos) { + charCount = GetLongest(curTextPos, rect.right, fontId); if (charCount == 0) break; - Width(curPos, 0, charCount, fontId, textWidth, textHeight, false); + Width(curTextLine, 0, charCount, fontId, textWidth, textHeight, false); maxTextWidth = MAX(textWidth, maxTextWidth); totalHeight += textHeight; - curPos += charCount; + curTextLine = curTextPos; } rect.bottom = totalHeight; rect.right = maxWidth ? maxWidth : MIN(rect.right, maxTextWidth); @@ -410,6 +457,8 @@ void GfxText16::Box(const char *text, bool show, const Common::Rect &rect, TextA GuiResourceId previousFontId = GetFontId(); int16 previousPenColor = _ports->_curPort->penClr; bool doubleByteMode = false; + const char *curTextPos = text; + const char *curTextLine = text; if (fontId != -1) SetFont(fontId); @@ -426,11 +475,11 @@ void GfxText16::Box(const char *text, bool show, const Common::Rect &rect, TextA _codeRefTempRect.left = _codeRefTempRect.top = -1; maxTextWidth = 0; - while (*text) { - charCount = GetLongest(text, rect.width(), fontId); + while (*curTextPos) { + charCount = GetLongest(curTextPos, rect.width(), fontId); if (charCount == 0) break; - Width(text, 0, charCount, fontId, textWidth, textHeight, true); + Width(curTextLine, 0, charCount, fontId, textWidth, textHeight, true); maxTextWidth = MAX<int16>(maxTextWidth, textWidth); switch (alignment) { case SCI_TEXT16_ALIGNMENT_RIGHT: @@ -449,13 +498,13 @@ void GfxText16::Box(const char *text, bool show, const Common::Rect &rect, TextA _ports->moveTo(rect.left + offset, rect.top + hline); if (show) { - Show(text, 0, charCount, fontId, previousPenColor); + Show(curTextLine, 0, charCount, fontId, previousPenColor); } else { - Draw(text, 0, charCount, fontId, previousPenColor); + Draw(curTextLine, 0, charCount, fontId, previousPenColor); } hline += textHeight; - text += charCount; + curTextLine = curTextPos; } SetFont(previousFontId); _ports->penColor(previousPenColor); diff --git a/engines/sci/graphics/text16.h b/engines/sci/graphics/text16.h index ab0cb13a64..715891be44 100644 --- a/engines/sci/graphics/text16.h +++ b/engines/sci/graphics/text16.h @@ -51,7 +51,7 @@ public: void ClearChar(int16 chr); - int16 GetLongest(const char *text, int16 maxWidth, GuiResourceId orgFontId); + int16 GetLongest(const char *&text, int16 maxWidth, GuiResourceId orgFontId); void Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont); void StringWidth(const char *str, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight); void ShowString(const char *str, GuiResourceId orgFontId, int16 orgPenColor); |