aboutsummaryrefslogtreecommitdiff
path: root/engines/xeen/xsurface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/xeen/xsurface.cpp')
-rw-r--r--engines/xeen/xsurface.cpp248
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