SCUMM: (SCUMM7/8) - reorganize font rendering - second part

- Attach actor talk texts to the appropriate text renderer and get rid of redundant code.
- Cleanup subtitle text handling.
- Fix handling of ^codes.
- Fix more regressions from last commit.
- Correct some x/y positioning.
This commit is contained in:
athrxx 2021-08-28 17:38:38 +02:00
parent 1f56132725
commit 0256e92c25
10 changed files with 164 additions and 448 deletions

View file

@ -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<int>(clipRect.top, y), _width - 1, _height);
return _width;
drawBits1(vs, x + vs.xstart, y, _charPtr, MAX<int>(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<int>(width, maxWidth);
width = 0;
} else if (*text != '\r' && *text != _vm->_newLineCharacter) {
width += getCharWidth(*text);
}
++text;
--strLenMax;
}
return MAX<int>(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);

View file

@ -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;

View file

@ -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<int>(x + shadowOffsetXTable[shadowIdx], clipRect.left);
int offY = MAX<int>(y + shadowOffsetYTable[shadowIdx], clipRect.top);
byte drawColor = shadowOffsetColorTable[shadowIdx];
for (int step = 0; step < _2byteSteps; ++step) {
int offX = MAX<int>(x + _2byteShadowXOffsetTable[step], clipRect.left);
int offY = MAX<int>(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<void *>(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<void *>(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

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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) {

View file

@ -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) {

View file

@ -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<int>(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;
}

View file

@ -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;