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:
parent
1f56132725
commit
0256e92c25
10 changed files with 164 additions and 448 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -33,7 +33,20 @@ NutRenderer::NutRenderer(ScummEngine *vm, const char *filename) :
|
|||
_maxCharSize(0),
|
||||
_fontHeight(0),
|
||||
_charBuffer(0),
|
||||
_decodedData(0) {
|
||||
_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);
|
||||
}
|
||||
|
@ -41,6 +54,7 @@ NutRenderer::NutRenderer(ScummEngine *vm, const char *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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,20 +459,19 @@ 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);
|
||||
|
||||
if (_talkDelay)
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue