SCUMM: (SCUMM7/8) - cleanup (text) verb drawing and Hebrew right-to-left drawing

(SCUMM7/8 only)
This commit is contained in:
athrxx 2021-09-09 20:38:54 +02:00
parent d9ca128863
commit acb665c109
8 changed files with 87 additions and 80 deletions

View file

@ -1294,14 +1294,18 @@ void Insane::smlayer_showStatusMsg(int32 arg_0, byte *renderBitmap, int32 codecp
assert(sf != NULL); assert(sf != NULL);
if (_vm->_language == Common::HE_ISR && !(flags & kStyleAlignCenter)) {
flags |= kStyleAlignRight;
pos_x = _player->_width - 1 - pos_x;
}
TextStyleFlags flg = (TextStyleFlags)(flags & 7);
// flags: // flags:
// bit 0 - center 0x01 // bit 0 - center 0x01
// bit 1 - not used (align right) 0x02 // bit 1 - not used (align right) 0x02
// bit 2 - word wrap 0x04 // bit 2 - word wrap 0x04
// bit 3 - switchable 0x08 // bit 3 - switchable 0x08
// bit 4 - fill background 0x10 // bit 4 - fill background 0x10
TextStyleFlags flg = (TextStyleFlags)(flags & 7); if (flg & kStyleWordWrap) {
if ((flg & kStyleWordWrap) || _vm->_language == Common::HE_ISR) {
Common::Rect clipRect(0, 0, _player->_width, _player->_height); Common::Rect clipRect(0, 0, _player->_width, _player->_height);
sf->drawStringWrap(str, renderBitmap, clipRect, pos_x, pos_y, color, flg); sf->drawStringWrap(str, renderBitmap, clipRect, pos_x, pos_y, color, flg);
} else { } else {

View file

@ -48,7 +48,7 @@ protected:
byte *_paletteMap; byte *_paletteMap;
byte _bpp; byte _bpp;
byte _palette[16]; byte _palette[16];
const bool _direction; const int _direction;
const int8 *_2byteShadowXOffsetTable; const int8 *_2byteShadowXOffsetTable;
const int8 *_2byteShadowYOffsetTable; const int8 *_2byteShadowYOffsetTable;

View file

@ -821,8 +821,8 @@ static void syncWithSerializer(Common::Serializer &s, ObjectData &od) {
s.syncAsByte(od.flags, VER(46)); s.syncAsByte(od.flags, VER(46));
} }
static void syncWithSerializer(Common::Serializer &s, VerbSlot &vs, bool isISR) { static void syncWithSerializer(Common::Serializer &s, VerbSlot &vs, bool isV7orISR) {
s.syncAsSint16LE(!isISR ? vs.curRect.left : vs.origLeft, VER(8)); s.syncAsSint16LE(!isV7orISR ? vs.curRect.left : vs.origLeft, VER(8));
s.syncAsSint16LE(vs.curRect.top, VER(8)); s.syncAsSint16LE(vs.curRect.top, VER(8));
s.syncAsSint16LE(vs.curRect.right, VER(8)); s.syncAsSint16LE(vs.curRect.right, VER(8));
s.syncAsSint16LE(vs.curRect.bottom, VER(8)); s.syncAsSint16LE(vs.curRect.bottom, VER(8));
@ -844,15 +844,15 @@ static void syncWithSerializer(Common::Serializer &s, VerbSlot &vs, bool isISR)
s.syncAsByte(vs.center, VER(8)); s.syncAsByte(vs.center, VER(8));
s.syncAsByte(vs.prep, VER(8)); s.syncAsByte(vs.prep, VER(8));
s.syncAsUint16LE(vs.imgindex, VER(8)); s.syncAsUint16LE(vs.imgindex, VER(8));
if (isISR && s.isLoading() && s.getVersion() >= 8) if (isV7orISR && s.isLoading() && s.getVersion() >= 8)
vs.curRect.left = vs.origLeft; vs.curRect.left = vs.origLeft;
} }
static void syncWithSerializerNonISR(Common::Serializer &s, VerbSlot &vs) { static void syncWithSerializerDef(Common::Serializer &s, VerbSlot &vs) {
syncWithSerializer(s, vs, false); syncWithSerializer(s, vs, false);
} }
static void syncWithSerializerISR(Common::Serializer &s, VerbSlot &vs) { static void syncWithSerializerV7orISR(Common::Serializer &s, VerbSlot &vs) {
syncWithSerializer(s, vs, true); syncWithSerializer(s, vs, true);
} }
@ -1239,7 +1239,7 @@ void ScummEngine::saveLoadWithSerializer(Common::Serializer &s) {
// //
// Save/load misc stuff // Save/load misc stuff
// //
s.syncArray(_verbs, _numVerbs, _language != Common::HE_ISR ? syncWithSerializerNonISR : syncWithSerializerISR); s.syncArray(_verbs, _numVerbs, (_game.version < 7 && _language != Common::HE_ISR) ? syncWithSerializerDef : syncWithSerializerV7orISR);
s.syncArray(vm.nest, 16, syncWithSerializer); s.syncArray(vm.nest, 16, syncWithSerializer);
s.syncArray(_sentence, 6, syncWithSerializer); s.syncArray(_sentence, 6, syncWithSerializer);
s.syncArray(_string, 6, syncWithSerializer); s.syncArray(_string, 6, syncWithSerializer);

View file

@ -977,10 +977,7 @@ void ScummEngine_v8::o8_verbOps() {
break; break;
case 0x9A: // SO_VERB_AT Set verb (X,Y) placement case 0x9A: // SO_VERB_AT Set verb (X,Y) placement
vs->curRect.top = pop(); vs->curRect.top = pop();
if (_language == Common::HE_ISR) vs->origLeft = pop();
vs->curRect.right = _screenWidth - 1 - pop();
else
vs->origLeft = pop();
break; break;
case 0x9B: // SO_VERB_ON Turn verb on case 0x9B: // SO_VERB_ON Turn verb on
vs->curmode = 1; vs->curmode = 1;

View file

@ -661,6 +661,12 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
// changes on the fly will be ignored for Smush texts, since our code design does // 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). // not permit it and the feature isn't used anyway).
if (_vm->_language == Common::HE_ISR && !(flags & kStyleAlignCenter)) {
flags |= kStyleAlignRight;
pos_x = _width - 1 - pos_x;
}
TextStyleFlags flg = (TextStyleFlags)(flags & 7);
// flags: // flags:
// bit 0 - center 0x01 // bit 0 - center 0x01
// bit 1 - not used (align right) 0x02 // bit 1 - not used (align right) 0x02
@ -671,8 +677,8 @@ void SmushPlayer::handleTextResource(uint32 subType, int32 subSize, Common::Seek
// bit 6 - vertical fix (COMI) 0x40 (COMI handles this in the printing method, but I haven't seen a case where it is used) // bit 6 - vertical fix (COMI) 0x40 (COMI handles this in the printing method, but I haven't seen a case where it is used)
// bit 7 - skip ^ codes (COMI) 0x80 (should be irrelevant for Smush, we strip these commands anyway) // bit 7 - skip ^ codes (COMI) 0x80 (should be irrelevant for Smush, we strip these commands anyway)
// bit 8 - no vertical fix (COMI) 0x100 (COMI handles this in the printing method, but I haven't seen a case where it is used) // bit 8 - no vertical fix (COMI) 0x100 (COMI handles this in the printing method, but I haven't seen a case where it is used)
TextStyleFlags flg = (TextStyleFlags)(flags & 7);
if ((flg & kStyleWordWrap) || _vm->_language == Common::HE_ISR) { if (flg & kStyleWordWrap) {
// COMI has to do it all a bit different, of course. SCUMM7 games immediately render the text from here and actually use the clipping data // COMI has to do it all a bit different, of course. SCUMM7 games immediately render the text from here and actually use the clipping data
// provided by the text resource. COMI does not render directly, but enqueues a blast string (which is then drawn through the usual main // provided by the text resource. COMI does not render directly, but enqueues a blast string (which is then drawn through the usual main
// loop routines). During that process the rect data will get dumped and replaced with the following default values. It's hard to tell // loop routines). During that process the rect data will get dumped and replaced with the following default values. It's hard to tell

View file

@ -39,6 +39,7 @@ TextRenderer_v7::TextRenderer_v7(ScummEngine *vm, GlyphRenderer_v7 *gr) :
_screenWidth(vm->_screenWidth), _screenWidth(vm->_screenWidth),
_useCJKMode(vm->_useCJKMode), _useCJKMode(vm->_useCJKMode),
_direction(vm->_language == Common::HE_ISR ? -1 : 1), _direction(vm->_language == Common::HE_ISR ? -1 : 1),
_rtlCenteredOffset(vm->_language == Common::HE_ISR ? 1 : 0),
_spacing(vm->_language != Common::JA_JPN ? 1 : 0), _spacing(vm->_language != Common::JA_JPN ? 1 : 0),
_lineBreakMarker(vm->_newLineCharacter), _lineBreakMarker(vm->_newLineCharacter),
_newStyle (gr->newStyleWrapping()), _newStyle (gr->newStyleWrapping()),
@ -147,39 +148,33 @@ int TextRenderer_v7::getStringHeight(const char *str, uint numBytesMax) {
} }
void TextRenderer_v7::drawSubstring(const char *str, uint numBytesMax, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 &col, TextStyleFlags flags) { void TextRenderer_v7::drawSubstring(const char *str, uint numBytesMax, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 &col, TextStyleFlags flags) {
if (_lang == Common::HE_ISR) { for (int i = 0; str[i] != 0 && numBytesMax; ++i) {
for (int i = numBytesMax; i > 0; i--) { if (_newStyle && str[i] == '^') {
x += _gr->drawChar(buffer, clipRect, x, y, pitch, col, flags, str[i - 1]); if (str[i + 1] == 'f') {
} _gr->setFont(str[i + 3] - '0');
} else { i += 3;
for (int i = 0; str[i] != 0 && numBytesMax; ++i) { numBytesMax -= 4;
if (_newStyle && str[i] == '^') { continue;
if (str[i + 1] == 'f') { } else if (str[i + 1] == 'c') {
_gr->setFont(str[i + 3] - '0'); col = str[i + 4] - '0' + 10 *(str[i + 3] - '0');
i += 3; i += 4;
numBytesMax -= 4; numBytesMax -= 5;
continue; continue;
} else if (str[i + 1] == 'c') { } else if (str[i + 1] == 'l') {
col = str[i + 4] - '0' + 10 *(str[i + 3] - '0'); i++;
i += 4; numBytesMax -= 2;
numBytesMax -= 5; continue;
continue;
} else if (str[i + 1] == 'l') {
i++;
numBytesMax -= 2;
continue;
}
} }
}
if (is2ByteCharacter(_lang, str[i])) { if (is2ByteCharacter(_lang, str[i])) {
x += _gr->draw2byte(buffer, clipRect, x, y, pitch, col, (byte)str[i] + 256 * (byte)str[i + 1]); x += _gr->draw2byte(buffer, clipRect, x, y, pitch, col, (byte)str[i] + 256 * (byte)str[i + 1]);
++i; ++i;
--numBytesMax;
} else if (str[i] != '\n' && str[i] != _lineBreakMarker) {
x += _gr->drawChar(buffer, clipRect, x, y, pitch, col, flags, str[i]);
}
--numBytesMax; --numBytesMax;
} else if (str[i] != '\n' && str[i] != _lineBreakMarker) {
x += _gr->drawChar(buffer, clipRect, x, y, pitch, col, flags, str[i]);
} }
--numBytesMax;
} }
} }
@ -215,21 +210,25 @@ void TextRenderer_v7::drawString(const char *str, byte *buffer, Common::Rect &cl
int xpos = x; int xpos = x;
if (flags & kStyleAlignCenter) if (flags & kStyleAlignCenter)
xpos = x - _direction * width / 2; xpos = x - _direction * width / 2 + (_rtlCenteredOffset & width);
else if (((flags & kStyleAlignRight) && _direction == 1) || ((flags & kStyleAlignLeft) && _direction == -1)) else if (((flags & kStyleAlignRight) && _direction == 1) || (!(flags & kStyleAlignRight) && _direction == -1))
// The original interpreter apparently does not expect a right-to-left written language when the kStyleAlignRight flag is set.
// It just right-aligns a left-to-right string. So we now move xpos to the left like the original interpreter would if it is a
// left-to-right string, but leave it on the right in case of a right-to-left string (and vice versa for right-to-left strings
// with kStyleAlignLeft flag).
xpos = x - _direction * width; xpos = x - _direction * width;
if (!_newStyle) if (!_newStyle)
xpos = CLIP<int>(xpos, clipRect.left, _screenWidth - width); xpos = (_direction == 1) ? CLIP<int>(xpos, clipRect.left, _screenWidth - width) : CLIP<int>(xpos, clipRect.left + width, _screenWidth - 1);
drawSubstring(str + lineStart, len, buffer, clipRect, _direction * width, y, pitch, col, flags); drawSubstring(str + lineStart, len, buffer, clipRect, xpos, y, pitch, col, flags);
y += height; y += height;
} }
lineStart = pos + 1; lineStart = pos + 1;
} }
clipRect.left = (flags & kStyleAlignCenter) ? x - maxWidth / 2: ((flags & kStyleAlignRight) ? x - maxWidth : x); clipRect.left = (flags & kStyleAlignCenter) ? x - maxWidth / 2 : ((flags & kStyleAlignRight) ? x - maxWidth : x);
clipRect.right = MIN<int>(clipRect.right, clipRect.left + maxWidth); clipRect.right = MIN<int>(clipRect.right, clipRect.left + maxWidth);
clipRect.top = y2; clipRect.top = y2;
clipRect.bottom = y; clipRect.bottom = y;
@ -349,7 +348,7 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
if (x - (maxWidth >> 1) < clipRect.left) if (x - (maxWidth >> 1) < clipRect.left)
x = clipRect.left + (maxWidth >> 1); x = clipRect.left + (maxWidth >> 1);
} else if (flags & kStyleAlignRight) { } else if (flags & kStyleAlignRight) {
if (x < clipRect.right) if (x > clipRect.right)
x = clipRect.right; x = clipRect.right;
if (x < clipRect.left + maxWidth); if (x < clipRect.left + maxWidth);
x = clipRect.left + maxWidth; x = clipRect.left + maxWidth;
@ -366,12 +365,16 @@ void TextRenderer_v7::drawStringWrap(const char *str, byte *buffer, Common::Rect
for (int i = 0; i < numSubstrings; i++) { for (int i = 0; i < numSubstrings; i++) {
int xpos = x; int xpos = x;
if (flags & kStyleAlignCenter) if (flags & kStyleAlignCenter)
xpos = x - _direction * substrWidths[i] / 2; xpos = x - _direction * substrWidths[i] / 2 + (_rtlCenteredOffset & substrWidths[i]);
else if (((flags & kStyleAlignRight) && _direction == 1) || ((flags & kStyleAlignLeft) && _direction == -1)) else if (((flags & kStyleAlignRight) && _direction == 1) || (!(flags & kStyleAlignRight) && _direction == -1))
// The original interpreter apparently does not expect a right-to-left written language when the kStyleAlignRight flag is set.
// It just right-aligns a left-to-right string. So we now move xpos to the left like the original interpreter would if it is a
// left-to-right string, but leave it on the right in case of a right-to-left string (and vice versa for right-to-left strings
// with kStyleAlignLeft flag).
xpos = x - _direction * substrWidths[i]; xpos = x - _direction * substrWidths[i];
if (!_newStyle) if (!_newStyle)
xpos = CLIP<int>(xpos, clipRect.left, _screenWidth - substrWidths[i]); xpos = (_direction == 1) ? CLIP<int>(xpos, clipRect.left, _screenWidth - substrWidths[i]) : CLIP<int>(xpos, clipRect.left + substrWidths[i], _screenWidth - 1);
len = substrByteLength[i] > 0 ? substrByteLength[i] : 0; len = substrByteLength[i] > 0 ? substrByteLength[i] : 0;
drawSubstring(str + substrStart[i], len, buffer, clipRect, xpos, y, pitch, col, flags); drawSubstring(str + substrStart[i], len, buffer, clipRect, xpos, y, pitch, col, flags);
@ -440,6 +443,12 @@ void ScummEngine_v7::drawBlastTexts() {
_charset->setCurID(_blastTextQueue[i].charset); _charset->setCurID(_blastTextQueue[i].charset);
// If a Hebrew String comes up that is still marked as kStyleAlignLeft we fix it here...
if (_language == Common::HE_ISR && !(bt.flags & (kStyleAlignCenter | kStyleAlignRight))) {
bt.flags = (TextStyleFlags)(bt.flags | kStyleAlignRight);
bt.xpos = _screenWidth - 1 - bt.xpos;
}
if (bt.flags & kStyleWordWrap) { if (bt.flags & kStyleWordWrap) {
bt.rect = _wrappedTextClipRect; bt.rect = _wrappedTextClipRect;

View file

@ -63,7 +63,8 @@ private:
const Common::Language _lang; const Common::Language _lang;
const byte _gameId; const byte _gameId;
const bool _useCJKMode; const bool _useCJKMode;
const bool _direction; const int _direction;
const int _rtlCenteredOffset;
const int _spacing; const int _spacing;
const byte _2byteCharWidth; const byte _2byteCharWidth;
const byte _lineBreakMarker; const byte _lineBreakMarker;

View file

@ -994,7 +994,15 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
else if (mode && vs->hicolor) else if (mode && vs->hicolor)
color = vs->hicolor; color = vs->hicolor;
TextStyleFlags flags = vs->center ? kStyleAlignCenter : kStyleAlignLeft; TextStyleFlags flags = kStyleAlignLeft;
int xpos = vs->origLeft;
if (vs->center) {
flags = kStyleAlignCenter;
} else if (_language == Common::HE_ISR) {
flags = kStyleAlignRight;
xpos = _screenWidth - 1 - vs->origLeft;
}
const byte *msg = getResourceAddress(rtVerb, verb); const byte *msg = getResourceAddress(rtVerb, verb);
if (!msg) if (!msg)
@ -1015,9 +1023,9 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
_charset->setCurID(vs->charset_nr); _charset->setCurID(vs->charset_nr);
// Compute the text rect // Compute the text rect
vs->oldRect = vs->curRect = _textV7->calcStringDimensions((const char*)msg, vs->origLeft, vs->curRect.top, vs->center ? kStyleAlignCenter : kStyleAlignLeft); vs->oldRect = vs->curRect = _textV7->calcStringDimensions((const char*)msg, xpos, vs->curRect.top, flags);
const int maxWidth = /*_language == Common::HE_ISR ? vs->curRect.right + 1 :*/ _screenWidth - vs->curRect.left; const int maxWidth = _screenWidth - vs->curRect.left;
int finalWidth = maxWidth; int finalWidth = maxWidth;
if (_game.version == 8 && _textV7->getStringWidth((const char*)buf) > maxWidth) { if (_game.version == 8 && _textV7->getStringWidth((const char*)buf) > maxWidth) {
@ -1036,30 +1044,12 @@ void ScummEngine_v7::drawVerb(int verb, int mode) {
--len; --len;
} }
enqueueText(tmpBuf, vs->origLeft, vs->curRect.top, color, vs->charset_nr, flags); enqueueText(tmpBuf, xpos, vs->curRect.top, color, vs->charset_nr, flags);
enqueueText(&msg[len + 1], vs->origLeft, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, flags); enqueueText(&msg[len + 1], xpos, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, flags);
vs->curRect.right = vs->curRect.left + finalWidth; vs->curRect.right = vs->curRect.left + finalWidth;
vs->curRect.bottom += _verbLineSpacing; vs->curRect.bottom += _verbLineSpacing;
/* } else {
int16 leftPos = vs->curRect.left; enqueueText(msg, xpos, vs->curRect.top, color, vs->charset_nr, flags);
if (_language == Common::HE_ISR)
vs->curRect.left = vs->origLeft = leftPos = vs->curRect.right - _charset->getStringWidth(0, tmpBuf);
else
vs->curRect.right = vs->curRect.left + _charset->getStringWidth(0, tmpBuf);
enqueueText(tmpBuf, leftPos, vs->curRect.top, color, vs->charset_nr, vs->center);
if (len >= 0) {
if (_language == Common::HE_ISR)
leftPos = vs->curRect.right - _charset->getStringWidth(0, &msg[len + 1]);
enqueueText(&msg[len + 1], leftPos, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, vs->center);
vs->curRect.bottom += _verbLineSpacing;
}*/
} else {/*
if (_language == Common::HE_ISR)
vs->curRect.left = vs->origLeft = vs->curRect.right - textWidth;
else
vs->curRect.right = vs->curRect.left + textWidth;
enqueueText(msg, vs->curRect.left, vs->curRect.top, color, vs->charset_nr, vs->center);*/
enqueueText(msg, vs->origLeft, vs->curRect.top, color, vs->charset_nr, flags);
} }
_charset->setCurID(oldID); _charset->setCurID(oldID);
} }