From 8dfa9a70f26f508779d674520673718ae1b6e995 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Fri, 2 Jan 2009 01:34:53 +0000 Subject: Fix bugs #1940871: "AGI: Fan(Beyond Titanic 2) - Lockup" and #1874918: "AGI GR: Text wrapping broken on long strings in the mines" by replacing our text wrap routine with one based on GBAGI. Also made debug messages more clear. svn-id: r35664 --- engines/agi/text.cpp | 145 +++++++++++++++++++++++++++------------------------ 1 file changed, 78 insertions(+), 67 deletions(-) (limited to 'engines/agi/text.cpp') diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp index 99031612f0..7e8d569fea 100644 --- a/engines/agi/text.cpp +++ b/engines/agi/text.cpp @@ -128,7 +128,7 @@ void AgiEngine::blitTextbox(const char *p, int y, int x, int len) { int xoff, yoff, lin, h, w; char *msg, *m; - debugC(3, kDebugLevelText, "x=%d, y=%d, len=%d", x, y, len); + debugC(3, kDebugLevelText, "blitTextbox(): x=%d, y=%d, len=%d", x, y, len); if (_game.window.active) closeWindow(); @@ -140,7 +140,6 @@ void AgiEngine::blitTextbox(const char *p, int y, int x, int len) { xoff = x * CHAR_COLS; yoff = y * CHAR_LINES; - len--; m = msg = wordWrapString(agiSprintf(p), &len); @@ -176,11 +175,11 @@ void AgiEngine::blitTextbox(const char *p, int y, int x, int len) { void AgiEngine::eraseTextbox() { if (!_game.window.active) { - debugC(3, kDebugLevelText, "no window active"); + debugC(3, kDebugLevelText, "eraseTextbox(): no window active"); return; } - debugC(4, kDebugLevelText, "x1=%d, y1=%d, x2=%d, y2=%d", _game.window.x1, + debugC(4, kDebugLevelText, "eraseTextbox(): x1=%d, y1=%d, x2=%d, y2=%d", _game.window.x1, _game.window.y1, _game.window.x2, _game.window.y2); _gfx->restoreBlock(_game.window.x1, _game.window.y1, @@ -204,7 +203,7 @@ void AgiEngine::printText(const char *msg, int f, int x, int y, int len, int fg, x *= CHAR_COLS; y *= CHAR_LINES; - debugC(4, kDebugLevelText, "%s, %d, %d, %d, %d, %d, %d", msg, f, x, y, len, fg, bg); + debugC(4, kDebugLevelText, "printText(): %s, %d, %d, %d, %d, %d, %d", msg, f, x, y, len, fg, bg); printText2(0, agiSprintf(msg), f, x, y, len, fg, bg, checkerboard); } @@ -222,71 +221,83 @@ void AgiEngine::printTextConsole(const char *msg, int x, int y, int len, int fg, * Wrap text line to the specified width. * @param str String to wrap. * @param len Length of line. + * + * Based on GBAGI implementaiton with permission from the author */ -char *AgiEngine::wordWrapString(char *str, int *len) { - /* If the message has a long word (longer than 31 character) then - * loop in line 239 (for (; *v != ' '; v--, c--);) can wrap - * around 0 and write large number in c. This causes returned - * length to be negative (!) and eventually crashes in calling - * code. The fix is simple -- remove unsigned in maxc, c, l - * declaration. --Vasyl - */ - char *msg, *v, *e; - int maxc, c, l = *len; - - v = msg = strdup(str); - e = msg + strlen(msg); - maxc = 0; - - for (;;) { - debugC(3, kDebugLevelText, "[%s], %d", msg, maxc); - if (strchr(v, ' ') == NULL && (int)strlen(v) > l) { - debugC(1, kDebugLevelText | kDebugLevelMain, "Word too long in message"); - l = strlen(v); +char *AgiEngine::wordWrapString(char *s, int *len) { + char *pWord, *outStr, *msgBuf, maxWidth = *len; + int lnLen, wLen; + + msgBuf = outStr = strdup(s); + + int msgWidth = 0; + + lnLen = 0; + + while (*s) { + pWord = s; + wLen = 0; + + while (*s != '\0' && *s != ' ' && *s != '\n' && *s != '\r') + s++; + + wLen = (int)(s - pWord); + + if (wLen && *s == '\n' && s[-1] == ' ') + wLen--; + + if (wLen + lnLen >= maxWidth) { + if (outStr != msgBuf) { + if (outStr[-1] == ' ') + outStr[-1] = '\n'; + else + *outStr++ = '\n'; + } + + lnLen = 0; + + while (wLen >= maxWidth) { + msgWidth = maxWidth; + + memcpy(outStr, pWord, maxWidth); + + wLen -= maxWidth; + outStr += maxWidth; + pWord += maxWidth; + *outStr++ = '\n'; + } } - /* Must include \r for MacOS 8 */ - while ((c = strcspn(v, "\n\r")) <= l) { - debugC(3, kDebugLevelText, "c = %d, maxc = %d", c, maxc); - if (c > maxc) - maxc = c; - if ((v += c + 1) >= e) - goto end; + + if (wLen) { + memcpy(outStr, pWord, wLen); + outStr += wLen; + } + lnLen += wLen+1; + + if (lnLen > msgWidth) { + msgWidth = lnLen; + + if (*s == '\0' || *s == ' ' || *s == '\n' || *s == '\r') + msgWidth--; } - c = l; - if ((v += l) >= e) - break; - /* The same line that caused that bug I mentioned - * should also do another check: - * for (; *v != ' ' && *v != '\n'; v--, c--); - * While this does not matter in most cases, in the case of - * long words it caused extra \n inserted in the line - * preceding long word. This one is definitely non-critical; - * one might argue that the function is not supposed to deal - * with long words. BTW, that condition at the beginning of - * the while loop that checks word length does not make much - * sense -- it verifies the length of the first word but for - * the rest it does something odd. Overall, even with these - * changes the function is still not completely robust. - * --Vasyl - */ - if (*v != ' ') - for (; *v != ' ' && *v != '\n' && *v != '\r'; - v--, c--); - if (c > maxc) - maxc = c; - *v++ = '\n'; + if (*s == '\n') + lnLen = 0; + + if (*s) + *outStr++ = *s++; } - end: - *len = maxc; - return msg; + *outStr = '\0'; + *len = msgWidth; + + return msgBuf; } /** * Remove existing window, if any. */ void AgiEngine::closeWindow() { - debugC(4, kDebugLevelText, "close window"); + debugC(4, kDebugLevelText, "closeWindow()"); _sprites->eraseBoth(); eraseTextbox(); /* remove window, if any */ _sprites->blitBoth(); @@ -307,7 +318,7 @@ int AgiEngine::messageBox(const char *s) { blitTextbox(s, -1, -1, -1); _sprites->blitBoth(); k = waitKey(); - debugC(4, kDebugLevelText, "wait_key returned %02x", k); + debugC(4, kDebugLevelText, "messageBox(): wait_key returned %02x", k); closeWindow(); return k; @@ -333,7 +344,7 @@ int AgiEngine::selectionBox(const char *m, const char **b) { x = _game.window.x1 + 5 * CHAR_COLS / 2; y = _game.window.y2 - 5 * CHAR_LINES / 2; s = _game.window.x2 - _game.window.x1 + 1 - 5 * CHAR_COLS; - debugC(3, kDebugLevelText, "s = %d", s); + debugC(3, kDebugLevelText, "selectionBox(): s = %d", s); /* Automatically position buttons */ for (i = 0; b[i]; i++) { @@ -342,7 +353,7 @@ int AgiEngine::selectionBox(const char *m, const char **b) { } if (i > 1) { - debugC(3, kDebugLevelText, "s / %d = %d", i - 1, s / (i - 1)); + debugC(3, kDebugLevelText, "selectionBox(): s / %d = %d", i - 1, s / (i - 1)); s /= (i - 1); } else { x += s / 2; @@ -363,7 +374,7 @@ int AgiEngine::selectionBox(const char *m, const char **b) { AllowSyntheticEvents on(this); - debugC(4, kDebugLevelText, "waiting..."); + debugC(4, kDebugLevelText, "selectionBox(): waiting..."); while (!shouldQuit()) { for (i = 0; b[i]; i++) _gfx->drawCurrentStyleButton(bx[i], by[i], b[i], i == active, false, i == 0); @@ -396,7 +407,7 @@ int AgiEngine::selectionBox(const char *m, const char **b) { } break; case 0x09: /* Tab */ - debugC(3, kDebugLevelText, "Focus change"); + debugC(3, kDebugLevelText, "selectionBox(): Focus change"); active++; active %= i; break; @@ -405,11 +416,11 @@ int AgiEngine::selectionBox(const char *m, const char **b) { } press: - debugC(4, kDebugLevelText, "Button pressed: %d", rc); + debugC(4, kDebugLevelText, "selectionBox(): Button pressed: %d", rc); getout: closeWindow(); - debugC(2, kDebugLevelText, "Result = %d", rc); + debugC(2, kDebugLevelText, "selectionBox(): Result = %d", rc); return rc; } @@ -421,7 +432,7 @@ int AgiEngine::print(const char *p, int lin, int col, int len) { if (p == NULL) return 0; - debugC(4, kDebugLevelText, "lin = %d, col = %d, len = %d", lin, col, len); + debugC(4, kDebugLevelText, "print(): lin = %d, col = %d, len = %d", lin, col, len); if (col == 0 && lin == 0 && len == 0) lin = col = -1; -- cgit v1.2.3