/* ScummVM - Scumm Interpreter * Copyright (C) 2004-2006 The ScummVM project * * 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. * * $Header$ * */ #include "common/stdafx.h" #include "common/system.h" #include "kyra/screen.h" #include "kyra/kyra.h" namespace Kyra { #define BITBLIT_RECTS 10 Screen::Screen(KyraEngine *vm, OSystem *system) : _system(system), _vm(vm) { _curPage = 0; for (int pageNum = 0; pageNum < SCREEN_PAGE_NUM; pageNum += 2) { uint8 *pagePtr = (uint8 *)malloc(SCREEN_PAGE_SIZE); if (pagePtr) { memset(pagePtr, 0, SCREEN_PAGE_SIZE); _pagePtrs[pageNum] = _pagePtrs[pageNum + 1] = pagePtr; } } memset(_shapePages, 0, sizeof(_shapePages)); _currentPalette = (uint8 *)malloc(768); if (_currentPalette) { memset(_currentPalette, 0, 768); } _screenPalette = (uint8 *)malloc(768); if (_screenPalette) { memset(_screenPalette, 0, 768); } for (int i = 0; i < 3; ++i) { _palettes[i] = (uint8 *)malloc(768); if (_palettes[i]) { memset(_palettes[i], 0, 768); } } _curDim = &_screenDimTable[0]; _charWidth = 0; _charOffset = 0; memset(_fonts, 0, sizeof(_fonts)); for (int i = 0; i < ARRAYSIZE(_textColorsMap); ++i) { _textColorsMap[i] = i; } _decodeShapeBuffer = NULL; _decodeShapeBufferSize = 0; _animBlockPtr = NULL; _animBlockSize = 0; _mouseLockCount = 0; _bitBlitRects = new Rect[BITBLIT_RECTS]; assert(_bitBlitRects); memset(_bitBlitRects, 0, sizeof(Rect)*BITBLIT_RECTS); _bitBlitNum = 0; memset(_saveLoadPage, 0, sizeof(_saveLoadPage)); } Screen::~Screen() { for (int pageNum = 0; pageNum < SCREEN_PAGE_NUM; pageNum += 2) { free(_pagePtrs[pageNum]); _pagePtrs[pageNum] = _pagePtrs[pageNum + 1] = 0; } for (int f = 0; f < ARRAYSIZE(_fonts); ++f) { delete[] _fonts[f].fontData; _fonts[f].fontData = NULL; } free(_currentPalette); free(_screenPalette); free(_decodeShapeBuffer); free(_animBlockPtr); for (int i = 0; i < 3; ++i) { free(_palettes[i]); } delete [] _bitBlitRects; for (int i = 0; i < ARRAYSIZE(_saveLoadPage); ++i) { delete [] _saveLoadPage[i]; _saveLoadPage[i] = 0; } } void Screen::updateScreen() { debug(9, "Screen::updateScreen()"); _system->copyRectToScreen(getPagePtr(0), SCREEN_W, 0, 0, SCREEN_W, SCREEN_H); _system->updateScreen(); } uint8 *Screen::getPagePtr(int pageNum) { debug(9, "Screen::getPagePtr(%d)", pageNum); assert(pageNum < SCREEN_PAGE_NUM); return _pagePtrs[pageNum]; } void Screen::clearPage(int pageNum) { debug(9, "Screen::clearPage(%d)", pageNum); assert(pageNum < SCREEN_PAGE_NUM); memset(getPagePtr(pageNum), 0, SCREEN_PAGE_SIZE); } int Screen::setCurPage(int pageNum) { debug(9, "Screen::setCurPage(%d)", pageNum); assert(pageNum < SCREEN_PAGE_NUM); int previousPage = _curPage; _curPage = pageNum; return previousPage; } void Screen::clearCurPage() { debug(9, "Screen::clearCurPage()"); memset(getPagePtr(_curPage), 0, SCREEN_PAGE_SIZE); } uint8 Screen::getPagePixel(int pageNum, int x, int y) { debug(9, "Screen::getPagePixel(%d, %d, %d)", pageNum, x, y); assert(pageNum < SCREEN_PAGE_NUM); assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H); return _pagePtrs[pageNum][y * SCREEN_W + x]; } void Screen::setPagePixel(int pageNum, int x, int y, uint8 color) { debug(9, "Screen::setPagePixel(%d, %d, %d, %d)", pageNum, x, y, color); assert(pageNum < SCREEN_PAGE_NUM); assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H); _pagePtrs[pageNum][y * SCREEN_W + x] = color; } void Screen::fadeFromBlack() { debug(9, "Screen::fadeFromBlack()"); fadePalette(_currentPalette, 0x54); } void Screen::fadeToBlack() { debug(9, "Screen::fadeToBlack()"); uint8 blackPal[768]; memset(blackPal, 0, 768); fadePalette(blackPal, 0x54); } void Screen::fadeSpecialPalette(int palIndex, int startIndex, int size, int fadeTime) { debug(9, "fadeSpecialPalette(%d, %d, %d, %d)", palIndex, startIndex, size, fadeTime); assert(_vm->palTable1()[palIndex]); assert(_currentPalette); uint8 tempPal[768]; memcpy(tempPal, _currentPalette, 768); memcpy(&tempPal[startIndex*3], _vm->palTable1()[palIndex], size*3); fadePalette(tempPal, fadeTime*18); memcpy(&_currentPalette[startIndex*3], &tempPal[startIndex*3], size*3); setScreenPalette(_currentPalette); _system->updateScreen(); } void Screen::fadePalette(const uint8 *palData, int delay) { debug(9, "Screen::fadePalette(0x%X, %d)", palData, delay); uint8 fadePal[768]; memcpy(fadePal, _screenPalette, 768); uint8 diff, maxDiff = 0; for (int i = 0; i < 768; ++i) { diff = ABS(palData[i] - fadePal[i]); if (diff > maxDiff) { maxDiff = diff; } } int16 delayInc = delay << 8; if (maxDiff != 0) { delayInc /= maxDiff; } delay = delayInc; for (diff = 1; diff <= maxDiff; ++diff) { if (delayInc >= 512) { break; } delayInc += delay; } int delayAcc = 0; while (1) { delayAcc += delayInc; bool needRefresh = false; for (int i = 0; i < 768; ++i) { int c1 = palData[i]; int c2 = fadePal[i]; if (c1 != c2) { needRefresh = true; if (c1 > c2) { c2 += diff; if (c1 < c2) { c2 = c1; } } if (c1 < c2) { c2 -= diff; if (c1 > c2) { c2 = c1; } } fadePal[i] = (uint8)c2; } } if (!needRefresh) { break; } setScreenPalette(fadePal); _system->updateScreen(); _system->delayMillis((delayAcc >> 8) * 1000 / 60); delayAcc &= 0xFF; } } void Screen::setScreenPalette(const uint8 *palData) { debug(9, "Screen::setScreenPalette(0x%X)", palData); memcpy(_screenPalette, palData, 768); uint8 screenPal[256 * 4]; for (int i = 0; i < 256; ++i) { screenPal[4 * i + 0] = (palData[0] << 2) | (palData[0] & 3); screenPal[4 * i + 1] = (palData[1] << 2) | (palData[1] & 3); screenPal[4 * i + 2] = (palData[2] << 2) | (palData[2] & 3); screenPal[4 * i + 3] = 0; palData += 3; } _system->setPalette(screenPal, 0, 256); } void Screen::copyToPage0(int y, int h, uint8 page, uint8 *seqBuf) { debug(9, "Screen::copyToPage0(%d, %d, %d, 0x%X)", y, h, page, seqBuf); assert(y + h <= SCREEN_H); const uint8 *src = getPagePtr(page) + y * SCREEN_W; uint8 *dstPage = getPagePtr(0) + y * SCREEN_W; for (int i = 0; i < h; ++i) { for (int x = 0; x < SCREEN_W; ++x) { if (seqBuf[x] != src[x]) { seqBuf[x] = src[x]; dstPage[x] = src[x]; } } src += SCREEN_W; seqBuf += SCREEN_W; dstPage += SCREEN_W; } } void Screen::copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage, int flags) { debug(9, "Screen::copyRegion(%d, %d, %d, %d, %d, %d, %d, %d, %d)", x1, y1, x2, y2, w, h, srcPage, dstPage, flags); if (flags & CR_CLIPPED) { if (x2 < 0) { if (x2 <= -w) return; w += x2; x1 -= x2; x2 = 0; } else if (x2 + w >= SCREEN_W) { if (x2 > SCREEN_W) return; w = SCREEN_W - x2; } if (y2 < 0) { if (y2 <= -h ) return; h += y2; y1 -= y2; y2 = 0; } else if (y2 + h >= SCREEN_H) { if (y2 > SCREEN_H) return; h = SCREEN_H - y2; } } assert(x1 + w <= SCREEN_W && y1 + h <= SCREEN_H); const uint8 *src = getPagePtr(srcPage) + y1 * SCREEN_W + x1; assert(x2 + w <= SCREEN_W && y2 + h <= SCREEN_H); uint8 *dst = getPagePtr(dstPage) + y2 * SCREEN_W + x2; if (flags & CR_X_FLIPPED) { while (h--) { for (int i = 0; i < w; ++i) { if (src[i]) { dst[w-i] = src[i]; } } src += SCREEN_W; dst += SCREEN_W; } } else { while (h--) { for (int i = 0; i < w; ++i) { if (src[i]) { dst[i] = src[i]; } } src += SCREEN_W; dst += SCREEN_W; } } } void Screen::copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 *dest) { debug(9, "Screen::copyRegionToBuffer(%d, %d, %d, %d, %d)", pageNum, x, y, w, h); assert(x >= 0 && x < Screen::SCREEN_W && y >= 0 && y < Screen::SCREEN_H && dest); uint8 *pagePtr = getPagePtr(pageNum); for (int i = y; i < y + h; i++) { memcpy(dest + (i - y) * w, pagePtr + i * SCREEN_W + x, w); } } void Screen::copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint8 *src) { debug(9, "Screen::copyBlockToPage(%d, %d, %d, %d, %d, 0x%X)", pageNum, x, y, w, h, src); assert(x >= 0 && x < Screen::SCREEN_W && y >= 0 && y < Screen::SCREEN_H); uint8 *dst = getPagePtr(pageNum) + y * SCREEN_W + x; while (h--) { memcpy(dst, src, w); dst += SCREEN_W; src += w; } } void Screen::copyFromCurPageBlock(int x, int y, int w, int h, const uint8 *src) { debug(9, "Screen::copyFromCurPageBlock(%d, %d, %d, %d, 0x%X)", x, y, w, h, src); if (x < 0) { x = 0; } else if (x >= 40) { return; } if (x + w > 40) { w = 40 - x; } if (y < 0) { y = 0; } else if (y >= 200) { return; } if (y + h > 200) { h = 200 - y; } uint8 *dst = getPagePtr(_curPage) + y * SCREEN_W + x * 8; while (h--) { memcpy(dst, src, w*8); dst += SCREEN_W; src += w*8; } } void Screen::copyCurPageBlock(int x, int y, int w, int h, uint8 *dst) { debug(9, "Screen::copyCurPageBlock(%d, %d, %d, %d, 0x%X)", x, y, w, h, dst); assert(dst); if (x < 0) { x = 0; } else if (x >= 40) { return; } if (x + w > 40) { w = 40 - x; } if (y < 0) { y = 0; } else if (y >= 200) { return; } if (y + h > 200) { h = 200 - y; } const uint8 *src = getPagePtr(_curPage) + y * SCREEN_W + x * 8; while (h--) { memcpy(dst, src, w*8); dst += w*8; src += SCREEN_W; } } void Screen::shuffleScreen(int sx, int sy, int w, int h, int srcPage, int dstPage, int ticks, bool transparent) { debug(9, "Screen::shuffleScreen(%d, %d, %d, %d, %d, %d, %d, %d)", sx, sy, w, h, srcPage, dstPage, ticks, transparent); assert(sx >= 0 && w <= SCREEN_W); int x; uint16 x_offs[SCREEN_W]; for (x = 0; x < SCREEN_W; ++x) { x_offs[x] = x; } for (x = 0; x < w; ++x) { int i = _vm->_rnd.getRandomNumber(w - 1); SWAP(x_offs[x], x_offs[i]); } assert(sy >= 0 && h <= SCREEN_H); int y; uint8 y_offs[SCREEN_H]; for (y = 0; y < SCREEN_H; ++y) { y_offs[y] = y; } for (y = 0; y < h; ++y) { int i = _vm->_rnd.getRandomNumber(h - 1); SWAP(y_offs[y], y_offs[i]); } for (y = 0; y < h; ++y) { int y_cur = y; for (x = 0; x < w; ++x) { int i = sx + x_offs[x]; int j = sy + y_offs[y_cur]; ++y_cur; if (y_cur >= h) { y_cur = 0; } uint8 color = getPagePixel(srcPage, i, j); if (!transparent || color != 0) { setPagePixel(dstPage, i, j, color); } } updateScreen(); _system->delayMillis(ticks * 1000 / 60); } } void Screen::fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum) { debug(9, "Screen::fillRect(%d, %d, %d, %d, %d, %d)", x1, y1, x2, y2, color, pageNum); assert(x2 < SCREEN_W && y2 < SCREEN_H); if (pageNum == -1) { pageNum = _curPage; } uint8 *dst = getPagePtr(pageNum) + y1 * SCREEN_W + x1; for (; y1 <= y2; ++y1) { memset(dst, color, x2 - x1 + 1); dst += SCREEN_W; } } void Screen::drawBox(int x1, int y1, int x2, int y2, int color1, int color2) { debug(9, "Screen::drawBox(%i, %i, %i, %i, %i, %i)", x1, y1, x2, y2, color1, color2); //if (_menuUnk1 == 0) //return; hideMouse(); fillRect(x1, y1, x2, y1 + 1, color1); fillRect(x2 - 1, y1, x2, y2, color1); drawClippedLine(x1, y1, x1, y2, color2); drawClippedLine(x1 + 1, y1 + 1, x1 + 1, y2 - 1, color2); drawClippedLine(x1, y2, x2, y2, color2); drawClippedLine(x1, y2 - 1, x2 - 1, y2 - 1, color2); showMouse(); } void Screen::drawClippedLine(int x1, int y1, int x2, int y2, int color) { debug(9, "Screen::drawClippedLine(%i, %i, %i, %i, %i)", x1, y1, x2, y2, color); if (x1 < 0) x1 = 0; else if (x1 > 319) x1 = 319; if (x2 < 0) x2 = 0; else if (x2 > 319) x2 = 319; if (y1 < 0) y1 = 0; else if (y1 > 199) y1 = 199; if (y2 < 0) y2 = 0; else if (y2 > 199) y2 = 199; if (x1 == x2) if (y1 > y2) drawLine(true, x1, y2, y1 - y2 + 1, color); else drawLine(true, x1, y1, y2 - y1 + 1, color); else if (x1 > x2) drawLine(false, x2, y1, x1 - x2 + 1, color); else drawLine(false, x1, y1, x2 - x1 + 1, color); } void Screen::drawLine(bool horizontal, int x, int y, int length, int color) { debug(9, "Screen::drawLine(%i, %i, %i, %i, %i)", horizontal, x, y, length, color); uint8 *ptr = getPagePtr(_curPage) + y * SCREEN_W + x; if (horizontal) { assert((y + length) <= SCREEN_H); int currLine = 0; while (currLine < length) { *ptr = color; ptr += SCREEN_W; currLine++; } } else { assert((x + length) <= SCREEN_W); memset(ptr, color, length); } } void Screen::setAnimBlockPtr(int size) { debug(9, "Screen::setAnimBlockPtr(%d)", size); free(_animBlockPtr); _animBlockPtr = (uint8 *)malloc(size); assert(_animBlockPtr); memset(_animBlockPtr, 0, size); _animBlockSize = size; } void Screen::setTextColorMap(const uint8 *cmap) { debug(9, "Screen::setTextColorMap(0x%X)", cmap); setTextColor(cmap, 0, 11); } void Screen::setTextColor(const uint8 *cmap, int a, int b) { debug(9, "Screen::setTextColor(0x%X, %d, %d)", cmap, a, b); for (int i = a; i <= b; ++i) { _textColorsMap[i] = *cmap++; } } void Screen::loadFont(FontId fontId, uint8 *fontData) { debug(9, "Screen::loadFont(%d, 0x%X)", fontId, fontData); Font *fnt = &_fonts[fontId]; assert(fontData && !fnt->fontData); fnt->fontData = fontData; uint16 fontSig = READ_LE_UINT16(fontData + 2); if (fontSig != 0x500) { error("Invalid font data"); } fnt->charWidthTable = fontData + READ_LE_UINT16(fontData + 8); fnt->charBoxHeight = READ_LE_UINT16(fontData + 4); fnt->charBitmapOffset = READ_LE_UINT16(fontData + 6); fnt->charWidthTableOffset = READ_LE_UINT16(fontData + 8); fnt->charHeightTableOffset = READ_LE_UINT16(fontData + 0xC); } Screen::FontId Screen::setFont(FontId fontId) { debug(9, "Screen::setFont(%d)", fontId); FontId prev = _currentFont; _currentFont = fontId; return prev; } int Screen::getCharWidth(uint8 c) const { debug(9, "Screen::getCharWidth('%c')", c); return (int)_fonts[_currentFont].charWidthTable[c] + _charWidth; } int Screen::getTextWidth(const char *str) const { debug(9, "Screen::getTextWidth('%s')", str); int curLineLen = 0; int maxLineLen = 0; while (1) { char c = *str++; if (c == 0) { break; } else if (c == '\r') { if (curLineLen > maxLineLen) { maxLineLen = curLineLen; } else { curLineLen = 0; } } else { curLineLen += getCharWidth(c); } } return MAX(curLineLen, maxLineLen); } void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2) { debug(9, "Screen::printText('%s', %d, %d, 0x%X, 0x%X)", str, x, y, color1, color2); uint8 cmap[2]; cmap[0] = color2; cmap[1] = color1; setTextColor(cmap, 0, 1); Font *fnt = &_fonts[_currentFont]; uint8 charHeight = *(fnt->fontData + fnt->charBoxHeight + 4); if (x < 0) { x = 0; } else if (x >= SCREEN_W) { return; } int x_start = x; if (y < 0) { y = 0; } else if (y >= SCREEN_H) { return; } while (1) { char c = *str++; if (c == 0) { break; } else if (c == '\r') { x = x_start; y += charHeight + _charOffset; } else { int charWidth = getCharWidth(c); if (x + charWidth > SCREEN_W) { x = x_start; y += charHeight + _charOffset; if (y >= SCREEN_H) { break; } } drawChar(c, x, y); x += charWidth; } } } void Screen::drawChar(uint8 c, int x, int y) { debug(9, "Screen::drawChar('%c', %d, %d)", c, x, y); Font *fnt = &_fonts[_currentFont]; uint8 *dst = getPagePtr(_curPage) + y * SCREEN_W + x; uint16 bitmapOffset = READ_LE_UINT16(fnt->fontData + fnt->charBitmapOffset + c * 2); if (bitmapOffset == 0) { return; } uint8 charWidth = *(fnt->fontData + fnt->charWidthTableOffset + c); if (charWidth + x > SCREEN_W) { return; } uint8 charH0 = *(fnt->fontData + fnt->charBoxHeight + 4); if (charH0 + y > SCREEN_H) { return; } uint8 charH1 = *(fnt->fontData + fnt->charHeightTableOffset + c * 2); uint8 charH2 = *(fnt->fontData + fnt->charHeightTableOffset + c * 2 + 1); charH0 -= charH1 + charH2; const uint8 *src = fnt->fontData + bitmapOffset; const int pitch = SCREEN_W - charWidth; while (charH1--) { uint8 col = _textColorsMap[0]; for (int i = 0; i < charWidth; ++i) { if (col != 0) { *dst = col; } ++dst; } dst += pitch; } while (charH2--) { uint8 b = 0; for (int i = 0; i < charWidth; ++i) { uint8 col; if (i & 1) { col = _textColorsMap[b >> 4]; } else { b = *src++; col = _textColorsMap[b & 0xF]; } if (col != 0) { *dst = col; } ++dst; } dst += pitch; } while (charH0--) { uint8 col = _textColorsMap[0]; for (int i = 0; i < charWidth; ++i) { if (col != 0) { *dst = col; } ++dst; } dst += pitch; } } void Screen::setScreenDim(int dim) { debug(9, "setScreenDim(%d)", dim); assert(dim < _screenDimTableCount); _curDim = &_screenDimTable[dim]; // XXX } void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...) { debug(9, "Screen::drawShape(%d, 0x%X, %d, %d, %d, 0x%.04X, ...)", pageNum, shapeData, x, y, sd, flags); if (!shapeData) return; va_list args; va_start(args, flags); static int drawShapeVar1 = 0; static int drawShapeVar2[] = { 1, 3, 2, 5, 4, 3, 2, 1 }; static int drawShapeVar3 = 1; static int drawShapeVar4 = 0; static int drawShapeVar5 = 0; uint8 *table = 0; int tableLoopCount = 0; int drawLayer = 0; uint8 *table2 = 0; uint8 *table3 = 0; uint8 *table4 = 0; if (flags & 0x8000) { table2 = va_arg(args, uint8*); } if (flags & 0x100) { table = va_arg(args, uint8*); tableLoopCount = va_arg(args, int); if (!tableLoopCount) flags &= 0xFFFFFEFF; } if (flags & 0x1000) { table3 = va_arg(args, uint8*); table4 = va_arg(args, uint8*); } if (flags & 0x200) { drawShapeVar1 += 1; drawShapeVar1 &= 7; drawShapeVar3 = drawShapeVar2[drawShapeVar1]; drawShapeVar4 = 0; drawShapeVar5 = 256; } if (flags & 0x4000) { drawShapeVar5 = va_arg(args, int); } if (flags & 0x800) { drawLayer = va_arg(args, int); } int scale_w, scale_h; if (flags & DSF_SCALE) { scale_w = va_arg(args, int); scale_h = va_arg(args, int); } else { scale_w = 0x100; scale_h = 0x100; } int ppc = (flags >> 8) & 0x3F; const uint8 *src = shapeData; if (_vm->features() & GF_TALKIE) { src += 2; } uint16 shapeFlags = READ_LE_UINT16(src); src += 2; int shapeHeight = *src++; int scaledShapeHeight = (shapeHeight * scale_h) >> 8; if (scaledShapeHeight == 0) { va_end(args); return; } int shapeWidth = READ_LE_UINT16(src); src += 2; int scaledShapeWidth = (shapeWidth * scale_w) >> 8; if (scaledShapeWidth == 0) { va_end(args); return; } if (flags & DSF_CENTER) { x -= scaledShapeWidth >> 1; y -= scaledShapeHeight >> 1; } src += 3; uint16 frameSize = READ_LE_UINT16(src); src += 2; if ((shapeFlags & 1) || (flags & 0x400)) { src += 0x10; } if (!(shapeFlags & 2)) { decodeFrame4(src, _animBlockPtr, frameSize); src = _animBlockPtr; } int shapeSize = shapeWidth * shapeHeight; if (_decodeShapeBufferSize < shapeSize) { free(_decodeShapeBuffer); _decodeShapeBuffer = (uint8 *)malloc(shapeSize); _decodeShapeBufferSize = shapeSize; } if (!_decodeShapeBuffer) { _decodeShapeBufferSize = 0; va_end(args); return; } memset(_decodeShapeBuffer, 0, _decodeShapeBufferSize); uint8 *decodedShapeFrame = _decodeShapeBuffer; // only used if shapeFlag & 1 is NOT zero const uint8 *colorTable = shapeData + 10; if (_vm->features() & GF_TALKIE) { colorTable += 2; } for (int j = 0; j < shapeHeight; ++j) { uint8 *dsbNextLine = decodedShapeFrame + shapeWidth; int count = shapeWidth; while (count > 0) { uint8 code = *src++; if (code != 0) { // this is guessed if (shapeFlags & 1) { if (code < 16) { *decodedShapeFrame++ = colorTable[code]; } } else { *decodedShapeFrame++ = code; } --count; } else { code = *src++; decodedShapeFrame += code; count -= code; } } decodedShapeFrame = dsbNextLine; } uint16 sx1 = _screenDimTable[sd].sx * 8; uint16 sy1 = _screenDimTable[sd].sy; uint16 sx2 = sx1 + _screenDimTable[sd].w * 8; uint16 sy2 = sy1 + _screenDimTable[sd].h; if (flags & DSF_WND_COORDS) { x += sx1; y += sy1; } int x1, x2; if (x >= 0) { x1 = 0; if (x + scaledShapeWidth < sx2) { x2 = scaledShapeWidth; } else { x2 = sx2 - x; } } else { x2 = scaledShapeWidth; x1 = -x; x = 0; if (x2 > sx2) { x2 = sx2; } } int y1, y2; if (y >= 0) { y1 = 0; if (y + scaledShapeHeight < sy2) { y2 = scaledShapeHeight; } else { y2 = sy2 - y; } } else { y2 = scaledShapeHeight; y1 = -y; y = 0; if (y2 > sy2) { y2 = sy2; } } uint8 *dst = getPagePtr(pageNum) + y * SCREEN_W + x; uint8 *dstStart = getPagePtr(pageNum); int scaleYTable[SCREEN_H]; assert(y1 >= 0 && y2 < SCREEN_H); for (y = y1; y < y2; ++y) { scaleYTable[y] = (y << 8) / scale_h; } int scaleXTable[SCREEN_W]; assert(x1 >= 0 && x2 < SCREEN_W); for (x = x1; x < x2; ++x) { scaleXTable[x] = (x << 8) / scale_w; } const uint8 *shapeBuffer = _decodeShapeBuffer; if (flags & DSF_Y_FLIPPED) { shapeBuffer += shapeWidth * (shapeHeight - 1); } if (flags & DSF_X_FLIPPED) { shapeBuffer += shapeWidth - 1; } for (y = y1; y < y2; ++y) { uint8 *dstNextLine = dst + SCREEN_W; int j = scaleYTable[y]; if (flags & DSF_Y_FLIPPED) { j = -j; } for (x = x1; x < x2; ++x) { int xpos = scaleXTable[x]; if (flags & DSF_X_FLIPPED) { xpos = -xpos; } uint8 color = shapeBuffer[j * shapeWidth + xpos]; if (color != 0) { switch (ppc) { case 0: *dst = color; break; case 1: for (int i = 0; i < tableLoopCount; ++i) { color = table[color]; } break; case 2: { int temp = drawShapeVar4 + drawShapeVar5; if (temp & 0xFF00) { drawShapeVar4 = temp & 0xFF; dst += drawShapeVar3; color = *dst; dst -= drawShapeVar3; } else { drawShapeVar4 = temp; } } break; case 7: case 3: color = *dst; for (int i = 0; i < tableLoopCount; ++i) { color = table[color]; } break; case 4: color = table2[color]; break; case 5: color = table2[color]; for (int i = 0; i < tableLoopCount; ++i) { color = table[color]; } break; case 6: { int temp = drawShapeVar4 + drawShapeVar5; if (temp & 0xFF00) { drawShapeVar4 = temp & 0xFF; dst += drawShapeVar3; color = *dst; dst -= drawShapeVar3; } else { drawShapeVar4 = temp; color = table2[color]; } } break; case 8: { int offset = dst - dstStart; uint8 pixel = *(_shapePages[0] + offset); pixel &= 0x7F; pixel &= 0x87; if (drawLayer < pixel) { color = *(_shapePages[1] + offset); } } break; case 9: { int offset = dst - dstStart; uint8 pixel = *(_shapePages[0] + offset); pixel &= 0x7F; pixel &= 0x87; if (drawLayer < pixel) { color = *(_shapePages[1] + offset); } else { for (int i = 0; i < tableLoopCount; ++i) { color = table[color]; } } } break; case 10: { int offset = dst - dstStart; uint8 pixel = *(_shapePages[0] + offset); pixel &= 0x7F; pixel &= 0x87; if (drawLayer < pixel) { color = *(_shapePages[1] + offset); drawShapeVar4 = pixel; } else { int temp = drawShapeVar4 + drawShapeVar5; if (temp & 0xFF00) { dst += drawShapeVar3; color = *dst; dst -= drawShapeVar3; } drawShapeVar4 = temp & 0xFF; } } break; case 15: case 11: { int offset = dst - dstStart; uint8 pixel = *(_shapePages[0] + offset); pixel &= 0x7F; pixel &= 0x87; if (drawLayer < pixel) { color = *(_shapePages[1] + offset); } else { color = *dst; for (int i = 0; i < tableLoopCount; ++i) { color = table[color]; } } } break; case 12: { int offset = dst - dstStart; uint8 pixel = *(_shapePages[0] + offset); pixel &= 0x7F; pixel &= 0x87; if (drawLayer < pixel) { color = *(_shapePages[1] + offset); } else { color = table2[color]; } } break; case 13: { int offset = dst - dstStart; uint8 pixel = *(_shapePages[0] + offset); pixel &= 0x7F; pixel &= 0x87; if (drawLayer < pixel) { color = *(_shapePages[1] + offset); } else { color = table2[color]; for (int i = 0; i < tableLoopCount; ++i) { color = table[color]; } } } break; case 14: { int offset = dst - dstStart; uint8 pixel = *(_shapePages[0] + offset); pixel &= 0x7F; pixel &= 0x87; if (drawLayer < pixel) { color = *(_shapePages[1] + offset); drawShapeVar4 = pixel; } else { int temp = drawShapeVar4 + drawShapeVar5; if (temp & 0xFF00) { dst += drawShapeVar3; color = *dst; dst -= drawShapeVar3; drawShapeVar4 = temp % 0xFF; } else { drawShapeVar4 = temp; color = table2[color]; } } } break; case 16: { uint8 newColor = table3[color]; if (!(newColor & 0x80)) { color = *dst; color = table4[color + (newColor << 8)]; } } break; case 17: { for (int i = 0; i < tableLoopCount; ++i) { color = table[color]; } uint8 newColor = table3[color]; if (!(newColor & 0x80)) { color = *dst; color = table4[color + (newColor << 8)]; } } break; case 18: { int temp = drawShapeVar4 + drawShapeVar5; if (temp & 0xFF00) { drawShapeVar4 = temp & 0xFF; dst += drawShapeVar3; color = *dst; dst -= drawShapeVar3; uint8 newColor = table3[color]; if (!(newColor & 0x80)) { color = *dst; color = table4[color + (newColor << 8)]; } } else { drawShapeVar4 = temp; } } break; case 23: case 19: { color = *dst; for (int i = 0; i < tableLoopCount; ++i) { color = table[color]; } uint8 newColor = table3[color]; if (!(newColor & 0x80)) { color = *dst; color = table4[color + (newColor << 8)]; } } break; case 20: { color = table2[color]; uint8 newColor = table3[color]; if (!(newColor & 0x80)) { color = *dst; color = table4[color + (newColor << 8)]; } } break; case 21: { color = table2[color]; for (int i = 0; i < tableLoopCount; ++i) { color = table[color]; } uint8 newColor = table3[color]; if (!(newColor & 0x80)) { color = *dst; color = table4[color + (newColor << 8)]; } } break; case 22: { int temp = drawShapeVar4 + drawShapeVar5; if (temp & 0xFF00) { drawShapeVar4 = temp & 0xFF; dst += drawShapeVar3; color = *dst; dst -= drawShapeVar3; uint8 newColor = table3[color]; if (!(newColor & 0x80)) { color = *dst; color = table4[color + (newColor << 8)]; } } else { drawShapeVar4 = temp; color = table2[color]; uint8 newColor = table3[color]; if (!(newColor & 0x80)) { color = *dst; color = table4[color + (newColor << 8)]; } } } break; case 24: { int offset = dst - dstStart; uint8 pixel = *(_shapePages[0] + offset); pixel &= 0x7F; pixel &= 0x87; if (drawLayer < pixel) { color = *(_shapePages[1] + offset); } uint8 newColor = table3[color]; if (!(newColor & 0x80)) { color = *dst; color = table4[color + (newColor << 8)]; } } break; default: warning("unhandled ppc: %d", ppc); break; } *dst = color; } ++dst; } dst = dstNextLine; } va_end(args); } void Screen::decodeFrame3(const uint8 *src, uint8 *dst, uint32 size) { debug(9, "Screen::decodeFrame3(0x%X, 0x%X, %d)", src, dst, size); const uint8 *dstEnd = dst + size; while (dst < dstEnd) { int8 code = *src++; if (code == 0) { uint16 sz = READ_BE_UINT16(src); src += 2; memset(dst, *src++, sz); dst += sz; } else if (code < 0) { memset(dst, *src++, -code); dst -= code; } else { memcpy(dst, src, code); dst += code; src += code; } } } void Screen::decodeFrame4(const uint8 *src, uint8 *dst, uint32 dstSize) { debug(9, "Screen::decodeFrame4(0x%X, 0x%X, %d)", src, dst, dstSize); uint8 *dstOrig = dst; uint8 *dstEnd = dst + dstSize; while (1) { int count = dstEnd - dst; if (count == 0) { break; } uint8 code = *src++; if (!(code & 0x80)) { int len = MIN(count, (code >> 4) + 3); int offs = ((code & 0xF) << 8) | *src++; const uint8 *dstOffs = dst - offs; while (len--) { *dst++ = *dstOffs++; } } else if (code & 0x40) { int len = (code & 0x3F) + 3; if (code == 0xFE) { len = READ_LE_UINT16(src); src += 2; if (len > count) { len = count; } memset(dst, *src++, len); dst += len; } else { if (code == 0xFF) { len = READ_LE_UINT16(src); src += 2; } int offs = READ_LE_UINT16(src); src += 2; if (len > count) { len = count; } const uint8 *dstOffs = dstOrig + offs; while (len--) { *dst++ = *dstOffs++; } } } else if (code != 0x80) { int len = MIN(count, code & 0x3F); while (len--) { *dst++ = *src++; } } else { break; } } } void Screen::decodeFrameDelta(uint8 *dst, const uint8 *src) { debug(9, "Screen::decodeFrameDelta(0x%X, 0x%X)", dst, src); while (1) { uint8 code = *src++; if (code == 0) { uint8 len = *src++; code = *src++; while (len--) { *dst++ ^= code; } } else if (code & 0x80) { code -= 0x80; if (code != 0) { dst += code; } else { uint16 subcode = READ_LE_UINT16(src); src += 2; if (subcode == 0) { break; } else if (subcode & 0x8000) { subcode -= 0x8000; if (subcode & 0x4000) { uint16 len = subcode - 0x4000; code = *src++; while (len--) { *dst++ ^= code; } } else { while (subcode--) { *dst++ ^= *src++; } } } else { dst += subcode; } } } else { while (code--) { *dst++ ^= *src++; } } } } void Screen::decodeFrameDeltaPage(uint8 *dst, const uint8 *src, int pitch, int noXor) { debug(9, "Screen::decodeFrameDeltaPage(0x%X, 0x%X, %d, %d)", dst, src, pitch, noXor); int count = 0; uint8 *dstNext = dst; while (1) { uint8 code = *src++; if (code == 0) { uint8 len = *src++; code = *src++; while (len--) { if (noXor) { *dst++ = code; } else { *dst++ ^= code; } if (++count == pitch) { count = 0; dstNext += SCREEN_W; dst = dstNext; } } } else if (code & 0x80) { code -= 0x80; if (code != 0) { dst += code; count += code; while (count >= pitch) { count -= pitch; dstNext += SCREEN_W; dst = dstNext + count; } } else { uint16 subcode = READ_LE_UINT16(src); src += 2; if (subcode == 0) { break; } else if (subcode & 0x8000) { subcode -= 0x8000; if (subcode & 0x4000) { uint16 len = subcode - 0x4000; code = *src++; while (len--) { if (noXor) { *dst++ = code; } else { *dst++ ^= code; } if (++count == pitch) { count = 0; dstNext += SCREEN_W; dst = dstNext; } } } else { while (subcode--) { if (noXor) { *dst++ = *src++; } else { *dst++ ^= *src++; } if (++count == pitch) { count = 0; dstNext += SCREEN_W; dst = dstNext; } } } } else { dst += subcode; count += subcode; while (count >= pitch) { count -= pitch; dstNext += SCREEN_W; dst = dstNext + count; } } } } else { while (code--) { if (noXor) { *dst++ = *src++; } else { *dst++ ^= *src++; } if (++count == pitch) { count = 0; dstNext += SCREEN_W; dst = dstNext; } } } } } uint8 *Screen::encodeShape(int x, int y, int w, int h, int flags) { debug(9, "Screen::encodeShape(%d, %d, %d, %d, %d)", x, y, w, h, flags); uint8 *srcPtr = &_pagePtrs[_curPage][y * SCREEN_W + x]; int16 shapeSize = 0; uint8 *tmp = srcPtr; int xpos = w; for (int i = h; i > 0; --i) { uint8 *start = tmp; shapeSize += w; xpos = w; while (xpos) { uint8 value = *tmp++; --xpos; if (!value) { shapeSize += 2; int16 curX = xpos; bool skip = false; while (xpos) { value = *tmp++; --xpos; if (value) { skip = true; break; } } if (!skip) ++curX; curX -= xpos; shapeSize -= curX; while (curX > 0xFF) { curX -= 0xFF; shapeSize += 2; } } } tmp = start + SCREEN_W; } int16 shapeSize2 = shapeSize; if (_vm->features() & GF_TALKIE) { shapeSize += 12; } else { shapeSize += 10; } if (flags & 1) shapeSize += 16; static uint8 table[274]; int tableIndex = 0; uint8 *newShape = NULL; newShape = (uint8*)malloc(shapeSize+16); assert(newShape); byte *dst = newShape; if (_vm->features() & GF_TALKIE) dst += 2; WRITE_LE_UINT16(dst, (flags & 3)); dst += 2; *dst = h; dst += 1; WRITE_LE_UINT16(dst, w); dst += 2; *dst = h; dst += 1; WRITE_LE_UINT16(dst, shapeSize); dst += 2; WRITE_LE_UINT16(dst, shapeSize2); dst += 2; byte *src = srcPtr; if (flags & 1) { dst += 16; memset(table, 0, sizeof(uint8)*274); tableIndex = 1; } for (int ypos = h; ypos > 0; --ypos) { uint8 *srcBackUp = src; xpos = w; while (xpos) { uint8 value = *src++; if (value) { if (flags & 1) { if (!table[value]) { if (tableIndex == 16) { value = 1; } else { table[0x100+tableIndex] = value; table[value] = tableIndex; ++tableIndex; value = tableIndex; } } else { value = table[value]; } } --xpos; *dst++ = value; } else { int16 temp = 1; --xpos; while (xpos) { if (*src) break; ++src; ++temp; --xpos; } while (temp > 0xFF) { *dst++ = 0; *dst++ = 0xFF; temp -= 0xFF; } if (temp & 0xFF) { *dst++ = 0; *dst++ = temp & 0xFF; } } } src = srcBackUp + SCREEN_W; } if (!(flags & 2)) { if (shapeSize > _animBlockSize) { dst = newShape; if (_vm->features() & GF_TALKIE) { dst += 2; } flags = READ_LE_UINT16(dst); flags |= 2; WRITE_LE_UINT16(dst, flags); } else { src = newShape; if (_vm->features() & GF_TALKIE) { src += 2; } if (flags & 1) { src += 16; } src += 10; uint8 *shapePtrBackUp = src; dst = _animBlockPtr; memcpy(dst, src, shapeSize2); int16 size = encodeShapeAndCalculateSize(_animBlockPtr, shapePtrBackUp, shapeSize2); if (size > shapeSize2) { shapeSize -= shapeSize2 - size; newShape = (uint8*)realloc(newShape, shapeSize); assert(newShape); } else { dst = shapePtrBackUp; src = _animBlockPtr; memcpy(dst, src, shapeSize2); dst = newShape; flags = READ_LE_UINT16(dst); flags |= 2; WRITE_LE_UINT16(dst, flags); } } } dst = newShape; if (_vm->features() & GF_TALKIE) { dst += 2; } WRITE_LE_UINT16((dst + 6), shapeSize); if (flags & 1) { dst = newShape + 10; if (_vm->features() & GF_TALKIE) { dst += 2; } src = &table[0x100]; memcpy(dst, src, sizeof(uint8)*16); } return newShape; } int16 Screen::encodeShapeAndCalculateSize(uint8 *from, uint8 *to, int size_to) { debug(9, "Screen::encodeShapeAndCalculateSize(0x%X, 0x%X, %d)", from, to, size_to); byte *fromPtrEnd = from + size_to; bool skipPixel = true; byte *tempPtr = 0; byte *toPtr = to; byte *fromPtr = from; byte *toPtr2 = to; *to++ = 0x81; *to++ = *from++; while (from < fromPtrEnd) { byte *curToPtr = to; to = fromPtr; int size = 1; while (true) { byte curPixel = *from; if (curPixel == *(from+0x40)) { byte *toBackUp = to; to = from; for (int i = 0; i < (fromPtrEnd - from); ++i) { if (*to++ != curPixel) break; } --to; uint16 diffSize = (to - from); if (diffSize >= 0x41) { skipPixel = false; from = to; to = curToPtr; *to++ = 0xFE; WRITE_LE_UINT16(to, diffSize); to += 2; *to++ = (size >> 8) & 0xFF; curToPtr = to; to = toBackUp; continue; } else { to = toBackUp; } } bool breakLoop = false; while (true) { if ((from - to) == 0) { breakLoop = true; break; } for (int i = 0; i < (from - to); ++i) { if (*to++ == curPixel) break; } if (*to == curPixel) { if (*(from+size-1) == *(to+size-2)) break; byte *fromBackUp = from; byte *toBackUp = to; --to; for (int i = 0; i < (fromPtrEnd - from); ++i) { if (*from++ != *to++) break; } if (*(from - 1) == *(to - 1)) ++to; from = fromBackUp; int temp = to - toBackUp; to = toBackUp; if (temp >= size) { size = temp; tempPtr = toBackUp - 1; } break; } else { breakLoop = true; break; } } if (breakLoop) break; } to = curToPtr; if (size > 2) { uint16 word = 0; if (size <= 0x0A) { uint16 diffSize = from - tempPtr; if (size <= 0x0FFF) { byte highByte = ((diffSize & 0xFF00) >> 8) + (((size & 0xFF) - 3) << 4); word = ((diffSize & 0xFF) << 8) | highByte; WRITE_LE_UINT16(to, word); to += 2; from += size; skipPixel = false; continue; } } if (size > 0x40) { *to++ = 0xFF; WRITE_LE_UINT16(to, size); to += 2; } else { *to++ = ((size & 0xFF) - 3) | 0xC0; } word = tempPtr - fromPtr; WRITE_LE_UINT16(to, word); to += 2; from += size; skipPixel = false; } else { if (!skipPixel) { toPtr2 = to; *to++ = 0x80; } if (*toPtr2 == 0xBF) { toPtr2 = to; *to++ = 0x80; } ++(*toPtr2); *to++ = *from++; skipPixel = true; } } *to++ = 0x80; return (to - toPtr); } int Screen::getRectSize(int x, int y) { if (x < 1) { x = 1; } else if (x > 40) { x = 40; } if (y < 1) { y = 1; } else if (y > 200) { y = 200; } return ((x*y) << 3); } void Screen::hideMouse() { debug(9, "Screen::hideMouse()"); ++_mouseLockCount; _system->showMouse(false); } void Screen::showMouse() { debug(9, "Screen::showMouse()"); if (_mouseLockCount > 0) _mouseLockCount--; if (_mouseLockCount == 0) _system->showMouse(true); } void Screen::setShapePages(int page1, int page2) { debug(9, "Screen::setShapePages(%d, %d)", page1, page2); _shapePages[0] = _pagePtrs[page1]; _shapePages[1] = _pagePtrs[page2]; } void Screen::setMouseCursor(int x, int y, byte *shape) { debug(9, "Screen::setMouseCursor(%d, %d, 0x%X)", x, y, shape); if (!shape) return; // if mouseDisabled // return _mouseShape if (_vm->features() & GF_TALKIE) shape += 2; int mouseHeight = *(shape+2); int mouseWidth = (READ_LE_UINT16(shape + 3)) + 2; if (_vm->features() & GF_TALKIE) shape -= 2; uint8 *cursor = (uint8 *)malloc(mouseHeight * mouseWidth); fillRect(0, 0, mouseWidth, mouseHeight, 0, 8); drawShape(8, shape, 0, 0, 0, 0); _system->showMouse(false); copyRegionToBuffer(8, 0, 0, mouseWidth, mouseHeight, cursor); _system->setMouseCursor(cursor, mouseWidth, mouseHeight, x, y, 0); _system->showMouse(true); free(cursor); return; } void Screen::copyScreenFromRect(int x, int y, int w, int h, uint8 *ptr) { debug(9, "Screen::copyScreenFromRect(%d, %d, %d, %d, 0x%X)", x, y, w, h, ptr); x <<= 3; w <<= 3; uint8 *src = ptr; uint8 *dst = &_pagePtrs[0][y * SCREEN_W + x]; for (int i = 0; i < h; ++i) { memcpy(dst, src, w); src += w; dst += SCREEN_W; } } void Screen::copyScreenToRect(int x, int y, int w, int h, uint8 *ptr) { debug(9, "Screen::copyScreenToRect(%d, %d, %d, %d, 0x%X)", x, y, w, h, ptr); x <<= 3; w <<= 3; uint8 *src = &_pagePtrs[0][y * SCREEN_W + x]; uint8 *dst = ptr; for (int i = 0; i < h; ++i) { memcpy(dst, src, w); dst += w; src += SCREEN_W; } } uint8 *Screen::getPalette(int num) { debug(9, "Screen::getPalette(%d)", num); assert(num >= 0 && num < 4); if (num == 0) { return _screenPalette; } return _palettes[num-1]; } byte Screen::getShapeFlag1(int x, int y) { debug(9, "Screen::getShapeFlag1(%d, %d)", x, y); uint8 color = _shapePages[0][y * SCREEN_W + x]; color &= 0x80; color ^= 0x80; if (color & 0x80) { return 1; } return 0; } byte Screen::getShapeFlag2(int x, int y) { debug(9, "Screen::getShapeFlag2(%d, %d)", x, y); uint8 color = _shapePages[0][y * SCREEN_W + x]; color &= 0x7F; color &= 0x87; return color; } int Screen::setNewShapeHeight(uint8 *shape, int height) { debug(9, "Screen::setNewShapeHeight(0x%X, %d)", shape, height); if (_vm->features() & GF_TALKIE) shape += 2; int oldHeight = shape[2]; shape[2] = height; return oldHeight; } int Screen::resetShapeHeight(uint8 *shape) { debug(9, "Screen::setNewShapeHeight(0x%X)", shape); if (_vm->features() & GF_TALKIE) shape += 2; int oldHeight = shape[2]; shape[2] = shape[5]; return oldHeight; } void Screen::addBitBlitRect(int x, int y, int w, int h) { debug(9, "Screen::addBitBlitRects(%d, %d, %d, %d)", x, y, w, h); if (_bitBlitNum >= BITBLIT_RECTS) { error("too many bit blit rects"); } _bitBlitRects[_bitBlitNum].x = x; _bitBlitRects[_bitBlitNum].y = y; _bitBlitRects[_bitBlitNum].x2 = w; _bitBlitRects[_bitBlitNum].y2 = h; ++_bitBlitNum; } void Screen::bitBlitRects() { debug(9, "Screen::bitBlitRects()"); Rect *cur = _bitBlitRects; while (_bitBlitNum) { _bitBlitNum--; copyRegion(cur->x, cur->y, cur->x, cur->y, cur->x2, cur->y2, 2, 0); ++cur; } } void Screen::savePageToDisk(const char *file, int page) { debug(9, "Screen::savePageToDisk('%s', %d)", file, page); if (!_saveLoadPage[page/2]) { _saveLoadPage[page/2] = new uint8[SCREEN_W * SCREEN_H]; assert(_saveLoadPage[page/2]); } memcpy(_saveLoadPage[page/2], getPagePtr(page), SCREEN_W * SCREEN_H); } void Screen::loadPageFromDisk(const char *file, int page) { debug(9, "Screen::loadPageFromDisk('%s', %d)", file, page); copyBlockToPage(page, 0, 0, SCREEN_W, SCREEN_H, _saveLoadPage[page/2]); delete [] _saveLoadPage[page/2]; _saveLoadPage[page/2] = 0; } } // End of namespace Kyra