diff options
Diffstat (limited to 'engines/kyra/screen_eob.cpp')
-rw-r--r-- | engines/kyra/screen_eob.cpp | 1781 |
1 files changed, 1781 insertions, 0 deletions
diff --git a/engines/kyra/screen_eob.cpp b/engines/kyra/screen_eob.cpp new file mode 100644 index 0000000000..e06ca42c40 --- /dev/null +++ b/engines/kyra/screen_eob.cpp @@ -0,0 +1,1781 @@ +/* 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. + * + */ + + +#if !defined(ENABLE_EOB) +#include "kyra/screen.h" +#endif + +#ifdef ENABLE_EOB + +#include "kyra/eobcommon.h" +#include "kyra/resource.h" + +#include "common/system.h" + +#include "graphics/cursorman.h" +#include "graphics/palette.h" + +namespace Kyra { + +Screen_EoB::Screen_EoB(EoBCoreEngine *vm, OSystem *system) : Screen(vm, system, _screenDimTable, _screenDimTableCount) { + _shapeFadeMode[0] = _shapeFadeMode[1] = 0; + _shapeFadeInternal = 0; + _fadeData = 0; + _fadeDataIndex = 0; + _dsX1 = _dsX2 = _dsY1 = _dsY2 = 0; + _gfxX = _gfxY = 0; + _gfxCol = 0; + _dsTempPage = 0; + _dsDiv = 0; + _dsRem = 0; + _dsScaleTrans = 0; + _cgaScaleTable = 0; + _gfxMaxY = 0; + _egaDitheringTable = 0; + _egaPixelValueTable = 0; + _cgaMappingDefault = 0; + _cgaDitheringTables[0] = _cgaDitheringTables[1] = 0; + _useLoResEGA = _useHiResEGADithering = false; +} + +Screen_EoB::~Screen_EoB() { + delete[] _fadeData; + delete[] _dsTempPage; + delete[] _cgaScaleTable; + delete[] _egaDitheringTable; + delete[] _egaPixelValueTable; + delete[] _cgaDitheringTables[0]; + delete[] _cgaDitheringTables[1]; +} + +bool Screen_EoB::init() { + // Define hi-res pages for EGA mode in EOB II + if (_vm->gameFlags().useHiRes) { + for (int i = 0; i < 8; i++) + _pageScaleFactor[i] = 2; + } + + if (Screen::init()) { + int temp; + _gfxMaxY = _vm->staticres()->loadRawData(kEoBBaseExpObjectY, temp); + + if (_renderMode != Common::kRenderCGA && _renderMode != Common::kRenderEGA) + _fadeData = _vm->resource()->fileData("FADING.DAT", 0); + + if (!_fadeData) { + _fadeData = new uint8[0x700]; + memset(_fadeData, 0, 0x700); + if (_renderMode != Common::kRenderCGA && _renderMode != Common::kRenderEGA) { + uint8 *pal = _vm->resource()->fileData("PALETTE1.PAL", 0); + for (int i = 0; i < 7; i++) + createFadeTable(pal, &_fadeData[i << 8], 18, (i + 1) * 36); + delete[] pal; + } + } + + _dsTempPage = new uint8[12000]; + + if (_vm->gameFlags().useHiRes && _renderMode == Common::kRenderEGA) { + _useHiResEGADithering = true; + _egaDitheringTable = new uint8[256]; + _egaPixelValueTable = new uint8[256]; + for (int i = 0; i < 256; i++) { + _egaDitheringTable[i] = i & 0x0f; + _egaPixelValueTable[i] = i & 0x0f; + } + } else if (_renderMode == Common::kRenderEGA) { + _useLoResEGA = true; + } else if (_renderMode == Common::kRenderCGA) { + _cgaMappingDefault = _vm->staticres()->loadRawData(kEoB1CgaMappingDefault, temp); + _cgaDitheringTables[0] = new uint16[256]; + memset(_cgaDitheringTables[0], 0, 256 * sizeof(uint16)); + _cgaDitheringTables[1] = new uint16[256]; + memset(_cgaDitheringTables[1], 0, 256 * sizeof(uint16)); + + _cgaScaleTable = new uint8[256]; + memset(_cgaScaleTable, 0, 256 * sizeof(uint8)); + for (int i = 0; i < 256; i++) + _cgaScaleTable[i] = ((i & 0xf0) >> 2) | (i & 0x03); + } + + return true; + } + return false; +} + +void Screen_EoB::setClearScreenDim(int dim) { + setScreenDim(dim); + clearCurDim(); +} + +void Screen_EoB::clearCurDim() { + fillRect(_curDim->sx << 3, _curDim->sy, ((_curDim->sx + _curDim->w) << 3) - 1, (_curDim->sy + _curDim->h) - 1, _curDim->unkA); +} + +void Screen_EoB::setMouseCursor(int x, int y, const byte *shape) { + setMouseCursor(x, y, shape, 0); +} + +void Screen_EoB::setMouseCursor(int x, int y, const byte *shape, const uint8 *ovl) { + if (!shape) + return; + + int mouseW = (shape[2] << 3); + int mouseH = (shape[3]); + int colorKey = (_renderMode == Common::kRenderCGA) ? 0 : _cursorColorKey; + + uint8 *cursor = new uint8[mouseW * _pageScaleFactor[6] * mouseH * _pageScaleFactor[6]]; + // We use memset and copyBlockToPage instead of fillRect to make sure that the + // color key 0xFF doesn't get converted into EGA color + memset(cursor, colorKey, mouseW * _pageScaleFactor[6] * mouseH * _pageScaleFactor[6]); + copyBlockToPage(6, 0, 0, mouseW, mouseH, cursor); + drawShape(6, shape, 0, 0, 0, 2, ovl); + CursorMan.showMouse(false); + copyRegionToBuffer(6, 0, 0, mouseW, mouseH, cursor); + + // Mouse cursor post processing for CGA mode. Unlike the original (which uses drawShape for the mouse cursor) + // the cursor manager cannot know whether a pixel value of 0 is supposed to be black or transparent. Thus, we + // go over the transparency mask again and turn the black pixels to color 4. + if (_renderMode == Common::kRenderCGA) { + const uint8 *maskTbl = shape + 4 + ((mouseW * mouseH) >> 2); + uint8 *dst = cursor; + uint8 trans = 0; + uint8 shift = 6; + + uint16 mH = mouseH; + while (mH--) { + uint16 mW = mouseW; + while (mW--) { + if (shift == 6) + trans = *maskTbl++; + if (!*dst && !((trans >> shift) & 3)) + *dst = 4; + dst++; + shift = (shift - 2) & 7; + } + } + } + + CursorMan.replaceCursor(cursor, mouseW * _pageScaleFactor[6], mouseH * _pageScaleFactor[6], x, y, colorKey); + if (isMouseVisible()) + CursorMan.showMouse(true); + delete[] cursor; + + // makes sure that the cursor is drawn + // we do not use Screen::updateScreen here + // so we can be sure that changes to page 0 + // are NOT updated on the real screen here + _system->updateScreen(); +} + +void Screen_EoB::loadFileDataToPage(Common::SeekableReadStream *s, int pageNum, uint32 size) { + s->read(_pagePtrs[pageNum], size); +} + +void Screen_EoB::printText(const char *str, int x, int y, uint8 color1, uint8 color2) { + if (_useHiResEGADithering) { + // This is sort of an abuse of the text color map. But since EOB doesn't use it anyway + // and the font drawing code needs access to both the original color values and the + // EGA dithering colors we pass them on like this. + uint8 cmap[2]; + cmap[0] = _egaDitheringTable[color2]; + cmap[1] = _egaDitheringTable[color1]; + setTextColor(cmap, 2, 3); + } + Screen::printText(str, x, y, color1, color2); +} + +void Screen_EoB::printShadedText(const char *string, int x, int y, int col1, int col2) { + printText(string, x - 1, y, 12, col2); + printText(string, x, y + 1, 12, 0); + printText(string, x - 1, y + 1, 12, 0); + printText(string, x, y, col1, 0); +} + +void Screen_EoB::loadShapeSetBitmap(const char *file, int tempPage, int destPage) { + loadEoBBitmap(file, _cgaMappingDefault, tempPage, destPage, -1); + _curPage = 2; +} + +void Screen_EoB::loadEoBBitmap(const char *file, const uint8 *cgaMapping, int tempPage, int destPage, int convertToPage) { + const char *filePattern = (_vm->game() == GI_EOB1 && (_renderMode == Common::kRenderEGA || _renderMode == Common::kRenderCGA)) ? "%s.EGA" : "%s.CPS"; + Common::String tmp = Common::String::format(filePattern, file); + Common::SeekableReadStream *s = _vm->resource()->createReadStream(tmp); + bool loadAlternative = false; + if (s) { + // This additional check is necessary since some localized versions of EOB II seem to contain invalid (size zero) cps files + if (s->size()) + loadBitmap(tmp.c_str(), tempPage, destPage, 0); + else + loadAlternative = true; + + delete s; + } else { + loadAlternative = true; + } + + if (loadAlternative) { + if (_vm->game() == GI_EOB1) { + tmp.insertChar('1', tmp.size() - 4); + loadBitmap(tmp.c_str(), tempPage, destPage, 0); + } else { + tmp.setChar('X', 0); + s = _vm->resource()->createReadStream(tmp); + if (!s) + error("Screen_EoB::loadEoBBitmap(): Failed to load file '%s'", file); + s->seek(768); + loadFileDataToPage(s, destPage, 64000); + delete s; + } + } + + if (convertToPage == -1) { + return; + } else if (convertToPage == 2 && _renderMode == Common::kRenderCGA) { + convertPage(destPage, 4, cgaMapping); + copyRegion(0, 0, 0, 0, 320, 200, 4, 2, Screen::CR_NO_P_CHECK); + } else if (convertToPage == 0) { + convertPage(destPage, 2, cgaMapping); + copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + } else { + convertPage(destPage, convertToPage, cgaMapping); + } +} + +void Screen_EoB::convertPage(int srcPage, int dstPage, const uint8 *cgaMapping) { + uint8 *src = getPagePtr(srcPage); + uint8 *dst = getPagePtr(dstPage); + if (src == dst) + return; + + if (_renderMode == Common::kRenderCGA) { + if (cgaMapping) + generateCGADitheringTables(cgaMapping); + + uint16 *d = (uint16*)dst; + uint8 tblSwitch = 0; + for (int height = SCREEN_H; height; height--) { + const uint16 *table = _cgaDitheringTables[(tblSwitch++) & 1]; + for (int width = SCREEN_W / 2; width; width--) { + WRITE_LE_UINT16(d++, table[((src[1] & 0x0f) << 4) | (src[0] & 0x0f)]); + src += 2; + } + } + + } else if (_useHiResEGADithering) { + for (int height = SCREEN_H; height; height--) { + uint8 *dst2 = dst + SCREEN_W * 2; + for (int width = SCREEN_W; width; width--) { + uint8 in = _egaDitheringTable[*src++]; + *dst++ = *dst2++ = in >> 4; + *dst++ = *dst2++ = in & 0x0f; + } + dst += (SCREEN_W * 2); + } + + } else if (_renderMode == Common::kRenderEGA) { + uint32 len = SCREEN_W * SCREEN_H; + while (len--) + *dst++ = *src++ & 0x0f; + + } else { + copyPage(srcPage, dstPage); + } + + if (dstPage == 0 || dstPage == 1) + _forceFullUpdate = true; +} + +void Screen_EoB::fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum, bool xored) { + if (!_useHiResEGADithering) { + Screen::fillRect(x1, y1, x2, y2, color, pageNum, xored); + return; + } + + assert(x2 < SCREEN_W && y2 < SCREEN_H); + if (pageNum == -1) + pageNum = _curPage; + + uint16 pitch = (SCREEN_W - (x2 - x1 + 1)) * _pageScaleFactor[pageNum]; + uint8 col1 = (_egaDitheringTable[color] >> 4); + uint8 col2 = (_egaDitheringTable[color] & 0x0f); + + x1 *= _pageScaleFactor[pageNum]; + y1 *= _pageScaleFactor[pageNum]; + x2 *= _pageScaleFactor[pageNum]; + y2 *= _pageScaleFactor[pageNum]; + uint16 w = x2 - x1 + _pageScaleFactor[pageNum]; + uint16 h = y2 - y1 + _pageScaleFactor[pageNum]; + + uint8 *dst = getPagePtr(pageNum) + y1 * SCREEN_W * _pageScaleFactor[pageNum] + x1; + if (pageNum == 0 || pageNum == 1) + addDirtyRect(x1, y1, w, h); + + while (h--) { + for (uint16 w1 = w; w1; w1 -= 2) { + *dst++ = col1; + *dst++ = col2; + } + dst += pitch; + } +} + +void Screen_EoB::drawLine(bool vertical, int x, int y, int length, int color) { + if (!_useHiResEGADithering) { + Screen::drawLine(vertical, x, y, length, color); + return; + } + + uint16 pitch = (SCREEN_W - 1) * _pageScaleFactor[_curPage]; + uint8 col1 = (_egaDitheringTable[color] >> 4); + uint8 col2 = (_egaDitheringTable[color] & 0x0f); + + x *= _pageScaleFactor[_curPage]; + y *= _pageScaleFactor[_curPage]; + length *= _pageScaleFactor[_curPage]; + uint8 *ptr = getPagePtr(_curPage) + y * SCREEN_W * _pageScaleFactor[_curPage] + x; + uint8 *ptr2 = ptr + SCREEN_W * _pageScaleFactor[_curPage]; + + if (vertical) { + assert((y + length) <= SCREEN_H * _pageScaleFactor[_curPage]); + int currLine = 0; + while (currLine < length) { + *ptr++ = col1; + *ptr++ = col2; + ptr += pitch; + currLine++; + } + } else { + assert((x + length) <= SCREEN_W * _pageScaleFactor[_curPage]); + int currLine = 0; + while (currLine < length) { + *ptr++ = *ptr2++ = col1; + *ptr++ = *ptr2++ = col2; + currLine += 2; + } + } + + if (_curPage == 0 || _curPage == 1) + addDirtyRect(x, y, (vertical) ? _pageScaleFactor[_curPage] : length, (vertical) ? length : _pageScaleFactor[_curPage]); +} + +uint8 Screen_EoB::getPagePixel(int pageNum, int x, int y) { + if (!_useHiResEGADithering) + return Screen::getPagePixel(pageNum, x, y); + + x *= _pageScaleFactor[_curPage]; + y *= _pageScaleFactor[_curPage]; + uint8 *pos = &_pagePtrs[pageNum][y * SCREEN_W * _pageScaleFactor[_curPage] + x]; + + return _egaPixelValueTable[(pos[0] << 4) | (pos[1] & 0x0f)]; +} + +void Screen_EoB::setPagePixel(int pageNum, int x, int y, uint8 color) { + if (!_useHiResEGADithering) { + Screen::setPagePixel(pageNum, x, y, color); + return; + } + + assert(pageNum < SCREEN_PAGE_NUM); + assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H); + + x *= _pageScaleFactor[_curPage]; + y *= _pageScaleFactor[_curPage]; + + if (pageNum == 0 || pageNum == 1) + addDirtyRect(x, y, _pageScaleFactor[pageNum], _pageScaleFactor[pageNum]); + + uint8 *pos = &_pagePtrs[pageNum][y * SCREEN_W * _pageScaleFactor[_curPage] + x]; + uint8 *pos2 = pos + SCREEN_W * _pageScaleFactor[_curPage]; + pos[0] = pos2[0] = _egaDitheringTable[color] >> 4; + pos[1] = pos2[1] = _egaDitheringTable[color] & 0x0f; +} + +void Screen_EoB::setScreenPalette(const Palette &pal) { + if (_useHiResEGADithering && pal.getNumColors() != 16) { + generateEGADitheringTable(pal); + } else if (_renderMode == Common::kRenderEGA && pal.getNumColors() == 16) { + _screenPalette->copy(pal); + _system->getPaletteManager()->setPalette(_screenPalette->getData(), 0, _screenPalette->getNumColors()); + } else if (_renderMode != Common::kRenderCGA && _renderMode != Common::kRenderEGA) { + Screen::setScreenPalette(pal); + } +} + +void Screen_EoB::getRealPalette(int num, uint8 *dst) { + if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA) { + const uint8 *pal = _screenPalette->getData(); + for (int i = 0; i < 16; ++i) { + dst[0] = (pal[0] << 2) | (pal[0] & 3); + dst[1] = (pal[1] << 2) | (pal[1] & 3); + dst[2] = (pal[2] << 2) | (pal[2] & 3); + dst += 3; + pal += 3; + } + } else { + Screen::getRealPalette(num, dst); + } +} + +uint8 *Screen_EoB::encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool encode8bit, const uint8 *cgaMapping) { + uint8 *shp = 0; + uint16 shapesize = 0; + + uint8 *srcLineStart = getPagePtr(_curPage | 1) + y * 320 + (x << 3); + uint8 *src = srcLineStart; + + if (_useLoResEGA) + encode8bit = false; + + if (_renderMode == Common::kRenderCGA) { + if (cgaMapping) + generateCGADitheringTables(cgaMapping); + shapesize = h * (w << 2) + 4; + shp = new uint8[shapesize]; + memset(shp, 0, shapesize); + uint8 *dst = shp; + + *dst++ = 4; + *dst++ = (h & 0xff); + *dst++ = (w & 0xff); + *dst++ = (h & 0xff); + + uint8 *dst2 = dst + (h * (w << 1)); + + uint8 tblSwitch = 0; + uint16 h1 = h; + while (h1--) { + uint16 w1 = w << 1; + const uint16 *table = _cgaDitheringTables[(tblSwitch++) & 1]; + + while (w1--) { + uint16 p0 = table[((src[1] & 0x0f) << 4) | (src[0] & 0x0f)]; + uint16 p1 = table[((src[3] & 0x0f) << 4) | (src[2] & 0x0f)]; + + *dst++ = ((p0 & 0x0003) << 6) | ((p0 & 0x0300) >> 4) | ((p1 & 0x0003) << 2) | ((p1 & 0x0300) >> 8); + + uint8 msk = 0; + for (int i = 0; i < 4; i++) { + if (!src[3 - i]) + msk |= (3 << (i << 1)); + } + *dst2++ = msk; + src += 4; + } + srcLineStart += SCREEN_W; + src = srcLineStart; + } + + } else if (encode8bit) { + uint16 h1 = h; + while (h1--) { + uint8 *lineEnd = src + (w << 3); + do { + if (!*src++) { + shapesize++; + uint8 *startZeroPos = src; + while (src != lineEnd && *src == 0) + src++; + + uint16 numZero = src - startZeroPos + 1; + if (numZero >> 8) + shapesize += 2; + } + shapesize++; + } while (src != lineEnd); + + srcLineStart += SCREEN_W; + src = srcLineStart; + } + + shapesize += 4; + + shp = new uint8[shapesize]; + memset(shp, 0, shapesize); + uint8 *dst = shp; + + *dst++ = 8; + *dst++ = (h & 0xff); + *dst++ = (w & 0xff); + *dst++ = (h & 0xff); + + srcLineStart = getPagePtr(_curPage | 1) + y * 320 + (x << 3); + src = srcLineStart; + + h1 = h; + while (h1--) { + uint8 *lineEnd = src + (w << 3); + do { + uint8 val = *src++; + if (!val) { + *dst++ = 0; + uint8 *startZeroPos = src; + + while (src != lineEnd && *src == 0) + src++; + + uint16 numZero = src - startZeroPos + 1; + if (numZero >> 8) { + *dst++ = 255; + *dst++ = 0; + numZero -= 255; + } + val = numZero & 0xff; + } + *dst++ = val; + } while (src != lineEnd); + + srcLineStart += SCREEN_W; + src = srcLineStart; + } + + } else { + uint8 nib = 0, col = 0; + uint8 *colorMap = 0; + + if (_renderMode != Common::kRenderEGA || _useHiResEGADithering) { + colorMap = new uint8[0x100]; + memset(colorMap, 0xff, 0x100); + } + + shapesize = h * (w << 2) + 20; + shp = new uint8[shapesize]; + memset(shp, 0, shapesize); + uint8 *dst = shp; + + *dst++ = 2; + *dst++ = (h & 0xff); + *dst++ = (w & 0xff); + *dst++ = (h & 0xff); + + if (_useLoResEGA) { + for (int i = 0; i < 16; i++) + dst[i] = i; + } else { + memset(dst, 0xff, 0x10); + } + + uint8 *pal = dst; + dst += 16; + nib = col = 0; + + uint16 h1 = h; + while (h1--) { + uint16 w1 = w << 3; + while (w1--) { + uint8 s = *src++; + uint8 c = s & 0x0f; + if (colorMap) { + c = colorMap[s]; + if (c == 0xff) { + if (col < 0x10) { + *pal++ = s; + c = colorMap[s] = col++; + if (!col) + c = 0; + } else { + c = 0; + } + } + } + + if (++nib & 1) + *dst = c << 4; + else + *dst++ |= c; + } + srcLineStart += SCREEN_W; + src = srcLineStart; + } + delete [] colorMap; + } + + return shp; +} + +void Screen_EoB::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...) { + uint8 *dst = getPagePtr(pageNum); + const uint8 *src = shapeData; + + if (!src) + return; + + va_list args; + va_start(args, flags); + uint8 *ovl = (flags & 2) ? va_arg(args, uint8 *) : 0; + va_end(args); + + if (sd != -1) { + const ScreenDim *dm = getScreenDim(sd); + setShapeFrame(dm->sx, dm->sy, dm->sx + dm->w, dm->sy + dm->h); + x += (_dsX1 << 3); + y += _dsY1; + } + + dst += (_dsX1 << 3) * _pageScaleFactor[pageNum]; + int16 dX = x - (_dsX1 << 3); + int16 dY = y; + int16 dW = _dsX2 - _dsX1; + uint8 pixelsPerByte = *src++ ; + + uint16 dH = *src++; + uint16 width = (*src++) << 3; + uint16 transOffset = (pixelsPerByte == 4) ? (dH * width) >> 2 : 0; + src++; + + int rX = x; + int rY = y; + int rW = width + 8; + int rH = dH; + + uint16 w2 = width; + int d = dY - _dsY1; + + int pixelStep = (flags & 1) ? -1 : 1; + + if (pixelsPerByte == 8) { + uint16 marginLeft = 0; + uint16 marginRight = 0; + + if (d < 0) { + dH += d; + if (dH <= 0) + return; + d = -d; + + for (int i = 0; i < d; i++) { + marginLeft = width; + for (int ii = 0; ii < marginLeft; ii++) { + if (!*src++) + marginLeft = marginLeft + 1 - *src++; + } + } + dY = _dsY1; + } + + d = _dsY2 - dY; + + if (d < 1) + return; + + if (d < dH) + dH = d; + + marginLeft = 0; + + if (dX < 0) { + width += dX; + marginLeft = -dX; + + if (marginLeft >= w2) + return; + + dX = 0; + } + + marginRight = 0; + d = (dW << 3) - dX; + + if (d < 1) + return; + + if (d < width) { + width = d; + marginRight = w2 - marginLeft - width; + } + + dst += (dY * SCREEN_W * _pageScaleFactor[pageNum] * _pageScaleFactor[pageNum] + dX * _pageScaleFactor[pageNum]); + uint8 *dstL = dst; + + if (pageNum == 0 || pageNum == 1) + addDirtyRect(rX * _pageScaleFactor[pageNum], rY * _pageScaleFactor[pageNum], rW * _pageScaleFactor[pageNum], rH * _pageScaleFactor[pageNum]); + + while (dH--) { + int16 xpos = (int16) marginLeft; + + if (flags & 1) { + for (int i = 0; i < w2; i++) { + if (*src++ == 0) { + i += (*src - 1); + src += (*src - 1); + } + } + src--; + } + const uint8 *src2 = src; + + if (xpos) { + do { + uint8 val = (flags & 1) ? *(src - 1) : *src; + while (val && xpos) { + src += pixelStep; + xpos--; + val = (flags & 1) ? *(src - 1) : *src; + } + + val = (flags & 1) ? *(src - 1) : *src; + if (!val) { + src += pixelStep; + uint8 bt = (flags & 1) ? src[1] : src[0]; + src += pixelStep; + xpos = xpos - bt; + } + } while (xpos > 0); + } + + dst -= (xpos * _pageScaleFactor[pageNum]); + xpos += width; + + while (xpos > 0) { + uint8 c = *src; + uint8 m = (flags & 1) ? *(src - 1) : c; + src += pixelStep; + + if (m) { + drawShapeSetPixel(dst, c, SCREEN_W * _pageScaleFactor[pageNum]); + dst += _pageScaleFactor[pageNum]; + xpos--; + } else { + uint8 len = (flags & 1) ? src[1] : src[0]; + dst += (len * _pageScaleFactor[pageNum]); + xpos -= len; + src += pixelStep; + } + } + xpos += marginRight; + + if (xpos) { + do { + uint8 val = (flags & 1) ? *(src - 1) : *src; + while (val && xpos) { + src += pixelStep; + xpos--; + val = (flags & 1) ? *(src - 1) : *src; + } + + val = (flags & 1) ? *(src - 1) : *src; + if (!val) { + src += pixelStep; + uint8 bt = (flags & 1) ? src[1] : src[0]; + src += pixelStep; + xpos = xpos - bt; + } + } while (xpos > 0); + } + + dstL += SCREEN_W * _pageScaleFactor[pageNum] * _pageScaleFactor[pageNum]; + dst = dstL; + if (flags & 1) + src = src2 + 1; + } + } else { + const uint8 *pal = 0; + uint8 cgaPal[4]; + memset(cgaPal, 0, 4); + + if (pixelsPerByte == 2) { + pal = ovl ? ovl : src; + src += 16; + } else { + static const uint8 cgaDefOvl[] = { 0x00, 0x55, 0xaa, 0xff }; + pal = ovl ? ovl : cgaDefOvl; + for (int i = 0; i < 4; i++) + cgaPal[i] = pal[i] & 3; + pal = cgaPal; + } + + if (d < 0) { + d = -d; + if (d >= dH) + return; + src += (d * (width / pixelsPerByte)); + d = dY + dH - _dsY1; + if (d >= 0) { + dH = d; + dY = _dsY1; + d = _dsY2 - dY; + } + } else { + d = _dsY2 - dY; + } + + if (d < 1) + return; + + if (d < dH) + dH = d; + + bool trimL = false; + uint8 dXbitAlign = dX & (pixelsPerByte - 1); + + if (dX < 0) { + width += dX; + d = -dX; + if (flags & 1) + src -= (d / pixelsPerByte); + else + src += (d / pixelsPerByte); + + if (d >= w2) + return; + + dX = 0; + trimL = true; + } + + d = (dW << 3) - dX; + + if (d < 1) + return; + + if (d < width) + width = d; + + dst += (dY * _pageScaleFactor[pageNum] * SCREEN_W * _pageScaleFactor[pageNum] + dX * _pageScaleFactor[pageNum]); + + if (pageNum == 0 || pageNum == 1) + addDirtyRect(rX * _pageScaleFactor[pageNum], rY * _pageScaleFactor[pageNum], rW * _pageScaleFactor[pageNum], rH * _pageScaleFactor[pageNum]); + + int pitch = SCREEN_W * _pageScaleFactor[pageNum] * _pageScaleFactor[pageNum] - width * _pageScaleFactor[pageNum]; + int16 lineSrcStep = (w2 - width) / pixelsPerByte; + uint8 lineSrcStepRemainder = (w2 - width) % pixelsPerByte; + + w2 /= pixelsPerByte; + if (flags & 1) + src += (w2 - 1); + + uint8 pixelPacking = 8 / pixelsPerByte; + uint8 pixelPackingMask = 0; + + for (int i = 0; i < pixelPacking; i++) + pixelPackingMask |= (1 << i); + + if (trimL && (dXbitAlign > lineSrcStepRemainder)) + lineSrcStep--; + + uint8 bitShDef = 8 - pixelPacking; + if (flags & 1) { + lineSrcStep = (w2 << 1) - lineSrcStep; + bitShDef = 0; + } + + uint8 bitShLineStart = bitShDef; + if (trimL) + bitShLineStart -= (dXbitAlign * pixelStep * pixelPacking); + + while (dH--) { + int16 wd = width; + uint8 in = 0; + uint8 trans = 0; + uint8 shift = bitShLineStart; + uint8 shSwtch = bitShLineStart; + + while (wd--) { + if (shift == shSwtch) { + in = *src; + trans = src[transOffset]; + src += pixelStep; + shSwtch = bitShDef; + } + uint8 col = (pixelsPerByte == 2) ? pal[(in >> shift) & pixelPackingMask] : (*dst & ((trans >> shift) & (pixelPackingMask))) | pal[(in >> shift) & pixelPackingMask]; + if (col || pixelsPerByte == 4) + drawShapeSetPixel(dst, col, SCREEN_W * _pageScaleFactor[pageNum]); + dst += _pageScaleFactor[pageNum]; + shift = ((shift - (pixelStep * pixelPacking)) & 7); + } + src += lineSrcStep; + dst += pitch; + } + } +} + +const uint8 *Screen_EoB::scaleShape(const uint8 *shapeData, int steps) { + setShapeFadeMode(1, steps ? true : false); + + while (shapeData && steps--) + shapeData = scaleShapeStep(shapeData); + + return shapeData; +} + +const uint8 *Screen_EoB::scaleShapeStep(const uint8 *shp) { + uint8 *dst = (shp != _dsTempPage) ? _dsTempPage : _dsTempPage + 6000; + uint8 *d = dst; + uint8 pixelsPerByte = *d++ = *shp++; + assert (pixelsPerByte > 1); + + uint16 h = shp[0] + 1; + d[0] = d[2] = (h << 1) / 3; + + uint16 w = shp[1]; + uint16 w2 = (w << 3) / pixelsPerByte; + uint16 t = ((w << 1) % 3) ? 1 : 0; + d[1] = ((w << 1) / 3) + t; + + uint32 transOffsetSrc = (pixelsPerByte == 4) ? (shp[0] * shp[1]) << 1 : 0; + uint32 transOffsetDst = (pixelsPerByte == 4) ? (d[0] * d[1]) << 1 : 0; + shp += 3; + d += 3; + + if (pixelsPerByte == 2) { + int i = 0; + while (i < 16) { + if (!shp[i]) { + i = -i; + break; + } + i++; + } + + if (i >= 0) + i = 0; + else + i = -i; + + _dsScaleTrans = (i << 4) | (i & 0x0f); + for (int ii = 0; ii < 16; ii++) + *d++ = *shp++; + } + + _dsDiv = w2 / 3; + _dsRem = w2 % 3; + + while (--h) { + if (pixelsPerByte == 2) + scaleShapeProcessLine4Bit(d, shp); + else + scaleShapeProcessLine2Bit(d, shp, transOffsetDst, transOffsetSrc); + if (!--h) + break; + if (pixelsPerByte == 2) + scaleShapeProcessLine4Bit(d, shp); + else + scaleShapeProcessLine2Bit(d, shp, transOffsetDst, transOffsetSrc); + if (!--h) + break; + shp += w2; + } + + return (const uint8*)dst; +} + +const uint8 *Screen_EoB::generateShapeOverlay(const uint8 *shp, int paletteOverlayIndex) { + if (*shp != 2) + return 0; + + shp += 4; + uint8 *ovl = getFadeTable(paletteOverlayIndex); + for (int i = 0; i < 16; i++) + _shapeOverlay[i] = ovl[shp[i]]; + return _shapeOverlay; +} + +void Screen_EoB::setShapeFrame(int x1, int y1, int x2, int y2) { + _dsX1 = x1; + _dsY1 = y1; + _dsX2 = x2; + _dsY2 = y2; +} + +void Screen_EoB::setShapeFadeMode(uint8 i, bool b) { + if (!i || i == 1) + _shapeFadeMode[i] = b; +} + +void Screen_EoB::setGfxParameters(int x, int y, int col) { + _gfxX = x; + _gfxY = y; + _gfxCol = col; +} + +void Screen_EoB::drawExplosion(int scale, int radius, int numElements, int stepSize, int aspectRatio, const uint8 *colorTable, int colorTableSize) { + int ymin = 0; + int ymax = _gfxMaxY[scale]; + int xmin = -100; + int xmax = 276; + + if (scale) + --scale; + + hideMouse(); + + const ScreenDim *dm = getScreenDim(5); + int rX1 = dm->sx << 3; + int rY1 = dm->sy; + int rX2 = rX1 + (dm->w << 3); + int rY2 = rY1 + dm->h - 1; + + int16 gx2 = _gfxX; + int16 gy2 = _gfxY; + + int16 *ptr2 = (int16 *)_dsTempPage; + int16 *ptr3 = (int16 *)&_dsTempPage[300]; + int16 *ptr4 = (int16 *)&_dsTempPage[600]; + int16 *ptr5 = (int16 *)&_dsTempPage[900]; + int16 *ptr6 = (int16 *)&_dsTempPage[1200]; + int16 *ptr7 = (int16 *)&_dsTempPage[1500]; + int16 *ptr8 = (int16 *)&_dsTempPage[1800]; + + if (numElements > 150) + numElements = 150; + + for (int i = 0; i < numElements; i++) { + ptr2[i] = ptr3[i] = 0; + ptr4[i] = _vm->_rnd.getRandomNumberRng(0, radius) - (radius >> 1); + ptr5[i] = _vm->_rnd.getRandomNumberRng(0, radius) - (radius >> 1) - (radius >> (8 - aspectRatio)); + ptr7[i] = _vm->_rnd.getRandomNumberRng(1024 / stepSize, 2048 / stepSize); + ptr8[i] = scale << 8; + } + + for (int l = 2; l;) { + if (l != 2) { + for (int i = numElements - 1; i >= 0; i--) { + int16 px = ((ptr2[i] >> 6) >> scale) + gx2; + int16 py = ((ptr3[i] >> 6) >> scale) + gy2; + if (py > ymax) + py = ymax; + if (posWithinRect(px, py, rX1, rY1, rX2, rY2)) + setPagePixel(0, px, py, ptr6[i]); + } + } + + l = 0; + + for (int i = 0; i < numElements; i++) { + uint32 end = _system->getMillis() + 1; + if (ptr4[i] <= 0) + ptr4[i]++; + else + ptr4[i]--; + ptr2[i] += ptr4[i]; + ptr5[i] += 5; + ptr3[i] += ptr5[i]; + ptr8[i] += ptr7[i]; + + int16 px = ((ptr2[i] >> 6) >> scale) + gx2; + int16 py = ((ptr3[i] >> 6) >> scale) + gy2; + if (py >= ymax || py < ymin) + ptr5[i] = -(ptr5[i] >> 1); + if (px >= xmax || px < xmin) + ptr4[i] = -(ptr4[i] >> 1); + + if (py > ymax) + py = ymax; + + int pxVal1 = 0; + if (posWithinRect(px, py, 0, 0, 319, 199)) { + pxVal1 = getPagePixel(2, px, py); + ptr6[i] = getPagePixel(0, px, py); + } + + assert((ptr8[i] >> 8) < colorTableSize); + int pxVal2 = colorTable[ptr8[i] >> 8]; + if (pxVal2) { + l = 1; + if (pxVal1 == _gfxCol && posWithinRect(px, py, rX1, rY1, rX2, rY2)) { + setPagePixel(0, px, py, pxVal2); + if (i % 5 == 0) { + updateScreen(); + uint32 cur = _system->getMillis(); + if (end > cur) + _system->delayMillis(end - cur); + } + } + } else { + ptr7[i] = 0; + } + } + } + + showMouse(); +} + +void Screen_EoB::drawVortex(int numElements, int radius, int stepSize, int, int disorder, const uint8 *colorTable, int colorTableSize) { + int16 *xCoords = (int16 *)_dsTempPage; + int16 *yCoords = (int16 *)&_dsTempPage[300]; + int16 *xMod = (int16 *)&_dsTempPage[600]; + int16 *yMod = (int16 *)&_dsTempPage[900]; + int16 *pixBackup = (int16 *)&_dsTempPage[1200]; + int16 *colTableStep = (int16 *)&_dsTempPage[1500]; + int16 *colTableIndex = (int16 *)&_dsTempPage[1800]; + int16 *pixDelay = (int16 *)&_dsTempPage[2100]; + + hideMouse(); + int cp = _curPage; + + if (numElements > 150) + numElements = 150; + + int cx = 88; + int cy = 48; + radius <<= 6; + + for (int i = 0; i < numElements; i++) { + int16 v38 = _vm->_rnd.getRandomNumberRng(radius >> 2, radius); + int16 stepSum = 0; + int16 sqsum = 0; + while (sqsum < v38) { + stepSum += stepSize; + sqsum += stepSum; + } + + switch (_vm->_rnd.getRandomNumber(255) & 3) { + case 0: + xCoords[i] = 32; + yCoords[i] = sqsum; + xMod[i] = stepSum; + yMod[i] = 0; + break; + + case 1: + xCoords[i] = sqsum; + yCoords[i] = 32; + xMod[i] = 0; + yMod[i] = stepSum; + break; + + case 2: + xCoords[i] = 32; + yCoords[i] = -sqsum; + xMod[i] = stepSum; + yMod[i] = 0; + break; + + default: + xCoords[i] = -sqsum; + yCoords[i] = 32; + xMod[i] = 0; + yMod[i] = stepSum; + break; + } + + if (_vm->_rnd.getRandomBit()) { + xMod[i] *= -1; + yMod[i] *= -1; + } + + colTableStep[i] = _vm->_rnd.getRandomNumberRng(1024 / disorder, 2048 / disorder); + colTableIndex[i] = 0; + pixDelay[i] = _vm->_rnd.getRandomNumberRng(0, disorder >> 2); + } + + int d = 0; + for (int i = 2; i;) { + if (i != 2) { + for (int ii = numElements - 1; ii >= 0; ii--) { + int16 px = CLIP((xCoords[ii] >> 6) + cx, 0, SCREEN_W - 1); + int16 py = CLIP((yCoords[ii] >> 6) + cy, 0, SCREEN_H - 1); + setPagePixel(0, px, py, pixBackup[ii]); + } + } + + i = 0; + int r = (stepSize >> 1) + (stepSize >> 2) + (stepSize >> 3); + uint32 nextDelay = _system->getMillis() + 1; + + for (int ii = 0; ii < numElements; ii++) { + if (pixDelay[ii] == 0) { + if (xCoords[ii] > 0) { + xMod[ii] -= ((xMod[ii] > 0) ? stepSize : r); + } else { + xMod[ii] += ((xMod[ii] < 0) ? stepSize : r); + } + + if (yCoords[ii] > 0) { + yMod[ii] -= ((yMod[ii] > 0) ? stepSize : r); + } else { + yMod[ii] += ((yMod[ii] < 0) ? stepSize : r); + } + + xCoords[ii] += xMod[ii]; + yCoords[ii] += yMod[ii]; + colTableIndex[ii] += colTableStep[ii]; + + } else { + pixDelay[ii]--; + } + + int16 px = CLIP((xCoords[ii] >> 6) + cx, 0, SCREEN_W - 1); + int16 py = CLIP((yCoords[ii] >> 6) + cy, 0, SCREEN_H - 1); + + uint8 tc1 = ((disorder >> 2) <= d) ? getPagePixel(2, px, py) : 0; + pixBackup[ii] = getPagePixel(0, px, py); + uint8 tblIndex = CLIP(colTableIndex[ii] >> 8, 0, colorTableSize - 1); + uint8 tc2 = colorTable[tblIndex]; + + if (tc2) { + i = 1; + if (tc1 == _gfxCol && !pixDelay[ii]) { + setPagePixel(0, px, py, tc2); + if (ii % 15 == 0) { + updateScreen(); + uint32 cur = _system->getMillis(); + if (nextDelay > cur) + _system->delayMillis(nextDelay - cur); + nextDelay += 1; + } + } + } else { + colTableStep[ii] = 0; + } + } + d++; + } + + _curPage = cp; + showMouse(); +} + +void Screen_EoB::fadeTextColor(Palette *pal, int color1, int rate) { + uint8 *col = pal->getData(); + + for (bool loop = true; loop;) { + loop = true; + uint32 end = _system->getMillis() + _vm->tickLength(); + + loop = false; + for (int ii = 0; ii < 3; ii++) { + uint8 c = col[color1 * 3 + ii]; + if (c > rate) { + col[color1 * 3 + ii] -= rate; + loop = true; + } else if (c) { + col[color1 * 3 + ii] = 0; + loop = true; + } + } + + if (loop) { + setScreenPalette(*pal); + updateScreen(); + uint32 cur = _system->getMillis(); + if (end > cur) + _system->delayMillis(end - cur); + } + } +} + +bool Screen_EoB::delayedFadePalStep(Palette *fadePal, Palette *destPal, int rate) { + bool res = false; + + uint8 *s = fadePal->getData(); + uint8 *d = destPal->getData(); + + for (int i = 0; i < 765; i++) { + int fadeVal = *s++; + int dstCur = *d; + int diff = ABS(fadeVal - dstCur); + + if (diff == 0) { + d++; + continue; + } + + res = true; + diff = MIN(diff, rate); + + if (dstCur < fadeVal) + *d += diff; + else + *d -= diff; + d++; + } + + return res; +} + +int Screen_EoB::getRectSize(int w, int h) { + return w * h; +} + +void Screen_EoB::setFadeTableIndex(int index) { + _fadeDataIndex = (CLIP(index, 0, 7) << 8); +} + +void Screen_EoB::createFadeTable(uint8 *palData, uint8 *dst, uint8 rootColor, uint8 weight) { + if (!palData) + return; + + uint8 *src = palData + 3 * rootColor; + uint8 r = *src++; + uint8 g = *src++; + uint8 b = *src; + uint8 tr, tg, tb; + src = palData + 3; + + *dst++ = 0; + weight >>= 1; + + for (uint8 i = 1; i; i++) { + uint16 tmp = (uint16)((*src - r) * weight) << 1; + tr = *src++ - ((tmp >> 8) & 0xff); + tmp = (uint16)((*src - g) * weight) << 1; + tg = *src++ - ((tmp >> 8) & 0xff); + tmp = (uint16)((*src - b) * weight) << 1; + tb = *src++ - ((tmp >> 8) & 0xff); + + uint8 *d = palData + 3; + uint16 v = 0xffff; + uint8 col = rootColor; + + for (uint8 ii = 1; ii; ii++) { + int a = *d++ - tr; + int t = a * a; + a = *d++ - tg; + t += (a * a); + a = *d++ - tb; + t += (a * a); + + if (t <= v && (ii == rootColor || ii != i)) { + v = t; + col = ii ; + } + } + *dst++ = col; + } +} + +uint8 *Screen_EoB::getFadeTable(int index) { + return (index >= 0 && index < 5) ? &_fadeData[index << 8] : 0; +} + +const uint16 *Screen_EoB::getCGADitheringTable(int index) { + return !(index & ~1) ? _cgaDitheringTables[index] : 0; +} + +const uint8 *Screen_EoB::getEGADitheringTable() { + return _egaDitheringTable; +} + +void Screen_EoB::drawShapeSetPixel(uint8 *dst, uint8 col, uint16 pitch) { + if ((_renderMode != Common::kRenderCGA && _renderMode != Common::kRenderEGA) || _useHiResEGADithering) { + if (_shapeFadeMode[0]) { + if (_shapeFadeMode[1]) { + col = _useHiResEGADithering ? _egaPixelValueTable[(dst[0] << 4) | (dst[1] & 0x0f)] : *dst; + } else { + _shapeFadeInternal &= 7; + col = _useHiResEGADithering ? _egaPixelValueTable[(dst[_shapeFadeInternal] << 4) | (dst[_shapeFadeInternal + 1] & 0x0f)] : dst[_shapeFadeInternal]; + _shapeFadeInternal++; + } + } + + if (_shapeFadeMode[1]) { + uint8 cnt = _shapeFadeMode[1]; + while (cnt--) + col = _fadeData[_fadeDataIndex + col]; + } + } + + if (_useHiResEGADithering) { + col = _egaDitheringTable[col]; + dst[0] = dst[pitch] = col >> 4; + dst[1] = dst[pitch + 1] = col & 0x0f; + } else { + *dst = col; + } +} + +void Screen_EoB::scaleShapeProcessLine2Bit(uint8 *&shpDst, const uint8 *&shpSrc, uint32 transOffsetDst, uint32 transOffsetSrc) { + for (int i = 0; i < _dsDiv; i++) { + shpDst[0] = (_cgaScaleTable[shpSrc[0]] << 2) | (shpSrc[1] >> 6); + shpDst[1] = ((shpSrc[1] & 0x0f) << 4) | ((shpSrc[2] >> 2) & 0x0f); + shpDst[transOffsetDst] = (_cgaScaleTable[shpSrc[transOffsetSrc]] << 2) | (shpSrc[transOffsetSrc + 1] >> 6); + shpDst[transOffsetDst + 1] = ((shpSrc[transOffsetSrc + 1] & 0x0f) << 4) | ((shpSrc[transOffsetSrc + 2] >> 2) & 0x0f); + shpSrc += 3; + shpDst += 2; + } + + if (_dsRem == 1) { + shpDst[0] = _cgaScaleTable[shpSrc[0]] << 2; + shpDst[1] = 0; + shpDst[transOffsetDst] = (_cgaScaleTable[shpSrc[transOffsetSrc]] << 2) | 3; + shpDst[transOffsetDst + 1] = 0xff; + shpSrc++; + shpDst += 2; + + } else if (_dsRem == 2) { + shpDst[0] = (_cgaScaleTable[shpSrc[0]] << 2) | (shpSrc[1] >> 6); + shpDst[1] = (shpSrc[1] & 0x3f) << 2; + shpDst[transOffsetDst] = (_cgaScaleTable[shpSrc[transOffsetSrc]] << 2) | (shpSrc[transOffsetSrc + 1] >> 6); + shpDst[transOffsetDst + 1] = ((shpSrc[transOffsetSrc + 1] & 0x3f) << 2) | 3; + shpSrc += 2; + shpDst += 2; + } +} + +void Screen_EoB::scaleShapeProcessLine4Bit(uint8 *&dst, const uint8 *&src) { + for (int i = 0; i < _dsDiv; i++) { + *dst++ = *src++; + *dst++ = (READ_BE_UINT16(src) >> 4) & 0xff; + src += 2; + } + + if (_dsRem == 1) { + *dst++ = *src++; + *dst++ = _dsScaleTrans; + } else if (_dsRem == 2) { + *dst++ = (src[0] & 0xf0) | (src[1] >> 4); + src += 2; + *dst++ = _dsScaleTrans; + *dst++ = _dsScaleTrans; + *dst++ = _dsScaleTrans; + } +} + +bool Screen_EoB::posWithinRect(int posX, int posY, int x1, int y1, int x2, int y2) { + if (posX < x1 || posX > x2 || posY < y1 || posY > y2) + return false; + return true; +} + +void Screen_EoB::generateEGADitheringTable(const Palette &pal) { + assert(_egaDitheringTable); + const uint8 *src = pal.getData(); + uint8 *dst = _egaDitheringTable; + + for (int i = 256; i; i--) { + int r = *src++; + int g = *src++; + int b = *src++; + + uint8 col = 0; + uint16 min = 0x2E83; + + for (int ii = 256; ii; ii--) { + const uint8 *palEntry = _egaMatchTable + (ii - 1) * 3; + if (*palEntry == 0xff) + continue; + + int e_r = palEntry[0] - r; + int e_g = palEntry[1] - g; + int e_b = palEntry[2] - b; + + uint16 s = (e_r * e_r) + (e_g * e_g) + (e_b * e_b); + + if (s <= min) { + min = s; + col = ii - 1; + } + } + *dst++ = col; + } + + memset(_egaPixelValueTable, 0, 256); + for (int i = 0; i < 256; i++) + _egaPixelValueTable[_egaDitheringTable[i]] = i; +} + +void Screen_EoB::generateCGADitheringTables(const uint8 *mappingData) { + for (int i = 0; i < 256; i++) { + _cgaDitheringTables[0][i] = (mappingData[(i >> 4) + 16] << 8) | mappingData[i & 0x0f]; + _cgaDitheringTables[1][i] = (mappingData[i >> 4] << 8) | mappingData[(i & 0x0f) + 16]; + } +} + +const uint8 Screen_EoB::_egaMatchTable[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x00, 0x1E, 0x1E, 0x1E, 0x00, 0x00, 0x1E, + 0x00, 0x1E, 0x1E, 0x0F, 0x00, 0x1E, 0x1E, 0x1E, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x2D, 0x0F, 0x2D, + 0x0F, 0x0F, 0x2D, 0x2D, 0x2D, 0x0F, 0x0F, 0x2D, 0x0F, 0x2D, 0x2D, 0x2D, 0x0F, 0x2D, 0x2D, 0x2D, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x1E, 0x1E, 0x00, 0x1E, 0x2A, 0x1E, 0x00, 0x1E, 0x1E, + 0x00, 0x2A, 0x1E, 0x0F, 0x1E, 0x1E, 0x1E, 0x2A, 0x0F, 0x0F, 0x21, 0x0F, 0x0F, 0x36, 0x0F, 0x2D, + 0x21, 0x0F, 0x2D, 0x36, 0x2D, 0x0F, 0x21, 0x2D, 0x0F, 0x36, 0x2D, 0x2D, 0x21, 0x2D, 0x2D, 0x36, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x2A, 0x00, 0x00, 0x2A, 0x1E, 0x1E, 0x1E, 0x00, 0x1E, + 0x1E, 0x1E, 0x1E, 0x21, 0x00, 0x1E, 0x2A, 0x1E, 0x0F, 0x21, 0x0F, 0x0F, 0x21, 0x2D, 0x0F, 0x36, + 0x0F, 0x0F, 0x36, 0x2D, 0x2D, 0x21, 0x0F, 0x2D, 0x21, 0x2D, 0x2D, 0x36, 0x0F, 0x2D, 0x36, 0x2D, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x2A, 0x2A, 0x1E, 0x1E, 0x1E, 0x1E, + 0x1E, 0x2A, 0x1E, 0x21, 0x1E, 0x1E, 0x2A, 0x2A, 0x0F, 0x21, 0x21, 0x0F, 0x21, 0x36, 0x0F, 0x36, + 0x21, 0x0F, 0x36, 0x36, 0x2D, 0x21, 0x21, 0x2D, 0x21, 0x36, 0x2D, 0x36, 0x21, 0x2D, 0x36, 0x36, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2A, 0x00, 0x00, 0x2A, + 0x00, 0x1E, 0x2A, 0x0F, 0x00, 0x2A, 0x1E, 0x1E, 0x21, 0x0F, 0x0F, 0x21, 0x0F, 0x2D, 0x21, 0x2D, + 0x0F, 0x21, 0x2D, 0x2D, 0x36, 0x0F, 0x0F, 0x36, 0x0F, 0x2D, 0x36, 0x2D, 0x0F, 0x36, 0x2D, 0x2D, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2A, + 0x00, 0x2A, 0x2A, 0x0F, 0x1E, 0x2A, 0x1E, 0x2A, 0x21, 0x0F, 0x21, 0x21, 0x0F, 0x36, 0x21, 0x2D, + 0x21, 0x21, 0x2D, 0x36, 0x36, 0x0F, 0x21, 0x36, 0x0F, 0x36, 0x36, 0x2D, 0x21, 0x36, 0x2D, 0x36, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x2A, 0x15, 0x00, 0x2A, 0x21, 0x1E, 0x21, 0x15, 0x0F, 0x21, 0x15, 0x2D, 0x21, 0x2F, + 0x0F, 0x21, 0x2F, 0x2D, 0x36, 0x15, 0x0F, 0x36, 0x15, 0x2D, 0x36, 0x2F, 0x0F, 0x36, 0x2F, 0x2D, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2A, 0x2A, 0x2A, 0x21, 0x21, 0x21, 0x21, 0x21, 0x36, 0x21, 0x36, + 0x21, 0x21, 0x36, 0x36, 0x36, 0x21, 0x21, 0x36, 0x21, 0x36, 0x36, 0x36, 0x21, 0x36, 0x36, 0x36, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x15, 0x15, 0x15, 0x15, 0x15, 0x2F, 0x15, 0x2F, + 0x15, 0x15, 0x2F, 0x2F, 0x2F, 0x15, 0x15, 0x2F, 0x15, 0x2F, 0x2F, 0x2F, 0x15, 0x2F, 0x2F, 0x2F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x15, 0x15, 0x3F, 0x15, 0x2F, + 0x2F, 0x15, 0x2F, 0x3F, 0x2F, 0x15, 0x2F, 0x2F, 0x15, 0x3F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x3F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x15, 0x3F, + 0x15, 0x15, 0x3F, 0x2F, 0x2F, 0x2F, 0x15, 0x2F, 0x2F, 0x2F, 0x2F, 0x3F, 0x15, 0x2F, 0x3F, 0x2F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x15, 0x3F, 0x3F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x3F, 0x2F, 0x3F, 0x2F, 0x2F, 0x3F, 0x3F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x15, 0x15, 0x3F, 0x15, 0x2F, 0x3F, 0x2F, 0x15, 0x3F, 0x2F, 0x2F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x15, 0x3F, 0x3F, 0x2F, 0x2F, 0x3F, 0x2F, 0x3F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x3F, 0x15, 0x3F, 0x3F, 0x2F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x3F, 0x3F +}; + +uint16 *OldDOSFont::_cgaDitheringTable = 0; +int OldDOSFont::_numRef = 0; + +OldDOSFont::OldDOSFont(Common::RenderMode mode, bool useHiResEGADithering) : _renderMode(mode), _useHiResEGADithering(useHiResEGADithering) { + _data = 0; + _width = _height = _numGlyphs = 0; + _bitmapOffsets = 0; + _useLoResEGA = (_renderMode == Common::kRenderEGA && !_useHiResEGADithering); + + _numRef++; + if (!_cgaDitheringTable && _numRef == 1) { + _cgaDitheringTable = new uint16[256]; + memset(_cgaDitheringTable, 0, 256 * sizeof(uint16)); + static const uint bits[] = { 0, 3, 12, 15 }; + for (int i = 0; i < 256; i++) + _cgaDitheringTable[i] = (bits[i & 3] << 8) | (bits[(i >> 2) & 3] << 12) | (bits[(i >> 4) & 3] << 0) | (bits[(i >> 6) & 3] << 4); + } +} + +OldDOSFont::~OldDOSFont() { + unload(); + + if (_numRef) + --_numRef; + + if (_cgaDitheringTable && !_numRef) { + delete[] _cgaDitheringTable; + _cgaDitheringTable = 0; + } +} + +bool OldDOSFont::load(Common::SeekableReadStream &file) { + unload(); + + _data = new uint8[file.size()]; + assert(_data); + + file.read(_data, file.size()); + if (file.err()) + return false; + + if (file.size() - 2 != READ_LE_UINT16(_data)) + return false; + + _width = _data[0x103]; + _height = _data[0x102]; + _numGlyphs = 255; + + _bitmapOffsets = (uint16 *)(_data + 2); + + for (int i = 0; i < _numGlyphs; ++i) + _bitmapOffsets[i] = READ_LE_UINT16(&_bitmapOffsets[i]); + + return true; +} + +int OldDOSFont::getCharWidth(uint16 c) const { + if (c >= _numGlyphs) + return 0; + return _width; +} + +void OldDOSFont::drawChar(uint16 c, byte *dst, int pitch) const { + static const uint8 renderMaskTable6[] = { 0xFC, 0x00, 0x7E, 0x00, 0x3F, 0x00, 0x1F, 0x80, 0x0F, 0xC0, 0x07, 0xE0, 0x03, 0xF0, 0x01, 0xF8 }; + static const uint8 renderMaskTable8[] = { 0xFF, 0x00, 0x7F, 0x80, 0x3F, 0xC0, 0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xF8, 0x03, 0xFC, 0x01, 0xFE }; + + if (_width != 8 && _width != 6) + error("EOB font rendering not implemented for other font widths than 6 and 8."); + + if (_width == 6) { + switch (c) { + case 0x81: + case 0x9a: + c = 0x5d; + break; + case 0x84: + case 0x8e: + c = 0x5b; + break; + case 0x94: + case 0x99: + c = 0x40; + case 0xe1: + // TODO: recheck this: no conversion for 'ß' ? + break; + } + } else if (_width == 8) { + switch (c) { + case 0x81: + case 0x9a: + case 0x5d: + c = 0x1d; + break; + case 0x84: + case 0x5b: + c = 0x1e; + break; + case 0x94: + case 0x40: + c = 0x1f; + break; + case 0x8e: + c = 0x1b; + break; + case 0x99: + c = 0x1c; + break; + case 0xe1: + c = 0x19; + break; + } + } + + const uint8 *src = &_data[_bitmapOffsets[c]]; + uint8 *dst2 = dst + pitch; + + int w = (_width - 1) >> 3; + pitch -= _width; + + if (_useHiResEGADithering) + pitch *= 2; + + uint8 color1 = _colorMap[1]; + uint8 color2 = _colorMap[0]; + + uint8 colEGA11 = _colorMap[3] >> 4; + uint8 colEGA12 = _colorMap[3] & 0x0f; + uint8 colEGA21 = _colorMap[2] >> 4; + uint8 colEGA22 = _colorMap[2] & 0x0f; + + static const uint16 cgaColorMask[] = { 0, 0x5555, 0xAAAA, 0xFFFF }; + uint16 cgaMask1 = cgaColorMask[color1 & 3]; + uint16 cgaMask2 = cgaColorMask[color2 & 3]; + + if (_renderMode == Common::kRenderCGA || _useLoResEGA) { + color1 &= 0x0f; + color2 &= 0x0f; + } + + int cH = _height; + while (cH--) { + int cW = w; + uint8 last = 0; + const uint8 *mtbl = _width == 8 ? renderMaskTable8 : renderMaskTable6; + + if (_renderMode == Common::kRenderCGA) { + uint8 s = *src++; + uint8 m = *mtbl++; + + uint8 in = s | last; + uint16 cmp1 = 0; + uint16 cmp2 = 0; + + if (color1) { + in &= m; + cmp1 = _cgaDitheringTable[in]; + } + + if (color2) { + in = ~in & m; + cmp2 = _cgaDitheringTable[in]; + } + + uint16 cDst = 0; + uint8 sh = 6; + for (int i = 0; i < _width; i++) { + cDst |= ((dst[i] & 3) << sh); + sh = (sh - 2) & 0x0f; + } + + uint16 out = (~(cmp1 | cmp2) & cDst) | (cmp1 & cgaMask1) | (cmp2 & cgaMask2); + + sh = 6; + for (int i = 0; i < _width; i++) { + *dst++ = (out >> sh) & 3; + sh = (sh - 2) & 0x0f; + } + + last = s; + + } else { + for (bool runWidthLoop = true; runWidthLoop;) { + uint8 s = *src++; + uint8 m = *mtbl++; + + for (uint8 i = 0x80; i; i >>= 1) { + if (!(m & i)) { + runWidthLoop = false; + break; + } + + if (_useHiResEGADithering) { + if (s & i) { + if (color1) { + dst[0] = dst2[0] = colEGA11; + dst[1] = dst2[1] = colEGA12; + } + } else if (color2) { + dst[0] = dst2[0] = colEGA21; + dst[1] = dst2[1] = colEGA22; + } + dst += 2; + dst2 += 2; + } else { + if (s & i) { + if (color1) + *dst = color1; + } else if (color2) { + *dst = color2; + } + dst++; + } + } + + if (cW) + cW--; + else + runWidthLoop = false; + } + } + + dst += pitch; + dst2 += pitch; + } +} + +void OldDOSFont::unload() { + delete[] _data; + _data = 0; + _width = _height = _numGlyphs = 0; + _bitmapOffsets = 0; +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB |