/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * */ #include "common/events.h" #include "common/keyboard.h" #include "common/file.h" #include "common/savefile.h" #include "common/config-manager.h" #include "base/plugins.h" #include "base/version.h" #include "graphics/cursorman.h" #include "sound/mixer.h" #include "toltecs/toltecs.h" #include "toltecs/palette.h" #include "toltecs/resource.h" #include "toltecs/screen.h" #include "toltecs/script.h" #include "toltecs/segmap.h" namespace Toltecs { Screen::Screen(ToltecsEngine *vm) : _vm(vm) { _frontScreen = new byte[268800]; _backScreen = new byte[870400]; memset(_fontResIndexArray, 0, sizeof(_fontResIndexArray)); _fontColor1 = 0; _fontColor2 = 0; // Screen shaking _shakeActive = false; _shakeCounterInit = 0; _shakeCounter = 0; _shakePos = 0; // Verb line _verbLineNum = 0; memset(_verbLineItems, 0, sizeof(_verbLineItems)); _verbLineX = 160; _verbLineY = 2; _verbLineWidth = 20; _verbLineCount = 0; // Talk text _talkTextItemNum = 0; memset(_talkTextItems, 0, sizeof(_talkTextItems)); _talkTextX = 0;//TODO correct init values _talkTextY = 0; _talkTextFontColor = 0; _talkTextMaxWidth = 520; } Screen::~Screen() { delete[] _frontScreen; delete[] _backScreen; } void Screen::unpackRle(byte *source, byte *dest, uint16 width, uint16 height) { int32 size = width * height; while (size > 0) { byte a = *source++; byte b = *source++; if (a == 0) { dest += b; size -= b; } else { b = ((b << 4) & 0xF0) | ((b >> 4) & 0x0F); memset(dest, b, a); dest += a; size -= a; } } } void Screen::loadMouseCursor(uint resIndex) { byte mouseCursor[16 * 16], *mouseCursorP = mouseCursor; byte *cursorData = _vm->_res->load(resIndex); for (int i = 0; i < 32; i++) { byte pixel; byte mask1 = *cursorData++; byte mask2 = *cursorData++; for (int j = 0; j < 8; j++) { pixel = 0xE5; if ((mask2 & 0x80) == 0) pixel = 0xE0; mask2 <<= 1; if ((mask1 & 0x80) == 0) pixel = 0; mask1 <<= 1; *mouseCursorP++ = pixel; } } //CursorMan.replaceCursor((const byte*)mouseCursor, 16, 16, 0, 0, 0); // FIXME: Where's the cursor hotspot? Using 8, 8 seems good enough for now. CursorMan.replaceCursor((const byte*)mouseCursor, 16, 16, 8, 8, 0); } void Screen::drawGuiImage(int16 x, int16 y, uint resIndex) { byte *imageData = _vm->_res->load(resIndex); int16 headerSize = READ_LE_UINT16(imageData); int16 width = imageData[2]; int16 height = imageData[3]; int16 workWidth = width, workHeight = height; imageData += headerSize; byte *dest = _frontScreen + x + (y + _vm->_cameraHeight) * 640; debug(0, "Screen::drawGuiImage() x = %d; y = %d; w = %d; h = %d; resIndex = %d", x, y, width, height, resIndex); //_vm->_arc->dump(resIndex, "gui"); while (workHeight > 0) { int count = 1; byte pixel = *imageData++; if (pixel & 0x80) { pixel &= 0x7F; count = *imageData++; count += 2; } pixel = pixel + 0xE0; while (count-- && workHeight > 0) { *dest++ = pixel; workWidth--; if (workWidth == 0) { workHeight--; dest += 640 - width; workWidth = width; } } } } void Screen::startShakeScreen(int16 shakeCounter) { _shakeActive = true; _shakeCounterInit = shakeCounter; _shakeCounter = shakeCounter; _shakePos = 0; } void Screen::stopShakeScreen() { _shakeActive = false; _vm->_system->setShakePos(0); } void Screen::updateShakeScreen() { if (_shakeActive) { _shakeCounter--; if (_shakeCounter == 0) { _shakeCounter = _shakeCounterInit; _shakePos ^= 8; _vm->_system->setShakePos(_shakePos); } } } void Screen::addStaticSprite(byte *spriteItem) { DrawRequest drawRequest; memset(&drawRequest, 0, sizeof(drawRequest)); drawRequest.y = READ_LE_UINT16(spriteItem + 0); drawRequest.x = READ_LE_UINT16(spriteItem + 2); int16 fragmentId = READ_LE_UINT16(spriteItem + 4); drawRequest.baseColor = _vm->_palette->findFragment(fragmentId) & 0xFF; drawRequest.resIndex = READ_LE_UINT16(spriteItem + 6); drawRequest.flags = READ_LE_UINT16(spriteItem + 8); drawRequest.scaling = 0; debug(0, "Screen::addStaticSprite() x = %d; y = %d; baseColor = %d; resIndex = %d; flags = %04X", drawRequest.x, drawRequest.y, drawRequest.baseColor, drawRequest.resIndex, drawRequest.flags); addDrawRequest(drawRequest); } void Screen::addAnimatedSprite(int16 x, int16 y, int16 fragmentId, byte *data, int16 *spriteArray, bool loop, int mode) { debug(0, "Screen::addAnimatedSprite(%d, %d, %d)", x, y, fragmentId); DrawRequest drawRequest; memset(&drawRequest, 0, sizeof(drawRequest)); drawRequest.x = x; drawRequest.y = y; drawRequest.baseColor = _vm->_palette->findFragment(fragmentId) & 0xFF; if (mode == 1) { drawRequest.scaling = _vm->_segmap->getScalingAtPoint(drawRequest.x, drawRequest.y); } else if (mode == 2) { drawRequest.scaling = 0; } int16 count = spriteArray[0]; debug(0, "count = %d", count); for (int16 index = 1; index <= count; index++) { byte *spriteItem = data + spriteArray[index]; uint16 loopNum = READ_LE_UINT16(spriteItem + 0) & 0x7FFF; uint16 loopCount = READ_LE_UINT16(spriteItem + 2); uint16 frameNum = READ_LE_UINT16(spriteItem + 4); uint16 frameCount = READ_LE_UINT16(spriteItem + 6); drawRequest.resIndex = READ_LE_UINT16(spriteItem + 8); drawRequest.flags = READ_LE_UINT16(spriteItem + 10 + loopNum * 2); debug(0, "Screen::addAnimatedSprite(%d of %d) loopNum = %d; loopCount = %d; frameNum = %d; frameCount = %d; resIndex = %d; flags = %04X, mode = %d", index, count, loopNum, loopCount, frameNum, frameCount, drawRequest.resIndex, drawRequest.flags, mode); addDrawRequest(drawRequest); frameNum++; if (frameNum == frameCount) { frameNum = 0; loopNum++; if (loopNum == loopCount) { if (loop) { loopNum = 0; } else { loopNum--; } } } else { loopNum |= 0x8000; } WRITE_LE_UINT16(spriteItem + 0, loopNum); WRITE_LE_UINT16(spriteItem + 4, frameNum); } } void Screen::clearSprites() { _spriteDrawList.clear(); // TODO } void Screen::addDrawRequest(const DrawRequest &drawRequest) { int16 scaleValueX, scaleValueY; int16 spriteDraw_X, spriteDraw_Y; byte *spriteData; int16 frameNum; SpriteDrawItem sprite; memset(&sprite, 0, sizeof(SpriteDrawItem)); if (drawRequest.flags == 0xFFFF) return; sprite.flags = 0; sprite.baseColor = drawRequest.baseColor; sprite.x = drawRequest.x; sprite.y = drawRequest.y; sprite.ybottom = drawRequest.y; sprite.resIndex = drawRequest.resIndex; spriteData = _vm->_res->load(drawRequest.resIndex); if (drawRequest.flags & 0x2000) { sprite.flags |= 0x10; } if (drawRequest.flags & 0x4000) { sprite.flags |= 0x40; } frameNum = drawRequest.flags & 0x0FFF; // First initialize the sprite item with the values from the sprite resource SpriteFrameEntry spriteFrameEntry(spriteData + frameNum * 12); if (spriteFrameEntry.w == 0 || spriteFrameEntry.h == 0) return; sprite.offset = spriteFrameEntry.offset; sprite.width = spriteFrameEntry.w; sprite.height = spriteFrameEntry.h; sprite.origWidth = spriteFrameEntry.w; sprite.origHeight = spriteFrameEntry.h; if (drawRequest.flags & 0x1000) { spriteDraw_X = spriteFrameEntry.w - spriteFrameEntry.x; } else { spriteDraw_X = spriteFrameEntry.x; } spriteDraw_Y = spriteFrameEntry.y; // If the sprite should be scaled we need to initialize some values now if (drawRequest.scaling != 0) { byte scaleValue = ABS(drawRequest.scaling); scaleValueX = scaleValue * sprite.origWidth; sprite.xdelta = (10000 * sprite.origWidth) / scaleValueX; scaleValueX /= 100; scaleValueY = scaleValue * sprite.origHeight; sprite.ydelta = (10000 * sprite.origHeight) / scaleValueY; scaleValueY /= 100; if (drawRequest.scaling > 0) { sprite.flags |= 2; sprite.width = sprite.origWidth + scaleValueX; sprite.height = sprite.origHeight + scaleValueY; spriteDraw_X += (spriteDraw_X * scaleValue) / 100; spriteDraw_Y += (spriteDraw_Y * scaleValue) / 100; } else { sprite.flags |= 1; sprite.width = sprite.origWidth - scaleValueX; sprite.height = sprite.origHeight - 1 - scaleValueY; if (sprite.width <= 0 || sprite.height <= 0) return; spriteDraw_X -= (spriteDraw_X * scaleValue) / 100; spriteDraw_Y -= (spriteDraw_Y * scaleValue) / 100; } } sprite.x -= spriteDraw_X; sprite.y -= spriteDraw_Y; sprite.yerror = sprite.ydelta; // Now we check if the sprite needs to be clipped // Clip Y if (sprite.y - _vm->_cameraY < 0) { int16 clipHeight = ABS(sprite.y - _vm->_cameraY); int16 chopHeight, skipHeight, lineWidth; byte *spriteFrameData; sprite.height -= clipHeight; if (sprite.height <= 0) return; sprite.y = _vm->_cameraY; // If the sprite is scaled if (sprite.flags & 3) { chopHeight = sprite.ydelta; skipHeight = clipHeight; if ((sprite.flags & 2) == 0) { do { chopHeight -= 100; if (chopHeight <= 0) { skipHeight++; chopHeight += sprite.ydelta; } else { clipHeight--; } } while (clipHeight > 0); } else { do { chopHeight -= 100; if (chopHeight < 0) { skipHeight--; chopHeight += sprite.ydelta + 100; } clipHeight--; } while (clipHeight > 0); } sprite.yerror = chopHeight; } spriteFrameData = spriteData + sprite.offset; // Now the sprite's offset is adjusted to point to the starting line if ((sprite.flags & 0x10) == 0) { while (clipHeight--) { lineWidth = 0; while (lineWidth _cameraY - _vm->_cameraHeight > 0) sprite.height -= sprite.y + sprite.height - _vm->_cameraY - _vm->_cameraHeight; if (sprite.height <= 0) return; sprite.value1 = 0; if (drawRequest.flags & 0x1000) { // Left border sprite.flags |= 4; if (sprite.x - _vm->_cameraX < 0) { sprite.width -= ABS(sprite.x - _vm->_cameraX); if (sprite.width <= 0) return; sprite.x = _vm->_cameraX; } // Right border if (sprite.x + sprite.width - _vm->_cameraX - 640 > 0) { sprite.flags |= 8; sprite.width -= sprite.x + sprite.width - _vm->_cameraX - 640; if (sprite.width <= 0) return; sprite.value1 = sprite.x + sprite.width - _vm->_cameraX - 640; } } else { // Left border if (sprite.x - _vm->_cameraX < 0) { sprite.flags |= 8; sprite.width -= ABS(sprite.x - _vm->_cameraX); if (sprite.width <= 0) return; sprite.value1 = ABS(sprite.x - _vm->_cameraX); sprite.x = _vm->_cameraX; } // Right border if (sprite.x + sprite.width - _vm->_cameraX - 640 > 0) { sprite.flags |= 8; sprite.width -= sprite.x + sprite.width - _vm->_cameraX - 640; if (sprite.width <= 0) return; } } // Add sprite sorted by priority Common::List::iterator iter = _spriteDrawList.begin(); while (iter != _spriteDrawList.end() && (*iter).ybottom <= sprite.ybottom) { iter++; } _spriteDrawList.insert(iter, sprite); } void Screen::drawSprite(SpriteDrawItem *sprite) { debug(0, "Screen::drawSprite() x = %d; y = %d; flags = %04X; resIndex = %d; offset = %08X; drawX = %d; drawY = %d", sprite->x, sprite->y, sprite->flags, sprite->resIndex, sprite->offset, sprite->x - _vm->_cameraX, sprite->y - _vm->_cameraY); debug(0, "Screen::drawSprite() width = %d; height = %d; origWidth = %d; origHeight = %d", sprite->width, sprite->height, sprite->origWidth, sprite->origHeight); byte *source = _vm->_res->load(sprite->resIndex) + sprite->offset; byte *dest = _frontScreen + (sprite->x - _vm->_cameraX) + (sprite->y - _vm->_cameraY) * 640; // FIXME: Temporary hack until proper clipping is implemented /* int16 dx = sprite->x - _vm->_cameraX, dy = sprite->y - _vm->_cameraY; if (dx < 0 || dy < 0 || dx + sprite->width >= 640 || dy + sprite->height >= 400) return; */ SpriteReader spriteReader(source, sprite); if (sprite->flags & 0x40) { // TODO: Shadow sprites } else if (sprite->flags & 0x10) { // 256 color sprite drawSpriteCore(dest, spriteReader, sprite); } else { // 16 color sprite if (sprite->flags & 1) { SpriteFilterScaleDown spriteScaler(sprite, &spriteReader); drawSpriteCore(dest, spriteScaler, sprite); } else if (sprite->flags & 2) { SpriteFilterScaleUp spriteScaler(sprite, &spriteReader); drawSpriteCore(dest, spriteScaler, sprite); } else { drawSpriteCore(dest, spriteReader, sprite); } } debug(0, "Screen::drawSprite() ok"); } void Screen::drawSpriteCore(byte *dest, SpriteFilter &reader, SpriteDrawItem *sprite) { int16 destInc; /* if ((sprite->flags & 8)) return; */ if (sprite->flags & 4) { destInc = -1; dest += sprite->width; } else { destInc = 1; } SpriteReaderStatus status; PixelPacket packet; byte *destp = dest; int16 skipX = sprite->value1; do { status = reader.readPacket(packet); if (skipX > 0) { while (skipX > 0) { skipX -= packet.count; if (skipX < 0) { packet.count = ABS(skipX); break; } status = reader.readPacket(packet); } } if (((sprite->flags & 0x10) && (packet.pixel != 0xFF)) || !(sprite->flags & 0x10) && (packet.pixel != 0)) { if (sprite->flags & 0x40) { } else if (sprite->flags & 0x10) { packet.pixel = ((packet.pixel << 4) & 0xF0) | ((packet.pixel >> 4) & 0x0F); } else { packet.pixel += sprite->baseColor - 1; } while (packet.count--) { *dest = packet.pixel; dest += destInc; } } else { dest += packet.count * destInc; } if (status == kSrsEndOfLine) { dest = destp + 640; destp = dest; skipX = sprite->value1; } } while (status != kSrsEndOfSprite); } void Screen::drawSprites() { for (Common::List::iterator iter = _spriteDrawList.begin(); iter != _spriteDrawList.end(); iter++) { SpriteDrawItem *sprite = &(*iter); drawSprite(sprite); _vm->_segmap->restoreMasksBySprite(sprite); } } void Screen::updateVerbLine(int16 slotIndex, int16 slotOffset) { debug(0, "Screen::updateVerbLine() _verbLineNum = %d; _verbLineX = %d; _verbLineY = %d; _verbLineWidth = %d; _verbLineCount = %d", _verbLineNum, _verbLineX, _verbLineY, _verbLineWidth, _verbLineCount); Font font(_vm->_res->load(_fontResIndexArray[0])); _verbLineItems[_verbLineNum].slotIndex = slotIndex; _verbLineItems[_verbLineNum].slotOffset = slotOffset; // First clear the line int16 y = _verbLineY; for (int16 i = 0; i < _verbLineCount; i++) { byte *dest = _frontScreen + _verbLineX - _verbLineWidth / 2 + (y - 1 + _vm->_cameraHeight) * 640; for (int16 j = 0; j < 20; j++) { memset(dest, 0xE0, _verbLineWidth); dest += 640; } y += 18; } int width = 0; byte *sourceString; byte *destString; byte len; _tempStringLen1 = 0; destString = _tempString; y = _verbLineY; memset(_tempString, 0, sizeof(_tempString)); for (int16 i = 0; i <= _verbLineNum; i++) { sourceString = _vm->_script->getSlotData(_verbLineItems[i].slotIndex) + _verbLineItems[i].slotOffset; preprocessText(_fontResIndexArray[0], _verbLineWidth, width, sourceString, destString, len); _tempStringLen1 += len; } if (_verbLineCount != 1) { int16 charWidth; if (*sourceString < 0xF0) { while (*sourceString > 0x20 && *sourceString < 0xF0 && len > 0/*CHECKME, len check added*/) { byte ch = *sourceString--; _tempStringLen1--; len--; charWidth = font.getCharWidth(ch) + font.getSpacing() - 1; width -= charWidth; } width += charWidth; sourceString++; _tempStringLen1 -= len; _tempStringLen2 = len + 1; drawString(_verbLineX - 1 - (width / 2), y, 0xF9, 0xFF, _fontResIndexArray[0]); destString = _tempString; width = 0; preprocessText(_fontResIndexArray[0], _verbLineWidth, width, sourceString, destString, len); _tempStringLen1 += len; y += 9; } y += 9; } _tempStringLen1 -= len; _tempStringLen2 = len; drawString(_verbLineX - 1 - (width / 2), y, 0xF9, 0xFF, _fontResIndexArray[0]); } void Screen::updateTalkText(int16 slotIndex, int16 slotOffset) { int16 x, y, maxWidth, width, length; byte durationModifier = 1; byte *textData = _vm->_script->getSlotData(slotIndex) + slotOffset; TalkTextItem *item = &_talkTextItems[_talkTextItemNum]; item->fontNum = 0; item->color = _talkTextFontColor; //debug(0, "## _talkTextMaxWidth = %d", _talkTextMaxWidth); x = CLIP(_talkTextX - _vm->_cameraX, 120, _talkTextMaxWidth); y = CLIP(_talkTextY - _vm->_cameraY, 4, _vm->_cameraHeight - 16); maxWidth = 624 - ABS(x - 320) * 2; while (1) { if (*textData == 0x0A) { x = CLIP(textData[3], 120, _talkTextMaxWidth); y = CLIP(READ_LE_UINT16(&textData[1]), 4, _vm->_cameraHeight - 16); maxWidth = 624 - ABS(x - 320) * 2; textData += 4; } else if (*textData == 0x14) { item->color = textData[1]; textData += 2; } else if (*textData == 0x19) { durationModifier = textData[1]; textData += 2; } else if (*textData < 0x0A) { item->fontNum = textData[1]; textData += 2; } else break; } item->slotIndex = slotIndex; item->slotOffset = textData - _vm->_script->getSlotData(slotIndex); width = 0; length = 0; item->rectCount = 0; Font font(_vm->_res->load(_fontResIndexArray[item->fontNum])); int16 wordLength, wordWidth; while (*textData < 0xF0) { if (*textData == 0x1E) { textData++; addTalkTextRect(font, x, y, length, width, item); // CHECKME? width = 0; length = 0; } else { wordLength = 0; wordWidth = 0; while (*textData >= 0x20 && *textData < 0xF0) { byte ch = *textData++; wordLength++; if (ch == 0x20) { wordWidth += font.getWidth(); break; } else { wordWidth += font.getCharWidth(ch) + font.getSpacing() - 1; } } debug(0, "## width = %d; wordWidth = %d; width + wordWidth = %d; maxWidth + font.getWidth() = %d", width, wordWidth, width + wordWidth, maxWidth + font.getWidth()); if (width + wordWidth > maxWidth + font.getWidth()) { addTalkTextRect(font, x, y, length, width, item); width = wordWidth; length = wordLength; } else { width += wordWidth; length += wordLength; } } } addTalkTextRect(font, x, y, length, width, item); debug(0, "## item->rectCount = %d", item->rectCount); int16 textDurationMultiplier = item->duration + 8; // TODO: Check sound/text flags if (*textData == 0xFE) { //textDurationMultiplier += 100; } item->duration = 4 * textDurationMultiplier * durationModifier; } void Screen::addTalkTextRect(Font &font, int16 x, int16 &y, int16 length, int16 width, TalkTextItem *item) { if (width > 0) { TextRect *textRect = &item->rects[item->rectCount]; width = width + 1 - font.getSpacing(); textRect->width = width; item->duration += length; textRect->length = length; textRect->y = y; textRect->x = CLIP(x - width / 2, 0, 640); item->rectCount++; } y += font.getHeight() - 1; } void Screen::drawTalkTextItems() { //debug(0, "## _talkTextItemNum = %d", _talkTextItemNum); for (int16 i = 0; i <= _talkTextItemNum; i++) { TalkTextItem *item = &_talkTextItems[i]; byte *text = _vm->_script->getSlotData(item->slotIndex) + item->slotOffset; if (item->fontNum == -1 || item->duration == 0) continue; item->duration -= _vm->_counter01; if (item->duration < 0) item->duration = 0; Font font(_vm->_res->load(_fontResIndexArray[item->fontNum])); for (byte j = 0; j < item->rectCount; j++) { int16 x = item->rects[j].x; for (byte pos = 0; pos < item->rects[j].length; pos++) { byte ch = *text++; if (ch < 0x20) continue; if (ch == 0x20) { x += font.getWidth(); } else { drawChar2(font, _frontScreen, x, item->rects[j].y, ch, item->color); x += font.getCharWidth(ch) + font.getSpacing() - 1; } } } } } int16 Screen::getTalkTextDuration() { return _talkTextItems[_talkTextItemNum].duration; } void Screen::registerFont(uint fontIndex, uint resIndex) { _fontResIndexArray[fontIndex] = resIndex; } void Screen::printText(byte *textData) { int16 x = 0, y = 0; // Really strange stuff. for (int i = 30; i >= 0; i--) { if (textData[i] >= 0xF0) break; if (i == 0) return; } do { if (*textData == 0x0A) { // Set text position y = textData[1]; x = READ_LE_UINT32(textData + 2); textData += 4; } else if (*textData == 0x0B) { // Inc text position y += textData[1]; // CHECKME: Maybe these are signed? x += textData[2]; textData += 3; } else { byte *destString = _tempString; int width = 0; _tempStringLen1 = 0; preprocessText(_fontResIndexArray[1], 640, width, textData, destString, _tempStringLen2); drawString(x - width / 2, y, _fontColor1, _fontColor2, _fontResIndexArray[1]); } } while (*textData != 0xFF); } void Screen::preprocessText(uint fontResIndex, int maxWidth, int &width, byte *&sourceString, byte *&destString, byte &len) { Font font(_vm->_res->load(fontResIndex)); len = 0; while (*sourceString >= 0x20 && *sourceString < 0xF0) { byte ch = *sourceString; byte charWidth; if (ch <= 0x20) charWidth = font.getWidth(); else charWidth = font.getCharWidth(ch) + font.getSpacing() - 1; if (width + charWidth >= maxWidth) break; len++; width += charWidth; *destString++ = *sourceString++; } } void Screen::drawString(int16 x, int16 y, byte fontColor1, byte fontColor2, uint fontResIndex) { debug(0, "Screen::drawString(%d, %d, %d, %d, %d) _tempStringLen1 = %d; _tempStringLen2 = %d", x, y, fontColor1, fontColor2, fontResIndex, _tempStringLen1, _tempStringLen2); Font font(_vm->_res->load(fontResIndex)); byte color = fontColor1; byte *text = _tempString; byte len = _tempStringLen1 + _tempStringLen2; int16 yadd = 1; for (byte pos = 0; pos < len; pos++) { if (pos == _tempStringLen1) { color = fontColor2; } byte ch = *text++; if (ch <= 0x20) { x += font.getWidth(); } else { drawChar(font, _frontScreen, x + 1, y + _vm->_cameraHeight - yadd, ch, color); x += font.getCharWidth(ch) + font.getSpacing() - 1; yadd = -yadd; } } } // TODO: Merge drawChar and drawChar2 void Screen::drawChar(const Font &font, byte *dest, int16 x, int16 y, byte ch, byte color) { int16 charWidth, charHeight; byte *charData; dest += x + (y * 640); charWidth = font.getCharWidth(ch); charHeight = font.getHeight() - 2; charData = font.getCharData(ch); while (charHeight--) { byte lineWidth = charWidth; while (lineWidth > 0) { byte count = charData[0] & 0x0F; byte flags = charData[0] & 0xF0; charData++; lineWidth -= count; if (!(flags & 0x80) && (flags & 0x10)) { memset(dest, color, count); } dest += count; } dest += 640 - charWidth; } } void Screen::drawChar2(const Font &font, byte *dest, int16 x, int16 y, byte ch, byte color) { int16 charWidth, charHeight; byte *charData; dest += x + (y * 640); charWidth = font.getCharWidth(ch); charHeight = font.getHeight() - 2; charData = font.getCharData(ch); while (charHeight--) { byte lineWidth = charWidth; while (lineWidth > 0) { byte count = charData[0] & 0x0F; byte flags = charData[0] & 0xF0; charData++; lineWidth -= count; if ((flags & 0x80) == 0) { if ((flags & 0x10) == 0) { memset(dest, 0, count); } else { memset(dest, color, count); } } dest += count; } dest += 640 - charWidth; } } void Screen::saveState(Common::WriteStream *out) { for (int i = 0; i < 8; i++) { out->writeUint16LE(_verbLineItems[i].slotIndex); out->writeUint16LE(_verbLineItems[i].slotOffset); } } void Screen::loadState(Common::ReadStream *in) { } /* void Screen::update() { } */ } // End of namespace Toltecs