diff options
Diffstat (limited to 'engines/xeen/xsurface.cpp')
-rw-r--r-- | engines/xeen/xsurface.cpp | 248 |
1 files changed, 242 insertions, 6 deletions
diff --git a/engines/xeen/xsurface.cpp b/engines/xeen/xsurface.cpp index d3c547861b..5e7d831380 100644 --- a/engines/xeen/xsurface.cpp +++ b/engines/xeen/xsurface.cpp @@ -21,16 +21,29 @@ */ #include "common/algorithm.h" +#include "common/util.h" #include "xeen/xsurface.h" #include "xeen/resdata.h" namespace Xeen { -XSurface::XSurface(): Graphics::Surface() { +const byte *XSurface::_fontData; + +XSurface::XSurface() : Graphics::Surface(), _bgColor(DEFAULT_BG_COLOR), _fontReduced(false), + _fontJustify(JUSTIFY_NONE), _msgWraps(false) { + _textColors[0] = 0; + _textColors[1] = 0x40; + _textColors[2] = 0x30; + _textColors[3] = 0x20; } -XSurface::XSurface(int w, int h) : Graphics::Surface() { +XSurface::XSurface(int w, int h) : Graphics::Surface(), _bgColor(DEFAULT_BG_COLOR), + _fontReduced(false), _fontJustify(JUSTIFY_NONE), _msgWraps(false) { create(w, h); + _textColors[0] = 0; + _textColors[1] = 0x40; + _textColors[2] = 0x30; + _textColors[3] = 0x20; } XSurface::~XSurface() { @@ -87,10 +100,10 @@ void XSurface::blitTo(XSurface &dest, const Common::Point &destPos) const { void XSurface::writeSymbol(int symbolId) { const byte *srcP = &SYMBOLS[symbolId][0]; - for (int yp = 0; yp < SYMBOL_HEIGHT; ++yp) { + for (int yp = 0; yp < FONT_HEIGHT; ++yp) { byte *destP = (byte *)getBasePtr(_writePos.x, _writePos.y + yp); - for (int xp = 0; xp < SYMBOL_WIDTH; ++xp, ++destP) { + for (int xp = 0; xp < FONT_WIDTH; ++xp, ++destP) { byte b = *srcP++; if (b) *destP = b; @@ -102,9 +115,158 @@ void XSurface::writeSymbol(int symbolId) { /** * Write a string to the surface + * @param s String to display + * @param bounds Window bounds to display string within + * @returns Any string remainder that couldn't be displayed + * @remarks Note that bounds is just used for wrapping purposes. Unless + * justification is set, the message will be written at _writePos */ -void XSurface::writeString(const Common::String &s) { - error("TODO"); +Common::String XSurface::writeString(const Common::String &s, const Common::Rect &bounds) { + _displayString = s.c_str(); + + for (;;) { + _msgWraps = false; + + // Get the size of the string that can be displayed on the likne + int xp = _fontJustify ? bounds.left : _writePos.x; + while (!getNextCharWidth(xp)) { + if (xp >= bounds.right) { + --_displayString; + _msgWraps = true; + } + } + + // Get the end point of the text that can be displayed + const char *displayEnd = _displayString; + _displayString = s.c_str(); + + if (*displayEnd && _fontJustify != JUSTIFY_RIGHT && xp >= bounds.right) { + // Need to handle justification of text + // First, move backwards to find the end of the previous word + // for a convenient point to break the line at + const char *endP = displayEnd; + while (endP > _displayString && (*endP & 0x7f) != ' ') + --endP; + + if (endP == _displayString) { + // There was no word breaks at all in the string + --displayEnd; + if (_fontJustify == JUSTIFY_NONE && _writePos.x != bounds.left) { + // Move to the next line + if (!newLine(bounds)) + continue; + // Ran out of space to display string + break; + } + } else { + // Found word break, find end of previous word + while (displayEnd > _displayString && (*displayEnd & 0x7f) == ' ') + --displayEnd; + } + } + + // Main character display loop + while (_displayString <= displayEnd) { + char c = getNextChar(); + + if (c == ' ') { + _writePos.x += _fontReduced ? 3 : 4; + } else if (c == '\r') { + fillRect(bounds, _bgColor); + _writePos = Common::Point(bounds.left, bounds.top); + } else if (c == 1) { + // Turn off reduced font mode + _fontReduced = false; + } else if (c == 2) { + // Turn on reduced font mode + _fontReduced = true; + } else if (c == 3) { + // Justify text + c = getNextChar(); + if (c == 'r') + _fontJustify = JUSTIFY_RIGHT; + else if (c == 'c') + _fontJustify = JUSTIFY_CENTER; + else + _fontJustify = JUSTIFY_NONE; + } else if (c == 4) { + // Draw an empty box of a given width + int w = fontAtoi(); + Common::Point pt = _writePos; + if (_fontJustify == JUSTIFY_RIGHT) + pt.x -= w; + fillRect(Common::Rect(pt.x, pt.y, pt.x + w, pt.y + (_fontReduced ? 9 : 10)), + _bgColor); + } else if (c == 5) { + continue; + } else if (c == 6) { + // Non-breakable space + writeChar(' '); + } else if (c == 7) { + // Set text background color + int c = fontAtoi(); + _bgColor = (c < 0 || c > 255) ? DEFAULT_BG_COLOR : c; + } else if (c == 8) { + // Draw a character outline + c = getNextChar(); + if (c == ' ') { + c = '\0'; + _writePos.x -= 3; + } else { + if (c == 6) + c = ' '; + byte charSize = _fontData[0x1000 + (int)c + (_fontReduced ? 0x80 : 0)]; + _writePos.x -= charSize; + } + + if (_writePos.x < bounds.left) + _writePos.x = bounds.left; + + if (c) { + int oldX = _writePos.x; + byte oldColor[4]; + Common::copy(&_textColors[0], &_textColors[4], &oldColor[0]); + + _textColors[1] = _textColors[2] = _textColors[3] = _bgColor; + writeChar(c); + + Common::copy(&oldColor[0], &oldColor[4], &_textColors[0]); + _writePos.x = oldX; + } + } else if (c == 9) { + // Skip x position + int xp = fontAtoi(); + _writePos.x = MIN(bounds.left + xp, (int)bounds.right); + } else if (c == 10) { + // Newline + if (newLine(bounds)) + break; + } else if (c == 11) { + // Skip y position + int yp = fontAtoi( ); + _writePos.y = MIN(bounds.top + yp, (int)bounds.bottom); + } else if (c == 12) { + // Set text colors + int idx = fontAtoi(); + if (idx < 0) + idx = 0; + setTextColor(idx); + } else if (c < ' ') { + // Invalid command + displayEnd = nullptr; + break; + } else { + // Standard character - write it out + writeChar(c); + } + } + + if (_displayString > displayEnd && _fontJustify != JUSTIFY_RIGHT && _msgWraps + && newLine(bounds)) + break; + } + + return Common::String(_displayString); } /** @@ -114,5 +276,79 @@ void XSurface::writeChar(char c) { error("TODO"); } +/** + * Return the next pending character to display + */ +char XSurface::getNextChar() { + return *_displayString++ & 0x7f; +} + +/** +* Return the width of a given character +*/ +bool XSurface::getNextCharWidth(int &total) { + char c = getNextChar(); + + if (c > ' ') { + total += _fontData[0x1000 + (int)c + (_fontReduced ? 0x80 : 0)]; + return false; + } else if (c == ' ') { + total += 4; + return false; + } else if (c == 8) { + c = getNextChar(); + if (c == ' ') { + total -= 2; + return false; + } else { + _displayString -= 2; + return true; + } + } else if (c == 12) { + c = getNextChar(); + if (c != 'd') + getNextChar(); + return false; + } else { + --_displayString; + return true; + } +} + +bool XSurface::newLine(const Common::Rect &bounds) { + // Move past any spaces currently being pointed to + while ((*_displayString & 0x7f) == ' ') + ++_displayString; + + _msgWraps = false; + _writePos.x = bounds.left; + + int h = _fontReduced ? 9 : 10; + _writePos.y += h; + + return ((_writePos.y + h - 1) > bounds.bottom); +} + +int XSurface::fontAtoi(int len) { + int total = 0; + for (int i = 0; i < len; ++i) { + char c = getNextChar(); + if (c == ' ') + c = '0'; + + int digit = c - '0'; + if (digit < 0 || digit > 9) + return -1; + + total = total * 10 + digit; + } + + return total; +} + +void XSurface::setTextColor(int idx) { + const byte *colP = &TEXT_COLORS[idx][0]; + Common::copy(colP, colP + 4, &_textColors[0]); +} } // End of namespace Xeen |