/* 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" namespace Kyra { Screen_Eob::Screen_Eob(EobCoreEngine *vm, OSystem *system) : Screen(vm, system) { _shapeFadeMode[0] = _shapeFadeMode[1] = 0; _shapeFadeInternal = 0; _fadeData = 0; _fadeDataIndex = 0; _dsX1 = _dsX2 = _dsY1 = _dsY2 = 0; _customDimTable = 0; _dsTempPage = 0; } Screen_Eob::~Screen_Eob() { delete[] _fadeData; delete[] _customDimTable; delete[] _dsTempPage; } bool Screen_Eob::init() { if (Screen::init()) { _customDimTable = new ScreenDim*[_screenDimTableCount]; memset(_customDimTable, 0, sizeof(ScreenDim *)* _screenDimTableCount); _fadeData = _vm->resource()->fileData("FADING.DAT", 0); if (!_fadeData) { _fadeData = new uint8[0x700]; memset(_fadeData, 0, 0x700); uint8 *pal = _vm->resource()->fileData("palette1.pal", 0); // EGA: palette0.pal for (int i = 0; i < 7; i++) createFadeTable(pal, &_fadeData[i << 8], 18, (i + 1) * 36); delete[] pal; } _dsTempPage = new uint8[6000]; return true; } return false; } void Screen_Eob::setScreenDim(int dim) { assert(dim < _screenDimTableCount); _curDim = _customDimTable[dim] ? (const ScreenDim *)_customDimTable[dim] : &_screenDimTable[dim]; _curDimIndex = dim; } const ScreenDim *Screen_Eob::getScreenDim(int dim) { assert(dim < _screenDimTableCount); return _customDimTable[dim] ? (const ScreenDim *)_customDimTable[dim] : &_screenDimTable[dim]; } void Screen_Eob::modifyScreenDim(int dim, int x, int y, int w, int h) { delete _customDimTable[dim]; _customDimTable[dim] = new ScreenDim; memcpy(_customDimTable[dim], &_screenDimTable[dim], sizeof(ScreenDim)); _customDimTable[dim]->sx = x; _customDimTable[dim]->sy = y; _customDimTable[dim]->w = w; _customDimTable[dim]->h = h; if (dim == _curDimIndex) setScreenDim(dim); } 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) { if (!shape) return; int mouseW = shape[2] << 3; int mouseH = shape[3]; uint8 *cursor = new uint8[mouseW * mouseH]; fillRect(0, 0, mouseW, mouseH, _cursorColorKey, 8); drawShape(8, shape, 0, 0, 0); CursorMan.showMouse(false); copyRegionToBuffer(8, 0, 0, mouseW, mouseH, cursor); CursorMan.replaceCursor(cursor, mouseW, mouseH, x, y, _cursorColorKey); 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::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::loadEobBitmap(const char *file, int tempPage, int destPage) { loadEobCpsFileToPage(file, 0, tempPage, destPage, -1); _curPage = 2; } void Screen_Eob::loadEobCpsFileToPage(const char *file, const uint8 *ditheringData, int tempPage, int destPage, int copyToPage) { char tmp[13]; sprintf(tmp, "%s.CPS", 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, tempPage, destPage, 0); else loadAlternative = true; delete s; } else { loadAlternative = true; } if (loadAlternative) { tmp[0] = 'X'; s = _vm->resource()->createReadStream(tmp); if (!s) error("Screen_Eob::loadEobCpsFileToPage(): CPS file loading failed."); s->seek(768); loadFileDataToPage(s, destPage, 64000); delete s; } if (copyToPage == -1) { return; } else if (copyToPage == 0) { copyPage(destPage, 2); copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); } else { copyPage(destPage, copyToPage); } } uint8 *Screen_Eob::encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool flag) { uint8 *shp = 0; uint16 shapesize = 0; uint8 *srcPage = getPagePtr(_curPage) + y * 320 + (x << 3); uint8 *src = srcPage; if (flag) { 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); srcPage += 320; src = srcPage; } shapesize += 4; shp = new uint8[shapesize]; memset (shp, 0, shapesize); uint8 *dst = shp; *dst++ = 0; *dst++ = (h & 0xff); *dst++ = (w & 0xff); *dst++ = (h & 0xff); srcPage = getPagePtr(_curPage) + y * 320 + (x << 3); src = srcPage; h1 = h; while (h1--) { uint8 *lineEnd = src + (w << 3); do { uint8 val = *src++; if (!val) { *dst++ = val; uint8 *startZeroPos = src; while (src != lineEnd && *src == 0) src++; uint16 numZero = src - startZeroPos + 1; if (numZero >> 8) { numZero -= 0xff; *dst++ = 0xff; *dst++ = 0; } val = (numZero & 0xff); } *dst++ = val; } while (src != lineEnd); srcPage += 320; src = srcPage; } } else { uint8 nib = 0, col = 0; uint8 *colorMap = new uint8[0x100]; memset (colorMap, 0xff, 0x100); shapesize = h * (w << 2) + 0x14; shp = new uint8[shapesize]; memset (shp, 0, shapesize); uint8 *dst = shp; *dst++ = 1; *dst++ = (h & 0xff); *dst++ = (w & 0xff); *dst++ = (h & 0xff); memset (dst, 0xff, 0x10); uint8 *pal = dst; dst += 0x10; srcPage = getPagePtr(_curPage) + y * 320 + (x << 3); src = srcPage; nib = col = 0; uint16 h1 = h; while (h1--) { uint16 w1 = w << 3; while (w1--) { uint8 s = *src++; uint8 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; } } srcPage += 320; src = srcPage; } 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); int16 dX = x - (_dsX1 << 3); int16 dY = y; int16 dW = _dsX2 - _dsX1; uint8 flag = *src++; uint16 dH = *src++; uint16 width = (*src++) << 3; 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 (flag) { const uint8 *pal = ovl ? ovl : src; src += 16; if (d < 0) { d = -d; if (d >= dH) return; src += (d * (width >> 1)); 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; int16 cnt1 = 0; int16 cnt2 = 0; int16 dXbit1 = dX & 1; if (dX < 0) { width += dX; d = -dX; if ((flags & 1)) src -= (d >> 1); else src += (d >> 1); if (d >= w2) return; dX = 0; cnt1++; } d = (dW << 3) - dX; if (d < 1) return; if (d < width) { width = d; cnt2++; } dst += (dY * 320 + dX); if (pageNum == 0 || pageNum == 1) addDirtyRect(rX, rY, rW, rH); int w3 = w2; dY = 320 - width; width >>= 1; w2 >>= 1; if ((flags & 1)) src += (w2 - 1); int16 w1shr = width; if (cnt1 && (dXbit1 & 1)) { w1shr++; w2++; if (!cnt2) dY += 2; } if (cnt2 && (dXbit1 & 1)) w1shr++; int lineSrcStep = (w2 - w1shr); if ((flags & 1)) lineSrcStep = w3 - lineSrcStep; while (dH--) { int16 hpos = width; uint8 col = 0; uint8 b = 0; uint8 nextloop = 0; if (cnt1 && dXbit1) { if (!hpos) return; b = *src; src += pixelStep; nextloop = 2; } else { nextloop = hpos ? 1 : 3; } while (nextloop) { switch (nextloop) { case 1: b = *src; src += pixelStep; col = pal[(flags & 1) ? (b & 0x0f) : (b >> 4)]; if (col) drawShapeSetPixel(dst, col); dst++; case 2: col = pal[(flags & 1) ? (b >> 4) : (b & 0x0f)]; if (!col) { nextloop = 4; break; } drawShapeSetPixel(dst++, col); nextloop = --hpos ? 1 : 3; break; case 3: if (cnt2 && dXbit1) { col = pal[(flags & 1) ? (*src & 0x0f) : (*src >> 4)]; src += pixelStep; if (col) drawShapeSetPixel(dst, col); dst++; } src += lineSrcStep; dst += dY; nextloop = 0; break; case 4: dst++; nextloop = --hpos ? 1 : 3; break; } } } } else { uint16 marginLeft = 0; uint16 marginRight = 0; if (d < 0) { dH += d; if (dH <= 0) return; d = -d; for (int ii = 0; ii < d; ii++) { marginLeft = width; int i = 0; do { for (i = 0; i < marginLeft; i++) if (!*src++) break; if (!*(src-1) || i < marginLeft) marginLeft = ++marginLeft - *src++; else marginLeft = 0; } while (marginLeft); } 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 += (y * 320 + dX); uint8 * dstL = dst; if (pageNum == 0 || pageNum == 1) addDirtyRect(rX, rY, rW, rH); while (dH--) { int16 xpos = (int16) marginLeft; if (xpos) { do { while (*src && xpos) { src++; xpos--; } if (!*src) { uint8 bt = *++src; src++; xpos = xpos - bt; } } while (xpos > 0); } dst -= xpos; xpos += width; while (xpos > 0) { uint8 c = *src++; if (c) { drawShapeSetPixel(dst++, c); xpos--; } else { dst += *src; xpos -= *src++; } } xpos += marginRight; if (xpos) { do { while (*src && xpos) { src++; xpos--; } if (!*src) { uint8 bt = *++src; src++; xpos = xpos - bt; } } while (xpos > 0); } dstL += 320; dst = dstL; } } } void Screen_Eob::drawShapeSetPixel(uint8 * dst, uint8 c) { if (_shapeFadeMode[0]) { if (_shapeFadeMode[1]) { c = *dst; } else { _shapeFadeInternal &= 7; c = *(dst + _shapeFadeInternal++); } } if (_shapeFadeMode[1]) { uint8 cnt = _shapeFadeMode[1]; while (cnt--) c = _fadeData[_fadeDataIndex + c]; } *dst = c; } 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 *d = _dsTempPage; *d++ = *shp++; uint16 h = (*shp++) + 1; d[0] = d[2] = (h << 1) / 3; d++; uint16 w = *shp++; uint16 w2 = w << 2; uint16 t = ((w << 1) % 3) ? 1 : 0; *d++ = ((w << 1) / 3) + t; shp++; d++; int i = 0; while (i < 16) { if (!shp[i]) { i = -i; break; } i++; } if (i >= 0) i = 0; else i = -i; _dsScaleTmp = (i << 4) | (i & 0x0f); memcpy(d, shp, 16); d += 16; shp += 16; _dsDiv = w2 / 3; _dsRem = w2 % 3; do { scaleShapeProcessLine(d, shp); if (!--h) break; scaleShapeProcessLine(d, shp); if (!--h) break; shp += w2; } while (--h); return (const uint8 *) _dsTempPage; } void Screen_Eob::replaceShapePalette(uint8 *shp, const uint8 *pal) { if (*shp != 1) return; shp += 4; memcpy(shp, pal, 16); } void Screen_Eob::applyShapeOverlay(uint8 *shp, int ovlIndex) { if (*shp != 1) return; shp += 4; uint8 *ovl = getFadeTable(ovlIndex); for (int i = 0; i < 16; i++) shp[i] = ovl[shp[i]]; } void Screen_Eob::scaleShapeProcessLine(uint8 *&dst, const uint8 *&src) { for (int i = 0; i < _dsDiv; i++) { *dst++ = *src++; *dst++ = READ_BE_UINT16(src) >> 4; src += 2; } if (_dsRem == 1) { *dst++ = *src++; *dst++ = _dsScaleTmp; } if (_dsRem == 2) { *dst++ = (src[0] & 0xf0) | (src[1] >> 4); src += 2; *dst++ = _dsScaleTmp; *dst++ = _dsScaleTmp; *dst++ = _dsScaleTmp; } } 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() + 16; 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; } 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; } } OldDOSFont::OldDOSFont() { _data = 0; _width = _height = _numGlyphs = 0; _bitmapOffsets = 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]]; int w = (_width - 1) >> 3; pitch -= _width; uint8 color1 = _colorMap[1]; uint8 color2 = _colorMap[0]; int cH = _height; while (cH--) { int cW = w; const uint8 *mtbl = _width == 8 ? renderMaskTable8 : renderMaskTable6; 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 (s & i) { if (color1) *dst = color1; } else if (color2) { *dst = color2; } dst++; } if (cW) cW--; else runWidthLoop = false; } dst += pitch; } } void OldDOSFont::unload() { delete[] _data; _data = 0; _width = _height = _numGlyphs = 0; _bitmapOffsets = 0; } } // End of namespace Kyra #endif // ENABLE_EOB