aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra/graphics/screen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/kyra/graphics/screen.cpp')
-rw-r--r--engines/kyra/graphics/screen.cpp3971
1 files changed, 3971 insertions, 0 deletions
diff --git a/engines/kyra/graphics/screen.cpp b/engines/kyra/graphics/screen.cpp
new file mode 100644
index 0000000000..a07e437d5f
--- /dev/null
+++ b/engines/kyra/graphics/screen.cpp
@@ -0,0 +1,3971 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/graphics/screen.h"
+#include "kyra/kyra_v1.h"
+#include "kyra/resource/resource.h"
+
+#include "common/endian.h"
+#include "common/memstream.h"
+#include "common/system.h"
+#include "common/config-manager.h"
+
+#include "engines/util.h"
+
+#include "graphics/cursorman.h"
+#include "graphics/palette.h"
+#include "graphics/sjis.h"
+
+namespace Kyra {
+
+Screen::Screen(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, const int dimTableSize)
+ : _system(system), _vm(vm), _sjisInvisibleColor(0), _dimTable(dimTable), _dimTableCount(dimTableSize),
+ _cursorColorKey((vm->game() == GI_KYRA1 || vm->game() == GI_EOB1 || vm->game() == GI_EOB2) ? 0xFF : 0) {
+ _debugEnabled = false;
+ _maskMinY = _maskMaxY = -1;
+
+ _drawShapeVar1 = 0;
+ _drawShapeVar3 = 1;
+ _drawShapeVar4 = 0;
+ _drawShapeVar5 = 0;
+
+ memset(_fonts, 0, sizeof(_fonts));
+
+ memset(_pagePtrs, 0, sizeof(_pagePtrs));
+ // In VGA mode the odd and even page pointers point to the same buffers.
+ for (int i = 0; i < SCREEN_PAGE_NUM; i++)
+ _pageMapping[i] = i & ~1;
+
+ _renderMode = Common::kRenderDefault;
+ _sjisMixedFontMode = false;
+
+ _useHiColorScreen = _vm->gameFlags().useHiColorMode;
+ _screenPageSize = SCREEN_PAGE_SIZE;
+ _16bitPalette = 0;
+ _16bitConversionPalette = 0;
+ _16bitShadingLevel = 0;
+ _bytesPerPixel = 1;
+
+ _currentFont = FID_8_FNT;
+ _paletteChanged = true;
+ _curDim = 0;
+}
+
+Screen::~Screen() {
+ for (int i = 0; i < SCREEN_OVLS_NUM; ++i)
+ delete[] _sjisOverlayPtrs[i];
+
+ delete[] _pagePtrs[0];
+
+ for (int f = 0; f < ARRAYSIZE(_fonts); ++f)
+ delete _fonts[f];
+
+ delete _screenPalette;
+ delete _internFadePalette;
+ delete[] _decodeShapeBuffer;
+ delete[] _animBlockPtr;
+ delete[] _16bitPalette;
+ delete[] _16bitConversionPalette;
+
+ for (uint i = 0; i < _palettes.size(); ++i)
+ delete _palettes[i];
+
+ for (int i = 0; i < _dimTableCount; ++i)
+ delete _customDimTable[i];
+ delete[] _customDimTable;
+}
+
+bool Screen::init() {
+ _debugEnabled = false;
+
+ memset(_sjisOverlayPtrs, 0, sizeof(_sjisOverlayPtrs));
+ _useOverlays = false;
+ _useSJIS = false;
+ _use16ColorMode = _vm->gameFlags().use16ColorMode;
+ _isAmiga = (_vm->gameFlags().platform == Common::kPlatformAmiga);
+
+ // We only check the "render_mode" setting for both Eye of the Beholder
+ // games here, since all the other games do not support the render_mode
+ // setting or handle it differently, like Kyra 1 PC-98. This avoids
+ // graphics glitches and crashes in other games, when the user sets his
+ // global render_mode setting to EGA for example.
+ // TODO/FIXME: It would be nice not to hardcode this. But there is no
+ // trivial/non annoying way to do mode checks in an easy fashion right
+ // now.
+ // In a more general sense, we might want to think about a way to only
+ // pass valid config values, as in values which the engine can work with,
+ // to the engines. We already limit the selection via our GUIO flags in
+ // the game specific settings, but this is not enough due to global
+ // settings allowing everything.
+ if (_vm->game() == GI_EOB1 || _vm->game() == GI_EOB2) {
+ if (ConfMan.hasKey("render_mode"))
+ _renderMode = Common::parseRenderMode(ConfMan.get("render_mode"));
+ }
+
+ // CGA and EGA modes use additional pages to do the CGA/EGA specific graphics conversions.
+ if (_vm->game() == GI_EOB1 && (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA)) {
+ for (int i = 0; i < 8; i++)
+ _pageMapping[i] = i;
+ }
+
+ memset(_fonts, 0, sizeof(_fonts));
+
+ _useOverlays = (_vm->gameFlags().useHiRes && _renderMode != Common::kRenderEGA);
+
+ if (_useOverlays) {
+ _useSJIS = (_vm->gameFlags().lang == Common::JA_JPN);
+ _sjisInvisibleColor = (_vm->game() == GI_KYRA1) ? 0x80 : 0xF6;
+ _sjisMixedFontMode = !_use16ColorMode;
+
+ if (!_sjisOverlayPtrs[0]) {
+ // We alway assume 2 bytes per pixel here when the backend is in hicolor mode, since this is the surface that is passed to the backend.
+ // We do this regardsless of the paramater sent to enableHiColorMode() so as not to have to change the backend color mode.
+ // Conversions from 8bit to 16bit have to take place when copying data to this surface here.
+ int bpp = _useHiColorScreen ? 2 : 1;
+ _sjisOverlayPtrs[0] = new uint8[SCREEN_OVL_SJIS_SIZE * bpp];
+ assert(_sjisOverlayPtrs[0]);
+ memset(_sjisOverlayPtrs[0], _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE * bpp);
+ }
+
+ for (int i = 1; i < SCREEN_OVLS_NUM; ++i) {
+ if (!_sjisOverlayPtrs[i]) {
+ _sjisOverlayPtrs[i] = new uint8[SCREEN_OVL_SJIS_SIZE];
+ assert(_sjisOverlayPtrs[i]);
+ memset(_sjisOverlayPtrs[i], _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE);
+ }
+ }
+
+ if (_useSJIS) {
+ Graphics::FontSJIS *font = Graphics::FontSJIS::createFont(_vm->gameFlags().platform);
+
+ if (!font)
+ error("Could not load any SJIS font, neither the original nor ScummVM's 'SJIS.FNT'");
+
+ _fonts[FID_SJIS_FNT] = new SJISFont(font, _sjisInvisibleColor, _use16ColorMode, !_use16ColorMode && _vm->game() != GI_LOL && _vm->game() != GI_EOB2, _vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformFMTowns, !_use16ColorMode && _vm->game() == GI_LOL ? 1 : 0);
+ }
+ }
+
+ _curPage = 0;
+
+ enableHiColorMode(false);
+
+ memset(_shapePages, 0, sizeof(_shapePages));
+
+ const int paletteCount = _isAmiga ? 13 : 4;
+ // We allow 256 color palettes in EGA mode, since original EOB II code does the same and requires it
+ const int numColors = _use16ColorMode ? 16 : (_isAmiga ? 32 : (_renderMode == Common::kRenderCGA ? 4 : 256));
+
+ _interfacePaletteEnabled = false;
+
+ _screenPalette = new Palette(numColors);
+ assert(_screenPalette);
+
+ _palettes.resize(paletteCount);
+ for (int i = 0; i < paletteCount; ++i) {
+ _palettes[i] = new Palette(numColors);
+ assert(_palettes[i]);
+ }
+
+ // Setup CGA colors (if CGA mode is selected)
+ if (_renderMode == Common::kRenderCGA) {
+ Palette pal(5);
+ pal.setCGAPalette(1, Palette::kIntensityHigh);
+ // create additional black color 4 for use with the mouse cursor manager
+ pal.fill(4, 1, 0);
+ Screen::setScreenPalette(pal);
+ }
+
+ _internFadePalette = new Palette(numColors);
+ assert(_internFadePalette);
+
+ setScreenPalette(getPalette(0));
+
+ // We setup the PC98 text mode palette at [16, 24], since that will be used
+ // for KANJI characters in Lands of Lore.
+ if (_use16ColorMode && _vm->gameFlags().platform == Common::kPlatformPC98) {
+ uint8 palette[8 * 3];
+
+ for (int i = 0; i < 8; ++i) {
+ palette[i * 3 + 0] = ((i >> 1) & 1) * 0xFF;
+ palette[i * 3 + 1] = ((i >> 2) & 1) * 0xFF;
+ palette[i * 3 + 2] = ((i >> 0) & 1) * 0xFF;
+ }
+
+ _system->getPaletteManager()->setPalette(palette, 16, 8);
+ }
+
+ _customDimTable = new ScreenDim *[_dimTableCount];
+ memset(_customDimTable, 0, sizeof(ScreenDim *) * _dimTableCount);
+
+ _curDimIndex = -1;
+ _curDim = 0;
+ _charWidth = 0;
+ _charOffset = 0;
+ for (int i = 0; i < ARRAYSIZE(_textColorsMap); ++i)
+ _textColorsMap[i] = i;
+ _textColorsMap16bit[0] = _textColorsMap16bit[1] = 0;
+ _decodeShapeBuffer = NULL;
+ _decodeShapeBufferSize = 0;
+ _animBlockPtr = NULL;
+ _animBlockSize = 0;
+ _mouseLockCount = 1;
+ CursorMan.showMouse(false);
+
+ _forceFullUpdate = false;
+
+ return true;
+}
+
+bool Screen::enableScreenDebug(bool enable) {
+ bool temp = _debugEnabled;
+
+ if (_debugEnabled != enable) {
+ _debugEnabled = enable;
+ setResolution();
+ _forceFullUpdate = true;
+ updateScreen();
+ }
+
+ return temp;
+}
+
+void Screen::setResolution() {
+ byte palette[3 * 256];
+ if (!_useHiColorScreen)
+ _system->getPaletteManager()->grabPalette(palette, 0, 256);
+
+ int width = 320, height = 200;
+
+ if (_vm->gameFlags().useHiRes) {
+ height = 400;
+
+ if (_debugEnabled)
+ width = 960;
+ else
+ width = 640;
+ } else {
+ if (_debugEnabled)
+ width = 640;
+ else
+ width = 320;
+ }
+
+ if (_useHiColorScreen) {
+ Graphics::PixelFormat px(2, 5, 5, 5, 0, 10, 5, 0, 0);
+ Common::List<Graphics::PixelFormat> tryModes = _system->getSupportedFormats();
+ for (Common::List<Graphics::PixelFormat>::iterator g = tryModes.begin(); g != tryModes.end(); ++g) {
+ if (g->bytesPerPixel != 2 || g->aBits()) {
+ g = tryModes.reverse_erase(g);
+ } else if (*g == px) {
+ tryModes.clear();
+ tryModes.push_back(px);
+ break;
+ }
+ }
+ initGraphics(width, height, tryModes);
+ if (_system->getScreenFormat().bytesPerPixel != 2)
+ error("Required graphics mode not supported by platform.");
+
+ } else {
+ initGraphics(width, height);
+ _system->getPaletteManager()->setPalette(palette, 0, 256);
+ }
+}
+
+void Screen::enableHiColorMode(bool enabled) {
+ if (_useHiColorScreen && enabled) {
+ if (!_16bitPalette)
+ _16bitPalette = new uint16[1024];
+ memset(_16bitPalette, 0, 1024 * sizeof(uint16));
+ delete[] _16bitConversionPalette;
+ _16bitConversionPalette = 0;
+ _bytesPerPixel = 2;
+ } else {
+ if (_useHiColorScreen) {
+ if (!_16bitConversionPalette)
+ _16bitConversionPalette = new uint16[256];
+ memset(_16bitConversionPalette, 0, 256 * sizeof(uint16));
+ }
+
+ delete[] _16bitPalette;
+ _16bitPalette = 0;
+ _bytesPerPixel = 1;
+ }
+
+ resetPagePtrsAndBuffers(SCREEN_PAGE_SIZE * _bytesPerPixel);
+}
+
+void Screen::updateScreen() {
+ bool needRealUpdate = _forceFullUpdate || !_dirtyRects.empty() || _paletteChanged;
+ _paletteChanged = false;
+
+ if (_useOverlays)
+ updateDirtyRectsOvl();
+ else if (_isAmiga && _interfacePaletteEnabled)
+ updateDirtyRectsAmiga();
+ else
+ updateDirtyRects();
+
+ if (_debugEnabled) {
+ needRealUpdate = true;
+
+ if (!_useOverlays)
+ _system->copyRectToScreen(getPagePtr(2), SCREEN_W, 320, 0, SCREEN_W, SCREEN_H);
+ else
+ _system->copyRectToScreen(getPagePtr(2), SCREEN_W, 640, 0, SCREEN_W, SCREEN_H);
+ }
+
+ if (needRealUpdate)
+ _system->updateScreen();
+}
+
+void Screen::updateDirtyRects() {
+ if (_forceFullUpdate) {
+ _system->copyRectToScreen(getCPagePtr(0), SCREEN_W, 0, 0, SCREEN_W, SCREEN_H);
+ } else {
+ const byte *page0 = getCPagePtr(0);
+ Common::List<Common::Rect>::iterator it;
+ for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
+ _system->copyRectToScreen(page0 + it->top * SCREEN_W + it->left, SCREEN_W, it->left, it->top, it->width(), it->height());
+ }
+ }
+ _forceFullUpdate = false;
+ _dirtyRects.clear();
+}
+
+void Screen::updateDirtyRectsAmiga() {
+ if (_forceFullUpdate) {
+ _system->copyRectToScreen(getCPagePtr(0), SCREEN_W, 0, 0, SCREEN_W, 136);
+
+ // Page 8 is not used by Kyra 1 AMIGA, thus we can use it to adjust the colors
+ copyRegion(0, 136, 0, 0, 320, 64, 0, 8, CR_NO_P_CHECK);
+
+ uint8 *dst = getPagePtr(8);
+ for (int y = 0; y < 64; ++y)
+ for (int x = 0; x < 320; ++x)
+ *dst++ += 32;
+
+ _system->copyRectToScreen(getCPagePtr(8), SCREEN_W, 0, 136, SCREEN_W, 64);
+ } else {
+ const byte *page0 = getCPagePtr(0);
+ Common::List<Common::Rect>::iterator it;
+
+ for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
+ if (it->bottom <= 136) {
+ _system->copyRectToScreen(page0 + it->top * SCREEN_W + it->left, SCREEN_W, it->left, it->top, it->width(), it->height());
+ } else {
+ // Check whether the rectangle is part of both the screen and the interface
+ if (it->top < 136) {
+ // The rectangle covers both screen part and interface part
+
+ const int screenHeight = 136 - it->top;
+ const int interfaceHeight = it->bottom - 136;
+
+ const int width = it->width();
+ const int lineAdd = SCREEN_W - width;
+
+ // Copy the screen part verbatim
+ _system->copyRectToScreen(page0 + it->top * SCREEN_W + it->left, SCREEN_W, it->left, it->top, width, screenHeight);
+
+ // Adjust the interface part
+ copyRegion(it->left, 136, 0, 0, width, interfaceHeight, 0, 8, Screen::CR_NO_P_CHECK);
+
+ uint8 *dst = getPagePtr(8);
+ for (int y = 0; y < interfaceHeight; ++y) {
+ for (int x = 0; x < width; ++x)
+ *dst++ += 32;
+ dst += lineAdd;
+ }
+
+ _system->copyRectToScreen(getCPagePtr(8), SCREEN_W, it->left, 136, width, interfaceHeight);
+ } else {
+ // The rectangle only covers the interface part
+
+ const int width = it->width();
+ const int height = it->height();
+ const int lineAdd = SCREEN_W - width;
+
+ copyRegion(it->left, it->top, 0, 0, width, height, 0, 8, Screen::CR_NO_P_CHECK);
+
+ uint8 *dst = getPagePtr(8);
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x)
+ *dst++ += 32;
+ dst += lineAdd;
+ }
+
+ _system->copyRectToScreen(getCPagePtr(8), SCREEN_W, it->left, it->top, width, height);
+ }
+ }
+ }
+ }
+
+ _forceFullUpdate = false;
+ _dirtyRects.clear();
+}
+
+void Screen::updateDirtyRectsOvl() {
+ if (_forceFullUpdate) {
+ const byte *src = getCPagePtr(0);
+ byte *dst = _sjisOverlayPtrs[0];
+ scale2x(dst, 640, src, SCREEN_W, SCREEN_W, SCREEN_H);
+ mergeOverlay(0, 0, 640, 400);
+ _system->copyRectToScreen(dst, _useHiColorScreen ? 1280 : 640, 0, 0, 640, 400);
+ } else {
+ const byte *page0 = getCPagePtr(0);
+ byte *ovl0 = _sjisOverlayPtrs[0];
+ int dstBpp = _useHiColorScreen ? 2 : 1;
+
+ Common::List<Common::Rect>::iterator it;
+ for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
+ byte *dst = ovl0 + it->top * 1280 * dstBpp + (it->left << dstBpp);
+ const byte *src = page0 + it->top * SCREEN_W * _bytesPerPixel + it->left * _bytesPerPixel;
+
+ scale2x(dst, 640, src, SCREEN_W, it->width(), it->height());
+ mergeOverlay(it->left<<1, it->top<<1, it->width()<<1, it->height()<<1);
+ _system->copyRectToScreen(dst, _useHiColorScreen ? 1280 : 640, it->left << 1, it->top << 1, it->width() << 1, it->height() << 1);
+ }
+ }
+
+ _forceFullUpdate = false;
+ _dirtyRects.clear();
+}
+
+void Screen::scale2x(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h) {
+ int srcBpp = _bytesPerPixel;
+ int dstBpp = _useHiColorScreen ? 2 : 1;
+
+ byte *dstL1 = dst;
+ byte *dstL2 = dst + dstPitch * dstBpp;
+
+ int dstAdd = (dstPitch * 2 - w * 2) * dstBpp;
+ int srcAdd = (srcPitch - w) * srcBpp;
+ int dstInc = 2 * dstBpp;
+
+ while (h--) {
+ for (int x = 0; x < w; x++, src += srcBpp, dstL1 += dstInc, dstL2 += dstInc) {
+ if (dstBpp == 1) {
+ uint16 col = *src;
+ col |= col << 8;
+ *(uint16 *)(dstL1) = *(uint16 *)(dstL2) = col;
+ } else if (dstBpp == srcBpp) {
+ uint32 col = *(const uint16 *)src;
+ col |= col << 16;
+ *(uint32 *)(dstL1) = *(uint32 *)(dstL2) = col;
+ } else if (dstBpp == 2) {
+ uint32 col = _16bitConversionPalette[*src];
+ col |= col << 16;
+ *(uint32 *)(dstL1) = *(uint32 *)(dstL2) = col;
+ }
+ }
+ dstL1 += dstAdd; dstL2 += dstAdd;
+ src += srcAdd;
+ }
+}
+
+void Screen::mergeOverlay(int x, int y, int w, int h) {
+ int bpp = _useHiColorScreen ? 2 : 1;
+ byte *dst = _sjisOverlayPtrs[0] + y * 640 * bpp + x * bpp;
+ const byte *src = _sjisOverlayPtrs[1] + y * 640 + x;
+ uint16 *p16 = _16bitPalette ? _16bitPalette : (_16bitConversionPalette ? _16bitConversionPalette : 0);
+
+ int add = 640 - w;
+
+ while (h--) {
+ for (x = 0; x < w; ++x, dst += bpp) {
+ byte col = *src++;
+ if (col != _sjisInvisibleColor) {
+ if (bpp == 2)
+ *(uint16*)dst = p16[col];
+ else
+ *dst = col;
+ }
+ }
+ dst += add * bpp;
+ src += add;
+ }
+}
+
+const ScreenDim *Screen::getScreenDim(int dim) const {
+ assert(dim < _dimTableCount);
+ return _customDimTable[dim] ? _customDimTable[dim] : &_dimTable[dim];
+}
+
+void Screen::modifyScreenDim(int dim, int x, int y, int w, int h) {
+ if (!_customDimTable[dim])
+ _customDimTable[dim] = new ScreenDim;
+
+ memcpy(_customDimTable[dim], &_dimTable[dim], sizeof(ScreenDim));
+ _customDimTable[dim]->sx = x;
+ _customDimTable[dim]->sy = y;
+ _customDimTable[dim]->w = w;
+ _customDimTable[dim]->h = h;
+ if (dim == _curDimIndex || _vm->game() == GI_LOL)
+ setScreenDim(dim);
+}
+
+void Screen::setScreenDim(int dim) {
+ _curDim = getScreenDim(dim);
+ _curDimIndex = dim;
+}
+
+void Screen::resetPagePtrsAndBuffers(int pageSize) {
+ _screenPageSize = pageSize;
+
+ delete[] _pagePtrs[0];
+ memset(_pagePtrs, 0, sizeof(_pagePtrs));
+
+ Common::Array<uint8> realPages;
+ for (int i = 0; i < SCREEN_PAGE_NUM; i++) {
+ if (Common::find(realPages.begin(), realPages.end(), _pageMapping[i]) == realPages.end())
+ realPages.push_back(_pageMapping[i]);
+ }
+
+ int numPages = realPages.size();
+ uint32 bufferSize = numPages * _screenPageSize;
+
+ uint8 *pagePtr = new uint8[bufferSize];
+ memset(pagePtr, 0, bufferSize);
+
+ memset(_pagePtrs, 0, sizeof(_pagePtrs));
+ for (int i = 0; i < SCREEN_PAGE_NUM; i++) {
+ if (_pagePtrs[_pageMapping[i]]) {
+ _pagePtrs[i] = _pagePtrs[_pageMapping[i]];
+ } else {
+ _pagePtrs[i] = pagePtr;
+ pagePtr += _screenPageSize;
+ }
+ }
+}
+
+uint8 *Screen::getPagePtr(int pageNum) {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ return _pagePtrs[pageNum];
+}
+
+const uint8 *Screen::getCPagePtr(int pageNum) const {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ return _pagePtrs[pageNum];
+}
+
+uint8 *Screen::getPageRect(int pageNum, int x, int y, int w, int h) {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ if (pageNum == 0 || pageNum == 1)
+ addDirtyRect(x, y, w, h);
+ return _pagePtrs[pageNum] + y * SCREEN_W + x;
+}
+
+void Screen::clearPage(int pageNum) {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ if (pageNum == 0 || pageNum == 1)
+ _forceFullUpdate = true;
+ memset(getPagePtr(pageNum), 0, _screenPageSize);
+ clearOverlayPage(pageNum);
+}
+
+int Screen::setCurPage(int pageNum) {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ int previousPage = _curPage;
+ _curPage = pageNum;
+ return previousPage;
+}
+
+void Screen::clearCurPage() {
+ if (_curPage == 0 || _curPage == 1)
+ _forceFullUpdate = true;
+ memset(getPagePtr(_curPage), 0, _screenPageSize);
+ clearOverlayPage(_curPage);
+}
+
+void Screen::copyWsaRect(int x, int y, int w, int h, int dimState, int plotFunc, const uint8 *src,
+ int unk1, const uint8 *unkPtr1, const uint8 *unkPtr2) {
+ uint8 *dstPtr = getPagePtr(_curPage);
+ uint8 *origDst = dstPtr;
+
+ const ScreenDim *dim = getScreenDim(dimState);
+ int dimX1 = dim->sx << 3;
+ int dimX2 = dim->w << 3;
+ dimX2 += dimX1;
+
+ int dimY1 = dim->sy;
+ int dimY2 = dim->h;
+ dimY2 += dimY1;
+
+ int temp = y - dimY1;
+ if (temp < 0) {
+ if ((temp += h) <= 0)
+ return;
+ else {
+ SWAP(temp, h);
+ y += temp - h;
+ src += (temp - h) * w;
+ }
+ }
+
+ temp = dimY2 - y;
+ if (temp <= 0)
+ return;
+
+ if (temp < h)
+ h = temp;
+
+ int srcOffset = 0;
+ temp = x - dimX1;
+ if (temp < 0) {
+ temp = -temp;
+ srcOffset = temp;
+ x += temp;
+ w -= temp;
+ }
+
+ int srcAdd = 0;
+
+ temp = dimX2 - x;
+ if (temp <= 0)
+ return;
+
+ if (temp < w) {
+ SWAP(w, temp);
+ temp -= w;
+ srcAdd = temp;
+ }
+
+ dstPtr += y * SCREEN_W + x;
+ uint8 *dst = dstPtr;
+
+ if (_curPage == 0 || _curPage == 1)
+ addDirtyRect(x, y, w, h);
+
+ if (!_use16ColorMode)
+ clearOverlayRect(_curPage, x, y, w, h);
+
+ temp = h;
+ int curY = y;
+ while (h--) {
+ src += srcOffset;
+ ++curY;
+ int cW = w;
+
+ switch (plotFunc) {
+ case 0:
+ memcpy(dst, src, cW);
+ dst += cW; src += cW;
+ break;
+
+ case 1:
+ while (cW--) {
+ uint8 d = *src++;
+ uint8 t = unkPtr1[d];
+ if (t != 0xFF)
+ d = unkPtr2[*dst + (t << 8)];
+ *dst++ = d;
+ }
+ break;
+
+ case 4:
+ while (cW--) {
+ uint8 d = *src++;
+ if (d)
+ *dst = d;
+ ++dst;
+ }
+ break;
+
+ case 5:
+ while (cW--) {
+ uint8 d = *src++;
+ if (d) {
+ uint8 t = unkPtr1[d];
+ if (t != 0xFF)
+ d = unkPtr2[*dst + (t << 8)];
+ *dst = d;
+ }
+ ++dst;
+ }
+ break;
+
+ case 8:
+ case 9:
+ while (cW--) {
+ uint8 d = *src++;
+ uint8 t = _shapePages[0][dst - origDst] & 7;
+ if (unk1 < t && (curY > _maskMinY && curY < _maskMaxY))
+ d = _shapePages[1][dst - origDst];
+ *dst++ = d;
+ }
+ break;
+
+ case 12:
+ case 13:
+ while (cW--) {
+ uint8 d = *src++;
+ if (d) {
+ uint8 t = _shapePages[0][dst - origDst] & 7;
+ if (unk1 < t && (curY > _maskMinY && curY < _maskMaxY))
+ d = _shapePages[1][dst - origDst];
+ *dst++ = d;
+ } else {
+ d = _shapePages[1][dst - origDst];
+ *dst++ = d;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ dst = (dstPtr += SCREEN_W);
+ src += srcAdd;
+ }
+}
+
+int Screen::getPagePixel(int pageNum, int x, int y) {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H);
+ if (_bytesPerPixel == 1)
+ return _pagePtrs[pageNum][y * SCREEN_W + x];
+ else
+ return ((uint16*)_pagePtrs[pageNum])[y * SCREEN_W + x];
+}
+
+void Screen::setPagePixel(int pageNum, int x, int y, uint8 color) {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H);
+
+ if (pageNum == 0 || pageNum == 1)
+ addDirtyRect(x, y, 1, 1);
+
+ if (_use16ColorMode) {
+ color &= 0x0F;
+ color |= (color << 4);
+ } else if (_renderMode == Common::kRenderCGA) {
+ color &= 0x03;
+ } else if (_renderMode == Common::kRenderEGA && !_useHiResEGADithering) {
+ color &= 0x0F;
+ }
+
+ if (_bytesPerPixel == 2) {
+ ((uint16*)_pagePtrs[pageNum])[y * SCREEN_W + x] = _16bitPalette[color];
+ } else {
+ _pagePtrs[pageNum][y * SCREEN_W + x] = color;
+ }
+}
+
+void Screen::fadeFromBlack(int delay, const UpdateFunctor *upFunc) {
+ fadePalette(getPalette(0), delay, upFunc);
+}
+
+void Screen::fadeToBlack(int delay, const UpdateFunctor *upFunc) {
+ if (_renderMode == Common::kRenderEGA)
+ return;
+
+ Palette pal(getPalette(0).getNumColors());
+ fadePalette(pal, delay, upFunc);
+}
+
+void Screen::fadePalette(const Palette &pal, int delay, const UpdateFunctor *upFunc) {
+ if (_renderMode == Common::kRenderEGA || _bytesPerPixel == 2)
+ setScreenPalette(pal);
+
+ updateScreen();
+
+ if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA || _bytesPerPixel == 2)
+ return;
+
+ int diff = 0, delayInc = 0;
+ getFadeParams(pal, delay, delayInc, diff);
+
+ int delayAcc = 0;
+ while (!_vm->shouldQuit()) {
+ delayAcc += delayInc;
+
+ int refreshed = fadePalStep(pal, diff);
+
+ if (upFunc && upFunc->isValid())
+ (*upFunc)();
+ else if (_useHiColorScreen)
+ updateScreen();
+ else
+ _system->updateScreen();
+
+ if (!refreshed)
+ break;
+
+ _vm->delay((delayAcc >> 8) * 1000 / 60);
+ delayAcc &= 0xFF;
+ }
+
+ // In case we should quit we setup the final palette here. This avoids
+ // ugly palette glitches when quitting while fading. This can for example
+ // be noticed when quitting while viewing the family album in Kyra3.
+ if (_vm->shouldQuit()) {
+ setScreenPalette(pal);
+ }
+}
+
+void Screen::getFadeParams(const Palette &pal, int delay, int &delayInc, int &diff) {
+ uint8 maxDiff = 0;
+
+ for (int i = 0; i < pal.getNumColors() * 3; ++i) {
+ diff = ABS(pal[i] - (*_screenPalette)[i]);
+ maxDiff = MAX<uint8>(maxDiff, diff);
+ }
+
+ delayInc = (delay << 8) & 0x7FFF;
+ if (maxDiff != 0)
+ delayInc /= maxDiff;
+
+ delay = delayInc;
+ for (diff = 1; diff <= maxDiff; ++diff) {
+ if (delayInc >= 512)
+ break;
+ delayInc += delay;
+ }
+}
+
+int Screen::fadePalStep(const Palette &pal, int diff) {
+ _internFadePalette->copy(*_screenPalette);
+
+ bool needRefresh = false;
+
+ for (int i = 0; i < pal.getNumColors() * 3; ++i) {
+ int c1 = pal[i];
+ int c2 = (*_internFadePalette)[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;
+ }
+
+ (*_internFadePalette)[i] = (uint8)c2;
+ }
+ }
+
+ if (needRefresh)
+ setScreenPalette(*_internFadePalette);
+
+ return needRefresh ? 1 : 0;
+}
+
+void Screen::setPaletteIndex(uint8 index, uint8 red, uint8 green, uint8 blue) {
+ Palette &pal = getPalette(0);
+
+ const int offset = index * 3;
+
+ if (pal[offset + 0] == red && pal[offset + 1] == green && pal[offset + 2] == blue)
+ return;
+
+ pal[offset + 0] = red;
+ pal[offset + 1] = green;
+ pal[offset + 2] = blue;
+
+ setScreenPalette(pal);
+}
+
+void Screen::getRealPalette(int num, uint8 *dst) {
+ const int colors = _use16ColorMode ? 16 : (_isAmiga ? 32 : 256);
+ const uint8 *palData = getPalette(num).getData();
+
+ if (!palData) {
+ memset(dst, 0, colors * 3);
+ return;
+ }
+
+ for (int i = 0; i < colors; ++i) {
+ dst[0] = (palData[0] * 0xFF) / 0x3F;
+ dst[1] = (palData[1] * 0xFF) / 0x3F;
+ dst[2] = (palData[2] * 0xFF) / 0x3F;
+ dst += 3;
+ palData += 3;
+ }
+}
+
+void Screen::setScreenPalette(const Palette &pal) {
+ uint8 screenPal[256 * 3];
+ _screenPalette->copy(pal);
+
+ for (int i = 0; i < pal.getNumColors(); ++i) {
+ screenPal[3 * i + 0] = (pal[i * 3 + 0] * 0xFF) / 0x3F;
+ screenPal[3 * i + 1] = (pal[i * 3 + 1] * 0xFF) / 0x3F;
+ screenPal[3 * i + 2] = (pal[i * 3 + 2] * 0xFF) / 0x3F;
+ }
+
+ _paletteChanged = true;
+
+ if (_useHiColorScreen) {
+ if (_16bitPalette)
+ memcpy(_16bitPalette, pal.getData(), 512);
+
+ // Generate 16bit palette for the 8bit/16 bit conversion in scale2x()
+ if (_16bitConversionPalette) {
+ Graphics::PixelFormat pixelFormat = _system->getScreenFormat();
+ for (int i = 0; i < 256; ++i)
+ _16bitConversionPalette[i] = pixelFormat.RGBToColor(screenPal[i * 3], screenPal[i * 3 + 1], screenPal[i * 3 + 2]);
+ // The whole Surface has to be converted again after each palette chance
+ _forceFullUpdate = true;
+ }
+ return;
+ }
+
+ _system->getPaletteManager()->setPalette(screenPal, 0, pal.getNumColors());
+}
+
+void Screen::enableInterfacePalette(bool e) {
+ _interfacePaletteEnabled = e;
+
+ _forceFullUpdate = true;
+ _dirtyRects.clear();
+
+ // TODO: We might need to reset the mouse cursor
+
+ updateScreen();
+}
+
+void Screen::setInterfacePalette(const Palette &pal, uint8 r, uint8 g, uint8 b) {
+ if (!_isAmiga)
+ return;
+
+ uint8 screenPal[32 * 3];
+
+ assert(32 <= pal.getNumColors());
+
+ for (int i = 0; i < pal.getNumColors(); ++i) {
+ if (i != 0x10) {
+ screenPal[3 * i + 0] = (pal[i * 3 + 0] * 0xFF) / 0x3F;
+ screenPal[3 * i + 1] = (pal[i * 3 + 1] * 0xFF) / 0x3F;
+ screenPal[3 * i + 2] = (pal[i * 3 + 2] * 0xFF) / 0x3F;
+ } else {
+ screenPal[3 * i + 0] = (r * 0xFF) / 0x3F;
+ screenPal[3 * i + 1] = (g * 0xFF) / 0x3F;
+ screenPal[3 * i + 2] = (b * 0xFF) / 0x3F;
+ }
+ }
+
+ _paletteChanged = true;
+ _system->getPaletteManager()->setPalette(screenPal, 32, pal.getNumColors());
+}
+
+void Screen::copyToPage0(int y, int h, uint8 page, uint8 *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;
+ }
+ addDirtyRect(0, y, SCREEN_W, h);
+ // This would remove the text in the end sequence of
+ // the (Kyrandia 1) FM-TOWNS version.
+ // Since this method is just used for the Seqplayer
+ // this shouldn't be a problem anywhere else, so it's
+ // safe to disable the call here.
+ //clearOverlayRect(0, 0, y, SCREEN_W, h);
+}
+
+void Screen::copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage, int flags) {
+ 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;
+ }
+
+ const uint8 *src = getPagePtr(srcPage) + y1 * SCREEN_W * _bytesPerPixel + x1 * _bytesPerPixel;
+ uint8 *dst = getPagePtr(dstPage) + y2 * SCREEN_W * _bytesPerPixel + x2 * _bytesPerPixel;
+
+ if (src == dst)
+ return;
+
+ if (dstPage == 0 || dstPage == 1)
+ addDirtyRect(x2, y2, w, h);
+
+ copyOverlayRegion(x1, y1, x2, y2, w, h, srcPage, dstPage);
+
+ if (flags & CR_NO_P_CHECK) {
+ while (h--) {
+ memmove(dst, src, w * _bytesPerPixel);
+ src += SCREEN_W * _bytesPerPixel;
+ dst += SCREEN_W * _bytesPerPixel;
+ }
+ } else {
+ while (h--) {
+ for (int i = 0; i < w; ++i) {
+ if (_bytesPerPixel == 2) {
+ uint px = *(const uint16*)&src[i << 1];
+ if (px)
+ *(uint16*)&dst[i << 1] = px;
+ } else {
+ if (src[i])
+ dst[i] = src[i];
+ }
+ }
+ src += SCREEN_W * _bytesPerPixel;
+ dst += SCREEN_W * _bytesPerPixel;
+ }
+ }
+}
+
+void Screen::copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 *dest) {
+ if (y < 0) {
+ dest += (-y) * w * _bytesPerPixel;
+ h += y;
+ y = 0;
+ } else if (y + h > SCREEN_H) {
+ h = SCREEN_H - y;
+ }
+
+ if (x < 0) {
+ dest += -x * _bytesPerPixel;
+ w += x;
+ x = 0;
+ } else if (x + w > SCREEN_W) {
+ w = SCREEN_W - x;
+ }
+
+ if (w < 0 || h < 0)
+ return;
+
+ uint8 *pagePtr = getPagePtr(pageNum);
+
+ for (int i = y; i < y + h; ++i)
+ memcpy(dest + (i - y) * w * _bytesPerPixel, pagePtr + i * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel, w * _bytesPerPixel);
+}
+
+void Screen::copyPage(uint8 srcPage, uint8 dstPage) {
+ uint8 *src = getPagePtr(srcPage);
+ uint8 *dst = getPagePtr(dstPage);
+ if (src != dst)
+ memcpy(dst, src, SCREEN_W * SCREEN_H * _bytesPerPixel);
+ copyOverlayRegion(0, 0, 0, 0, SCREEN_W, SCREEN_H, srcPage, dstPage);
+
+ if (dstPage == 0 || dstPage == 1)
+ _forceFullUpdate = true;
+}
+
+void Screen::copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint8 *src) {
+ if (y < 0) {
+ src += (-y) * w * _bytesPerPixel;
+ h += y;
+ y = 0;
+ } else if (y + h > SCREEN_H) {
+ h = SCREEN_H - y;
+ }
+
+ if (x < 0) {
+ src += -x * _bytesPerPixel;
+ w += x;
+ x = 0;
+ } else if (x + w > SCREEN_W) {
+ w = SCREEN_W - x;
+ }
+
+ if (w < 0 || h < 0)
+ return;
+
+ uint8 *dst = getPagePtr(pageNum) + y * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel;
+
+ if (pageNum == 0 || pageNum == 1)
+ addDirtyRect(x, y, w, h);
+
+ clearOverlayRect(pageNum, x, y, w, h);
+
+ while (h--) {
+ memcpy(dst, src, w * _bytesPerPixel);
+ dst += SCREEN_W * _bytesPerPixel;
+ src += w * _bytesPerPixel;
+ }
+}
+
+void Screen::shuffleScreen(int sx, int sy, int w, int h, int srcPage, int dstPage, int ticks, bool 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]);
+ }
+
+ int32 start, now;
+ int wait;
+ for (y = 0; y < h && !_vm->shouldQuit(); ++y) {
+ start = (int32)_system->getMillis();
+ 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);
+ }
+ // forcing full update for now
+ _forceFullUpdate = true;
+ updateScreen();
+ now = (int32)_system->getMillis();
+ wait = ticks * _vm->tickLength() - (now - start);
+ if (wait > 0)
+ _vm->delay(wait);
+ }
+
+ copyOverlayRegion(sx, sy, sx, sy, w, h, srcPage, dstPage);
+
+ if (_vm->shouldQuit()) {
+ copyRegion(sx, sy, sx, sy, w, h, srcPage, dstPage);
+ _system->updateScreen();
+ }
+}
+
+void Screen::fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum, bool xored) {
+ assert(x2 < SCREEN_W && y2 < SCREEN_H);
+ uint16 color16 = 0;
+ if (pageNum == -1)
+ pageNum = _curPage;
+
+ uint8 *dst = getPagePtr(pageNum) + y1 * SCREEN_W * _bytesPerPixel + x1 * _bytesPerPixel;
+
+ if (pageNum == 0 || pageNum == 1)
+ addDirtyRect(x1, y1, x2-x1+1, y2-y1+1);
+
+ clearOverlayRect(pageNum, x1, y1, x2-x1+1, y2-y1+1);
+
+ if (_use16ColorMode) {
+ color &= 0x0F;
+ color |= (color << 4);
+ } else if (_renderMode == Common::kRenderCGA) {
+ color &= 0x03;
+ } else if (_renderMode == Common::kRenderEGA && !_useHiResEGADithering) {
+ color &= 0x0F;
+ } else if (_bytesPerPixel == 2)
+ color16 = shade16bitColor(_16bitPalette[color]);
+
+ if (xored) {
+ // no 16 bit support for this (unneeded)
+ for (; y1 <= y2; ++y1) {
+ for (int x = x1; x <= x2; ++x)
+ dst[x] ^= color;
+ dst += SCREEN_W;
+ }
+ } else {
+ for (; y1 <= y2; ++y1) {
+ if (_bytesPerPixel == 2) {
+ uint16 *ptr = (uint16*)dst;
+ for (int i = 0; i < x2 - x1 + 1; i++)
+ *ptr++ = color16;
+ } else {
+ memset(dst, color, x2 - x1 + 1);
+ }
+ dst += SCREEN_W * _bytesPerPixel;
+ }
+ }
+}
+
+void Screen::drawBox(int x1, int y1, int x2, int y2, int color) {
+ drawClippedLine(x1, y1, x2, y1, color);
+ drawClippedLine(x1, y1, x1, y2, color);
+ drawClippedLine(x2, y1, x2, y2, color);
+ drawClippedLine(x1, y2, x2, y2, color);
+}
+
+void Screen::drawShadedBox(int x1, int y1, int x2, int y2, int color1, int color2) {
+ assert(x1 >= 0 && y1 >= 0);
+ 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 - 1, x2 - 1, y2 - 1, color2);
+ drawClippedLine(x1, y2, x2, y2, color2);
+}
+
+void Screen::drawClippedLine(int x1, int y1, int x2, int y2, int 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 vertical, int x, int y, int length, int color) {
+ uint8 *ptr = getPagePtr(_curPage) + y * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel;
+
+ if (_use16ColorMode) {
+ color &= 0x0F;
+ color |= (color << 4);
+ } else if (_renderMode == Common::kRenderCGA) {
+ color &= 0x03;
+ } else if (_renderMode == Common::kRenderEGA && !_useHiResEGADithering) {
+ color &= 0x0F;
+ } else if (_bytesPerPixel == 2)
+ color = shade16bitColor(_16bitPalette[color]);
+
+ if (vertical) {
+ assert((y + length) <= SCREEN_H);
+ int currLine = 0;
+ while (currLine < length) {
+ if (_bytesPerPixel == 2)
+ *(uint16*)ptr = color;
+ else
+ *ptr = color;
+ ptr += SCREEN_W * _bytesPerPixel;
+ currLine++;
+ }
+ } else {
+ assert((x + length) <= SCREEN_W);
+ if (_bytesPerPixel == 2) {
+ for (int i = 0; i < length; i++) {
+ *(uint16*)ptr = color;
+ ptr += 2;
+ }
+ } else {
+ memset(ptr, color, length);
+ }
+ }
+
+ if (_curPage == 0 || _curPage == 1)
+ addDirtyRect(x, y, (vertical) ? 1 : length, (vertical) ? length : 1);
+
+ clearOverlayRect(_curPage, x, y, (vertical) ? 1 : length, (vertical) ? length : 1);
+}
+
+void Screen::setAnimBlockPtr(int size) {
+ delete[] _animBlockPtr;
+ _animBlockPtr = new uint8[size];
+ assert(_animBlockPtr);
+ memset(_animBlockPtr, 0, size);
+ _animBlockSize = size;
+}
+
+void Screen::setTextColor(const uint8 *cmap8, int a, int b) {
+ memcpy(&_textColorsMap[a], cmap8, (b - a + 1));
+ // We need to update the color tables of all fonts, we
+ // setup so far here.
+ for (int i = 0; i < FID_NUM; ++i) {
+ if (_fonts[i])
+ _fonts[i]->setColorMap(_textColorsMap);
+ }
+}
+
+void Screen::setTextColor16bit(const uint16 *cmap16) {
+ assert(cmap16);
+ _textColorsMap16bit[0] = cmap16[0];
+ _textColorsMap16bit[1] = cmap16[1];
+ // We need to update the color tables of all fonts, we
+ // setup so far here.
+ for (int i = 0; i < FID_NUM; ++i) {
+ if (_fonts[i])
+ _fonts[i]->set16bitColorMap(_textColorsMap16bit);
+ }
+}
+
+bool Screen::loadFont(FontId fontId, const char *filename) {
+ if (fontId == FID_SJIS_FNT) {
+ warning("Trying to replace system SJIS font");
+ return true;
+ }
+
+ Font *&fnt = _fonts[fontId];
+
+ if (!fnt) {
+ if (_isAmiga)
+ fnt = new AMIGAFont();
+#ifdef ENABLE_EOB
+ else if (_vm->game() == GI_EOB1 || _vm->game() == GI_EOB2)
+ // We use normal VGA rendering in EOB II, since we do the complete EGA dithering in updateScreen().
+ fnt = new OldDOSFont(_useHiResEGADithering ? Common::kRenderVGA : _renderMode);
+#endif // ENABLE_EOB
+ else
+ fnt = new DOSFont();
+
+ assert(fnt);
+ }
+
+ Common::SeekableReadStream *file = _vm->resource()->createReadStream(filename);
+ if (!file)
+ error("Font file '%s' is missing", filename);
+
+ bool ret = fnt->load(*file);
+ fnt->setColorMap(_textColorsMap);
+ delete file;
+ return ret;
+}
+
+Screen::FontId Screen::setFont(FontId fontId) {
+ FontId prev = _currentFont;
+ _currentFont = fontId;
+
+ assert(_fonts[_currentFont]);
+ return prev;
+}
+
+int Screen::getFontHeight() const {
+ return _fonts[_currentFont]->getHeight();
+}
+
+int Screen::getFontWidth() const {
+ return _fonts[_currentFont]->getWidth();
+}
+
+int Screen::getCharWidth(uint16 c) const {
+ const int width = _fonts[_currentFont]->getCharWidth(c);
+ return width + ((_currentFont != FID_SJIS_FNT && _currentFont != FID_SJIS_LARGE_FNT && _currentFont != FID_SJIS_SMALL_FNT) ? _charWidth : 0);
+}
+
+int Screen::getTextWidth(const char *str) {
+ int curLineLen = 0;
+ int maxLineLen = 0;
+
+ FontId curFont = _currentFont;
+
+ while (1) {
+ if (_sjisMixedFontMode && curFont != FID_SJIS_FNT && curFont != FID_SJIS_LARGE_FNT && curFont != FID_SJIS_SMALL_FNT)
+ setFont((*str & 0x80) ? ((_vm->game() == GI_EOB2 && curFont == FID_6_FNT) ? FID_SJIS_SMALL_FNT : FID_SJIS_FNT) : curFont);
+
+ uint c = fetchChar(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) {
+ uint16 cmap16[2];
+ if (_16bitPalette) {
+ cmap16[0] = color2 ? shade16bitColor(_16bitPalette[color2]) : 0xFFFF;
+ cmap16[1] = _16bitPalette[color1];
+ setTextColor16bit(cmap16);
+ }
+
+ uint8 cmap8[2];
+ cmap8[0] = color2;
+ cmap8[1] = color1;
+ setTextColor(cmap8, 0, 1);
+
+ FontId curFont = _currentFont;
+
+ 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) {
+ if (_sjisMixedFontMode && curFont != FID_SJIS_FNT && curFont != FID_SJIS_LARGE_FNT && curFont != FID_SJIS_SMALL_FNT)
+ setFont((*str & 0x80) ? ((_vm->game() == GI_EOB2 && curFont == FID_6_FNT) ? FID_SJIS_SMALL_FNT : FID_SJIS_FNT) : curFont);
+
+ uint8 charHeightFnt = getFontHeight();
+
+ uint c = fetchChar(str);
+
+ if (c == 0) {
+ break;
+ } else if (c == '\r') {
+ x = x_start;
+ y += (charHeightFnt + _charOffset);
+ } else {
+ int charWidth = getCharWidth(c);
+ if (x + charWidth > SCREEN_W) {
+ x = x_start;
+ y += (charHeightFnt + _charOffset);
+ if (y >= SCREEN_H)
+ break;
+ }
+
+ drawChar(c, x, y);
+ x += charWidth;
+ }
+ }
+}
+
+uint16 Screen::fetchChar(const char *&s) const {
+ if (_currentFont != FID_SJIS_FNT && _currentFont != FID_SJIS_LARGE_FNT && _currentFont != FID_SJIS_SMALL_FNT)
+ return (uint8)*s++;
+
+ uint16 ch = (uint8)*s++;
+
+ if (ch <= 0x7F || (ch >= 0xA1 && ch <= 0xDF))
+ return ch;
+
+ ch |= (uint8)(*s++) << 8;
+ return ch;
+}
+
+void Screen::drawChar(uint16 c, int x, int y) {
+ Font *fnt = _fonts[_currentFont];
+ assert(fnt);
+
+ const bool useOverlay = fnt->usesOverlay();
+ const int charWidth = fnt->getCharWidth(c);
+ const int charHeight = fnt->getHeight();
+
+ if (x < 0 || y < 0)
+ return;
+ if (x + charWidth > SCREEN_W || y + charHeight > SCREEN_H)
+ return;
+
+ if (useOverlay) {
+ uint8 *destPage = getOverlayPtr(_curPage);
+ if (!destPage) {
+ warning("trying to draw SJIS char on unsupported page %d", _curPage);
+ return;
+ }
+
+ int bpp = (_currentFont == Screen::FID_SJIS_FNT || _currentFont == Screen::FID_SJIS_SMALL_FNT) ? 1 : 2;
+ destPage += (y * 2) * 640 * bpp + (x * 2 * bpp);
+
+ fnt->drawChar(c, destPage, 640, bpp);
+ } else {
+ fnt->drawChar(c, getPagePtr(_curPage) + y * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel, SCREEN_W, _bytesPerPixel);
+ }
+
+ if (_curPage == 0 || _curPage == 1)
+ addDirtyRect(x, y, charWidth, charHeight);
+}
+
+void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...) {
+ if (!shapeData)
+ return;
+
+ if (_vm->gameFlags().useAltShapeHeader)
+ shapeData += 2;
+
+ if (*shapeData & 1)
+ flags |= 0x400;
+
+ va_list args;
+ va_start(args, flags);
+
+ static const int drawShapeVar2[] = {
+ 1, 3, 2, 5, 4, 3, 2, 1
+ };
+
+ _dsShapeFadingTable = 0;
+ _dsShapeFadingLevel = 0;
+ _dsColorTable = 0;
+ _dsTransparencyTable1 = 0;
+ _dsTransparencyTable2 = 0;
+ _dsBackgroundFadingTable = 0;
+ _dsDrawLayer = 0;
+
+ if (flags & DSF_CUSTOM_PALETTE) {
+ _dsColorTable = va_arg(args, uint8 *);
+ }
+
+ if (flags & DSF_SHAPE_FADING) {
+ _dsShapeFadingTable = va_arg(args, uint8 *);
+ _dsShapeFadingLevel = va_arg(args, int);
+ if (!_dsShapeFadingLevel)
+ flags &= ~DSF_SHAPE_FADING;
+ }
+
+ if (flags & DSF_TRANSPARENCY) {
+ _dsTransparencyTable1 = va_arg(args, uint8 *);
+ _dsTransparencyTable2 = va_arg(args, uint8 *);
+ }
+
+ if (flags & 0x200) {
+ _drawShapeVar1 = (_drawShapeVar1 + 1) & 0x7;
+ _drawShapeVar3 = drawShapeVar2[_drawShapeVar1];
+ _drawShapeVar4 = 0;
+ _drawShapeVar5 = 256;
+ }
+
+ if (flags & 0x4000)
+ _drawShapeVar5 = va_arg(args, int);
+
+ if (flags & 0x800)
+ _dsDrawLayer = va_arg(args, int);
+
+ if (flags & DSF_SCALE) {
+ _dsScaleW = va_arg(args, int);
+ _dsScaleH = va_arg(args, int);
+ } else {
+ _dsScaleW = 0x100;
+ _dsScaleH = 0x100;
+ }
+
+ if ((flags & DSF_BACKGROUND_FADING) && _vm->game() != GI_KYRA1)
+ _dsBackgroundFadingTable = va_arg(args, uint8 *);
+
+ va_end(args);
+
+ static const DsMarginSkipFunc dsMarginFunc[] = {
+ &Screen::drawShapeMarginNoScaleUpwind,
+ &Screen::drawShapeMarginNoScaleDownwind,
+ &Screen::drawShapeMarginNoScaleUpwind,
+ &Screen::drawShapeMarginNoScaleDownwind,
+ &Screen::drawShapeMarginScaleUpwind,
+ &Screen::drawShapeMarginScaleDownwind,
+ &Screen::drawShapeMarginScaleUpwind,
+ &Screen::drawShapeMarginScaleDownwind
+ };
+
+ static const DsMarginSkipFunc dsSkipFunc[] = {
+ &Screen::drawShapeMarginNoScaleUpwind,
+ &Screen::drawShapeMarginNoScaleDownwind,
+ &Screen::drawShapeMarginNoScaleUpwind,
+ &Screen::drawShapeMarginNoScaleDownwind,
+ &Screen::drawShapeSkipScaleUpwind,
+ &Screen::drawShapeSkipScaleDownwind,
+ &Screen::drawShapeSkipScaleUpwind,
+ &Screen::drawShapeSkipScaleDownwind
+ };
+
+ static const DsLineFunc dsLineFunc[] = {
+ &Screen::drawShapeProcessLineNoScaleUpwind,
+ &Screen::drawShapeProcessLineNoScaleDownwind,
+ &Screen::drawShapeProcessLineNoScaleUpwind,
+ &Screen::drawShapeProcessLineNoScaleDownwind,
+ &Screen::drawShapeProcessLineScaleUpwind,
+ &Screen::drawShapeProcessLineScaleDownwind,
+ &Screen::drawShapeProcessLineScaleUpwind,
+ &Screen::drawShapeProcessLineScaleDownwind
+ };
+
+ static const DsPlotFunc dsPlotFunc[] = {
+ &Screen::drawShapePlotType0, // used by Kyra 1 + 2
+ &Screen::drawShapePlotType1, // used by Kyra 3
+ 0,
+ &Screen::drawShapePlotType3_7, // used by Kyra 3 (shadow)
+ &Screen::drawShapePlotType4, // used by Kyra 1, 2 + 3
+ &Screen::drawShapePlotType5, // used by Kyra 1
+ &Screen::drawShapePlotType6, // used by Kyra 1 (invisibility)
+ &Screen::drawShapePlotType3_7, // used by Kyra 1 (invisibility)
+ &Screen::drawShapePlotType8, // used by Kyra 2
+ &Screen::drawShapePlotType9, // used by Kyra 1 + 3
+ 0,
+ &Screen::drawShapePlotType11_15, // used by Kyra 1 (invisibility) + Kyra 3 (shadow)
+ &Screen::drawShapePlotType12, // used by Kyra 2
+ &Screen::drawShapePlotType13, // used by Kyra 1
+ &Screen::drawShapePlotType14, // used by Kyra 1 (invisibility)
+ &Screen::drawShapePlotType11_15, // used by Kyra 1 (invisibility)
+ &Screen::drawShapePlotType16, // used by LoL PC-98/16 Colors (teleporters),
+ 0, 0, 0,
+ &Screen::drawShapePlotType20, // used by LoL (heal spell effect)
+ &Screen::drawShapePlotType21, // used by LoL (white tower spirits)
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ &Screen::drawShapePlotType33, // used by LoL (blood spots on the floor)
+ 0, 0, 0,
+ &Screen::drawShapePlotType37, // used by LoL (monsters)
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ &Screen::drawShapePlotType48, // used by LoL (slime spots on the floor)
+ 0, 0, 0,
+ &Screen::drawShapePlotType52, // used by LoL (projectiles)
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0
+ };
+
+ int scaleCounterV = 0;
+
+ const int drawFunc = flags & 0x0F;
+ _dsProcessMargin = dsMarginFunc[drawFunc];
+ _dsScaleSkip = dsSkipFunc[drawFunc];
+ _dsProcessLine = dsLineFunc[drawFunc];
+
+ const int ppc = (flags >> 8) & 0x3F;
+ _dsPlot = dsPlotFunc[ppc];
+ DsPlotFunc dsPlot2 = dsPlotFunc[ppc], dsPlot3 = dsPlotFunc[ppc];
+ if (flags & 0x800)
+ dsPlot3 = dsPlotFunc[((flags >> 8) & 0xF7) & 0x3F];
+
+ if (!_dsPlot || !dsPlot2 || !dsPlot3) {
+ if (!dsPlot2)
+ warning("Missing drawShape plotting method type %d", ppc);
+ if (dsPlot3 != dsPlot2 && !dsPlot3)
+ warning("Missing drawShape plotting method type %d", (((flags >> 8) & 0xF7) & 0x3F));
+ return;
+ }
+
+ int curY = y;
+ const uint8 *src = shapeData;
+ uint8 *dst = _dsDstPage = getPagePtr(pageNum);
+
+ const ScreenDim *dsDim = getScreenDim(sd);
+ dst += (dsDim->sx << 3);
+
+ if (!(flags & 0x10))
+ x -= (dsDim->sx << 3);
+
+ int x2 = (dsDim->w << 3);
+ int y1 = dsDim->sy;
+ if (flags & 0x10)
+ y += y1;
+
+ int y2 = y1 + dsDim->h;
+
+ uint16 shapeFlags = READ_LE_UINT16(src); src += 2;
+
+ int shapeHeight = *src++;
+ uint16 shapeWidth = READ_LE_UINT16(src); src += 2;
+
+ int shpWidthScaled1 = shapeWidth;
+ int shpWidthScaled2 = shapeWidth;
+
+ if (flags & DSF_SCALE) {
+ shapeHeight = (shapeHeight * _dsScaleH) >> 8;
+ shpWidthScaled1 = shpWidthScaled2 = (shapeWidth * _dsScaleW) >> 8;
+
+ if (!shapeHeight || !shpWidthScaled1)
+ return;
+ }
+
+ if (flags & DSF_CENTER) {
+ x -= (shpWidthScaled1 >> 1);
+ y -= (shapeHeight >> 1);
+ }
+
+ src += 3;
+
+ uint16 frameSize = READ_LE_UINT16(src); src += 2;
+
+ int colorTableColors = ((_vm->game() != GI_KYRA1) && (shapeFlags & 4)) ? *src++ : 16;
+
+ if (!(flags & 0x8000) && (shapeFlags & 1))
+ _dsColorTable = src;
+
+ if (flags & 0x400)
+ src += colorTableColors;
+
+ if (!(shapeFlags & 2)) {
+ decodeFrame4(src, _animBlockPtr, frameSize);
+ src = _animBlockPtr;
+ }
+
+ int t = (flags & 2) ? y2 - y - shapeHeight : y - y1;
+
+ if (t < 0) {
+ shapeHeight += t;
+ if (shapeHeight <= 0) {
+ return;
+ }
+
+ t *= -1;
+ const uint8 *srcBackUp = 0;
+
+ do {
+ _dsOffscreenScaleVal1 = 0;
+ srcBackUp = src;
+ _dsTmpWidth = shapeWidth;
+
+ int cnt = shapeWidth;
+ (this->*_dsScaleSkip)(dst, src, cnt);
+
+ scaleCounterV += _dsScaleH;
+
+ if (scaleCounterV & 0xFF00) {
+ uint8 r = scaleCounterV >> 8;
+ scaleCounterV &= 0xFF;
+ t -= r;
+ }
+ } while (!(scaleCounterV & 0xFF00) && (t > 0));
+
+ if (t < 0) {
+ src = srcBackUp;
+ scaleCounterV += (-t << 8);
+ }
+
+ if (!(flags & 2))
+ y = y1;
+ }
+
+ t = (flags & 2) ? y + shapeHeight - y1 : y2 - y;
+ if (t <= 0)
+ return;
+
+ if (t < shapeHeight) {
+ shapeHeight = t;
+ if (flags & 2)
+ y = y1;
+ }
+
+ _dsOffscreenLeft = 0;
+ if (x < 0) {
+ shpWidthScaled1 += x;
+ _dsOffscreenLeft = -x;
+ if (_dsOffscreenLeft >= shpWidthScaled2)
+ return;
+ x = 0;
+ }
+
+ _dsOffscreenRight = 0;
+ t = x2 - x;
+
+ if (t <= 0)
+ return;
+
+ if (t < shpWidthScaled1) {
+ shpWidthScaled1 = t;
+ _dsOffscreenRight = shpWidthScaled2 - _dsOffscreenLeft - shpWidthScaled1;
+ }
+
+ int dsPitch = 320;
+ int ty = y;
+
+ if (flags & 2) {
+ dsPitch *= -1;
+ ty = ty - 1 + shapeHeight;
+ }
+
+ if (flags & DSF_X_FLIPPED) {
+ SWAP(_dsOffscreenLeft, _dsOffscreenRight);
+ dst += (shpWidthScaled1 - 1);
+ }
+
+ dst += (320 * ty + x);
+
+ if (flags & DSF_SCALE) {
+ _dsOffscreenRight = 0;
+ _dsOffscreenScaleVal2 = _dsOffscreenLeft;
+ _dsOffscreenLeft <<= 8;
+ _dsOffscreenScaleVal1 = (_dsOffscreenLeft % _dsScaleW) * -1;
+ _dsOffscreenLeft /= _dsScaleW;
+ }
+
+ if (shapeHeight <= 0 || shpWidthScaled1 <= 0)
+ return;
+
+ if (pageNum == 0 || pageNum == 1)
+ addDirtyRect(x, y, shpWidthScaled1, shapeHeight);
+ clearOverlayRect(pageNum, x, y, shpWidthScaled1, shapeHeight);
+
+ uint8 *d = dst;
+
+ bool normalPlot = true;
+ while (true) {
+ while (!(scaleCounterV & 0xFF00)) {
+ scaleCounterV += _dsScaleH;
+ if (!(scaleCounterV & 0xFF00)) {
+ _dsTmpWidth = shapeWidth;
+ int cnt = shapeWidth;
+ (this->*_dsScaleSkip)(d, src, cnt);
+ }
+ }
+
+ const uint8 *b_src = src;
+
+ do {
+ src = b_src;
+ _dsTmpWidth = shapeWidth;
+ int cnt = _dsOffscreenLeft;
+ int scaleState = (this->*_dsProcessMargin)(d, src, cnt);
+
+ if (_dsTmpWidth) {
+ cnt += shpWidthScaled1;
+ if (cnt > 0) {
+ if (flags & 0x800)
+ normalPlot = (curY > _maskMinY && curY < _maskMaxY);
+ _dsPlot = normalPlot ? dsPlot2 : dsPlot3;
+ (this->*_dsProcessLine)(d, src, cnt, scaleState);
+ }
+ cnt += _dsOffscreenRight;
+ if (cnt)
+ (this->*_dsScaleSkip)(d, src, cnt);
+ }
+ dst += dsPitch;
+ d = dst;
+ ++curY;
+
+ if (!--shapeHeight)
+ return;
+
+ scaleCounterV -= 0x100;
+ } while (scaleCounterV & 0xFF00);
+ }
+}
+
+int Screen::drawShapeMarginNoScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt) {
+ while (cnt-- > 0) {
+ if (*src++)
+ continue;
+ cnt = cnt + 1 - (*src++);
+ }
+
+ cnt++;
+ dst -= cnt;
+ return 0;
+}
+
+int Screen::drawShapeMarginNoScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt) {
+ while (cnt-- > 0) {
+ if (*src++)
+ continue;
+ cnt = cnt + 1 - (*src++);
+ }
+
+ cnt++;
+ dst += cnt;
+ return 0;
+}
+
+int Screen::drawShapeMarginScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt) {
+ _dsTmpWidth -= cnt;
+
+ while (cnt > 0) {
+ --cnt;
+ if (*src++)
+ continue;
+
+ cnt = cnt + 1 - (*src++);
+ }
+
+ if (!cnt)
+ return _dsOffscreenScaleVal1;
+
+ _dsTmpWidth += cnt;
+
+ int i = (_dsOffscreenLeft - cnt) * _dsScaleW;
+ int res = i & 0xFF;
+ i >>= 8;
+ i -= _dsOffscreenScaleVal2;
+ dst += i;
+ cnt = -i;
+
+ return res;
+}
+
+int Screen::drawShapeMarginScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt) {
+ _dsTmpWidth -= cnt;
+
+ while (cnt > 0) {
+ --cnt;
+ if (*src++)
+ continue;
+
+ cnt = cnt + 1 - (*src++);
+ }
+
+ if (!cnt)
+ return _dsOffscreenScaleVal1;
+
+ _dsTmpWidth += cnt;
+
+ int i = (_dsOffscreenLeft - cnt) * _dsScaleW;
+ int res = i & 0xFF;
+ i >>= 8;
+ i -= _dsOffscreenScaleVal2;
+ dst -= i;
+ cnt = -i;
+
+ return res;
+}
+
+int Screen::drawShapeSkipScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt) {
+ cnt = _dsTmpWidth;
+
+ if (cnt <= 0)
+ return 0;
+
+ do {
+ --cnt;
+ if (*src++)
+ continue;
+ cnt = cnt + 1 - (*src++);
+ } while (cnt > 0);
+
+ return 0;
+}
+
+int Screen::drawShapeSkipScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt) {
+ cnt = _dsTmpWidth;
+ bool found = false;
+
+ if (cnt == 0)
+ return 0;
+
+ do {
+ --cnt;
+ if (*src++)
+ continue;
+ found = true;
+ cnt = cnt + 1 - (*src++);
+ } while (cnt > 0);
+
+ return found ? 0 : _dsOffscreenScaleVal1;
+}
+
+void Screen::drawShapeProcessLineNoScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt, int16) {
+ do {
+ uint8 c = *src++;
+ if (c) {
+ uint8 *d = dst++;
+ (this->*_dsPlot)(d, c);
+ cnt--;
+ } else {
+ c = *src++;
+ dst += c;
+ cnt -= c;
+ }
+ } while (cnt > 0);
+}
+
+void Screen::drawShapeProcessLineNoScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt, int16) {
+ do {
+ uint8 c = *src++;
+ if (c) {
+ uint8 *d = dst--;
+ (this->*_dsPlot)(d, c);
+ cnt--;
+ } else {
+ c = *src++;
+ dst -= c;
+ cnt -= c;
+ }
+ } while (cnt > 0);
+}
+
+void Screen::drawShapeProcessLineScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt, int16 scaleState) {
+ int c = 0;
+
+ do {
+ if ((scaleState & 0x8000) || !(scaleState & 0xFF00)) {
+ c = *src++;
+ _dsTmpWidth--;
+ if (c) {
+ scaleState += _dsScaleW;
+ } else {
+ _dsTmpWidth++;
+ c = *src++;
+ _dsTmpWidth -= c;
+ int r = c * _dsScaleW + scaleState;
+ dst += (r >> 8);
+ cnt -= (r >> 8);
+ scaleState = r & 0xFF;
+ }
+ } else if (scaleState) {
+ (this->*_dsPlot)(dst++, c);
+ scaleState -= 0x100;
+ cnt--;
+ }
+ } while (cnt > 0);
+
+ cnt = -1;
+}
+
+void Screen::drawShapeProcessLineScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt, int16 scaleState) {
+ int c = 0;
+
+ do {
+ if ((scaleState & 0x8000) || !(scaleState & 0xFF00)) {
+ c = *src++;
+ _dsTmpWidth--;
+ if (c) {
+ scaleState += _dsScaleW;
+ } else {
+ _dsTmpWidth++;
+ c = *src++;
+ _dsTmpWidth -= c;
+ int r = c * _dsScaleW + scaleState;
+ dst -= (r >> 8);
+ cnt -= (r >> 8);
+ scaleState = r & 0xFF;
+ }
+ } else {
+ (this->*_dsPlot)(dst--, c);
+ scaleState -= 0x100;
+ cnt--;
+ }
+ } while (cnt > 0);
+
+ cnt = -1;
+}
+
+void Screen::drawShapePlotType0(uint8 *dst, uint8 cmd) {
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType1(uint8 *dst, uint8 cmd) {
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType3_7(uint8 *dst, uint8 cmd) {
+ cmd = *dst;
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType4(uint8 *dst, uint8 cmd) {
+ *dst = _dsColorTable[cmd];
+}
+
+void Screen::drawShapePlotType5(uint8 *dst, uint8 cmd) {
+ cmd = _dsColorTable[cmd];
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType6(uint8 *dst, uint8 cmd) {
+ int t = _drawShapeVar4 + _drawShapeVar5;
+ if (t & 0xFF00) {
+ cmd = dst[_drawShapeVar3];
+ t &= 0xFF;
+ } else {
+ cmd = _dsColorTable[cmd];
+ }
+
+ _drawShapeVar4 = t;
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType8(uint8 *dst, uint8 cmd) {
+ uint32 relOffs = dst - _dsDstPage;
+ int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
+ if (_dsDrawLayer < t)
+ cmd = _shapePages[1][relOffs];
+
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType9(uint8 *dst, uint8 cmd) {
+ uint32 relOffs = dst - _dsDstPage;
+ int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
+ if (_dsDrawLayer < t) {
+ cmd = _shapePages[1][relOffs];
+ } else {
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+ }
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType11_15(uint8 *dst, uint8 cmd) {
+ uint32 relOffs = dst - _dsDstPage;
+ int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
+
+ if (_dsDrawLayer < t) {
+ cmd = _shapePages[1][relOffs];
+ } else {
+ cmd = *dst;
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+ }
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType12(uint8 *dst, uint8 cmd) {
+ uint32 relOffs = dst - _dsDstPage;
+ int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
+ if (_dsDrawLayer < t) {
+ cmd = _shapePages[1][relOffs];
+ } else {
+ cmd = _dsColorTable[cmd];
+ }
+
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType13(uint8 *dst, uint8 cmd) {
+ uint32 relOffs = dst - _dsDstPage;
+ int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
+ if (_dsDrawLayer < t) {
+ cmd = _shapePages[1][relOffs];
+ } else {
+ cmd = _dsColorTable[cmd];
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+ }
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType14(uint8 *dst, uint8 cmd) {
+ uint32 relOffs = dst - _dsDstPage;
+ int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
+ if (_dsDrawLayer < t) {
+ cmd = _shapePages[1][relOffs];
+ } else {
+ t = _drawShapeVar4 + _drawShapeVar5;
+ if (t & 0xFF00) {
+ cmd = dst[_drawShapeVar3];
+ t &= 0xFF;
+ } else {
+ cmd = _dsColorTable[cmd];
+ }
+ }
+
+ _drawShapeVar4 = t;
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType16(uint8 *dst, uint8 cmd) {
+ uint8 tOffs = _dsTransparencyTable1[cmd];
+ if (!(tOffs & 0x80))
+ cmd = _dsTransparencyTable2[tOffs << 8 | *dst];
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType20(uint8 *dst, uint8 cmd) {
+ cmd = _dsColorTable[cmd];
+ uint8 tOffs = _dsTransparencyTable1[cmd];
+ if (!(tOffs & 0x80))
+ cmd = _dsTransparencyTable2[tOffs << 8 | *dst];
+
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType21(uint8 *dst, uint8 cmd) {
+ cmd = _dsColorTable[cmd];
+ uint8 tOffs = _dsTransparencyTable1[cmd];
+ if (!(tOffs & 0x80))
+ cmd = _dsTransparencyTable2[tOffs << 8 | *dst];
+
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType33(uint8 *dst, uint8 cmd) {
+ if (cmd == 255) {
+ *dst = _dsBackgroundFadingTable[*dst];
+ } else {
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+ if (cmd)
+ *dst = cmd;
+ }
+}
+
+void Screen::drawShapePlotType37(uint8 *dst, uint8 cmd) {
+ cmd = _dsColorTable[cmd];
+
+ if (cmd == 255) {
+ cmd = _dsBackgroundFadingTable[*dst];
+ } else {
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+ }
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType48(uint8 *dst, uint8 cmd) {
+ uint8 offs = _dsTransparencyTable1[cmd];
+ if (!(offs & 0x80))
+ cmd = _dsTransparencyTable2[(offs << 8) | *dst];
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType52(uint8 *dst, uint8 cmd) {
+ cmd = _dsColorTable[cmd];
+ uint8 offs = _dsTransparencyTable1[cmd];
+
+ if (!(offs & 0x80))
+ cmd = _dsTransparencyTable2[(offs << 8) | *dst];
+
+ *dst = cmd;
+}
+
+void Screen::decodeFrame1(const uint8 *src, uint8 *dst, uint32 size) {
+ const uint8 *dstEnd = dst + size;
+
+ struct Pattern {
+ const uint8 *pos;
+ uint16 len;
+ };
+
+ Pattern *patterns = new Pattern[3840];
+ uint16 numPatterns = 0;
+ uint8 nib = 0;
+
+ uint16 code = decodeEGAGetCode(src, nib);
+ uint8 last = code & 0xFF;
+
+ uint8 *dstPrev = dst;
+ uint16 count = 1;
+ uint16 countPrev = 1;
+
+ *dst++ = last;
+
+ while (dst < dstEnd) {
+ code = decodeEGAGetCode(src, nib);
+ uint8 cmd = code >> 8;
+
+ if (cmd--) {
+ code = (cmd << 8) | (code & 0xFF);
+ uint8 *tmpDst = dst;
+
+ if (code < numPatterns) {
+ const uint8 *tmpSrc = patterns[code].pos;
+ countPrev = patterns[code].len;
+ last = *tmpSrc;
+ for (int i = 0; i < countPrev; i++)
+ *dst++ = *tmpSrc++;
+
+ } else {
+ const uint8 *tmpSrc = dstPrev;
+ count = countPrev;
+ for (int i = 0; i < countPrev; i++)
+ *dst++ = *tmpSrc++;
+ *dst++ = last;
+ countPrev++;
+ }
+
+ if (numPatterns < 3840) {
+ patterns[numPatterns].pos = dstPrev;
+ patterns[numPatterns++].len = ++count;
+ }
+
+ dstPrev = tmpDst;
+ count = countPrev;
+
+ } else {
+ *dst++ = last = (code & 0xFF);
+
+ if (numPatterns < 3840) {
+ patterns[numPatterns].pos = dstPrev;
+ patterns[numPatterns++].len = ++count;
+ }
+
+ dstPrev = dst - 1;
+ count = 1;
+ countPrev = 1;
+ }
+ }
+ delete[] patterns;
+}
+
+uint16 Screen::decodeEGAGetCode(const uint8 *&pos, uint8 &nib) {
+ uint16 res = READ_BE_UINT16(pos++);
+ if ((++nib) & 1) {
+ res >>= 4;
+ } else {
+ pos++;
+ res &= 0xFFF;
+ }
+ return res;
+}
+
+void Screen::decodeFrame3(const uint8 *src, uint8 *dst, uint32 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;
+ }
+ }
+}
+
+uint Screen::decodeFrame4(const uint8 *src, uint8 *dst, uint32 dstSize) {
+ uint8 *dstOrig = dst;
+ uint8 *dstEnd = dst + dstSize;
+ while (1) {
+ int count = dstEnd - dst;
+ if (count == 0)
+ break;
+
+ uint8 code = *src++;
+ if (!(code & 0x80)) { // 8th bit isn't set
+ int len = MIN(count, (code >> 4) + 3); //upper half of code is the length
+ int offs = ((code & 0xF) << 8) | *src++; //lower half of code as byte 2 of offset.
+ const uint8 *dstOffs = dst - offs;
+ while (len--)
+ *dst++ = *dstOffs++;
+ } else if (code & 0x40) { // 7th bit is set
+ 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) { // not just the 8th bit set.
+ //Copy some bytes from source to dest.
+ int len = MIN(count, code & 0x3F);
+ while (len--)
+ *dst++ = *src++;
+ } else {
+ break;
+ }
+ }
+ return dst - dstOrig;
+}
+
+void Screen::decodeFrameDelta(uint8 *dst, const uint8 *src, bool noXor) {
+ if (noXor)
+ wrapped_decodeFrameDelta<true>(dst, src);
+ else
+ wrapped_decodeFrameDelta<false>(dst, src);
+}
+
+template<bool noXor>
+void Screen::wrapped_decodeFrameDelta(uint8 *dst, const uint8 *src) {
+ while (1) {
+ uint8 code = *src++;
+ if (code == 0) {
+ uint8 len = *src++;
+ code = *src++;
+ while (len--) {
+ if (noXor)
+ *dst++ = code;
+ else
+ *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--) {
+ if (noXor)
+ *dst++ = code;
+ else
+ *dst++ ^= code;
+ }
+ } else {
+ while (subcode--) {
+ if (noXor)
+ *dst++ = *src++;
+ else
+ *dst++ ^= *src++;
+ }
+ }
+ } else {
+ dst += subcode;
+ }
+ }
+ } else {
+ while (code--) {
+ if (noXor)
+ *dst++ = *src++;
+ else
+ *dst++ ^= *src++;
+ }
+ }
+ }
+}
+
+void Screen::decodeFrameDeltaPage(uint8 *dst, const uint8 *src, int pitch, bool noXor) {
+ if (noXor)
+ wrapped_decodeFrameDeltaPage<true>(dst, src, pitch);
+ else
+ wrapped_decodeFrameDeltaPage<false>(dst, src, pitch);
+}
+
+void Screen::convertAmigaGfx(uint8 *data, int w, int h, int depth, bool wsa, int bytesPerPlane) {
+ const int planeWidth = (bytesPerPlane == -1) ? (w + 7) / 8 : bytesPerPlane;
+ const int planeSize = planeWidth * h;
+ const uint imageSize = planeSize * depth;
+
+ // Our static buffer which holds the plane data. We need this
+ // because the "data" pointer is both source and destination pointer.
+ // The buffer has enough space to fit the AMIGA MSC files, which are
+ // the biggest graphics files found in the AMIGA version.
+ static uint8 temp[40320];
+ assert(imageSize <= sizeof(temp));
+
+ // WSA files store their graphics data in a little different format, than
+ // the usual AMIGA graphics format used in BitMaps. Thus we need to do
+ // some special handling for them here. Means we convert them into
+ // the usual format.
+ //
+ // TODO: We might think of moving this conversion into the WSAMovieAmiga
+ // class.
+ if (wsa) {
+ const byte *src = data;
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < planeWidth; ++x)
+ for (int i = 0; i < depth; ++i)
+ temp[y * planeWidth + x + planeSize * i] = *src++;
+ }
+ } else {
+ memcpy(temp, data, imageSize);
+ }
+
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < w; ++x) {
+ const int bytePos = x / 8 + y * planeWidth;
+ const int bitPos = 7 - (x & 7); // x & 7 == x % 8
+
+ byte col = 0;
+
+ for (int i = 0; i < depth; ++i)
+ col |= ((temp[bytePos + planeSize * i] >> bitPos) & 1) << i;
+
+ *data++ = col;
+ }
+ }
+}
+
+void Screen::convertAmigaMsc(uint8 *data) {
+ // MSC files are always 320x144, thus we can safely assume
+ // this to be correct. Also they contain 7 planes instead
+ // of the normal 5 planes, which is used in 32 color mode.
+ // The need for 7 planes can be explained, because the MSC
+ // files have 6 bits for the layer number (bits 1 to 6)
+ // and one bit for the "blocked" flag (bit 0), and every
+ // plane contains one bit per pixel.
+ convertAmigaGfx(data, 320, 144, 7);
+
+ // We need to do some post conversion, since
+ // the AMIGA MSC format is different from the DOS
+ // one we use internally for our code.That is even
+ // after converting it from the AMIGA plane based
+ // approach to one byte per pixel approach.
+ for (int i = 0; i < 320 * 144; ++i) {
+ // The lowest bit indicates, whether the position
+ // is walkable or not. If the bit is set, the
+ // position is walkable, elsewise it is blocked.
+ if (data[i] & 1)
+ data[i] &= 0xFE;
+ else
+ data[i] |= 0x80;
+
+ // The graphics layer for the pixel is saved
+ // in the following format:
+ // The highest bit set indicates the number of
+ // the graphics layer. We count the first
+ // bit as 0 here, thus we need to add one,
+ // to get the correct number.
+ //
+ // Funnily since the first bit (bit 0) is
+ // resevered for testing whether the position
+ // is walkable or not, there is no possibility
+ // for layer 1 to be present.
+ int layer = 0;
+ for (int k = 0; k < 7; ++k)
+ if (data[i] & (1 << k))
+ layer = k + 1;
+
+ data[i] &= 0x80;
+ data[i] |= layer;
+ }
+}
+
+template<bool noXor>
+void Screen::wrapped_decodeFrameDeltaPage(uint8 *dst, const uint8 *src, int pitch) {
+ 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) {
+ 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->gameFlags().useAltShapeHeader)
+ shapeSize += 12;
+ else
+ shapeSize += 10;
+
+ if (flags & 1)
+ shapeSize += 16;
+
+ uint8 table[274];
+ int tableIndex = 0;
+
+ uint8 *newShape = 0;
+ newShape = new uint8[shapeSize+16];
+ assert(newShape);
+
+ byte *dst = newShape;
+
+ if (_vm->gameFlags().useAltShapeHeader)
+ 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(table));
+ 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 = table[value];
+ }
+ } 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->gameFlags().useAltShapeHeader)
+ dst += 2;
+
+ flags = READ_LE_UINT16(dst);
+ flags |= 2;
+ WRITE_LE_UINT16(dst, flags);
+ } else {
+ src = newShape;
+ if (_vm->gameFlags().useAltShapeHeader)
+ 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;
+ uint8 *newShape2 = new uint8[shapeSize];
+ assert(newShape2);
+ memcpy(newShape2, newShape, shapeSize);
+ delete[] newShape;
+ newShape = newShape2;
+ } else {
+ dst = shapePtrBackUp;
+ src = _animBlockPtr;
+ memcpy(dst, src, shapeSize2);
+ dst = newShape;
+ if (_vm->gameFlags().useAltShapeHeader)
+ dst += 2;
+ flags = READ_LE_UINT16(dst);
+ flags |= 2;
+ WRITE_LE_UINT16(dst, flags);
+ }
+ }
+ }
+
+ dst = newShape;
+ if (_vm->gameFlags().useAltShapeHeader)
+ dst += 2;
+ WRITE_LE_UINT16((dst + 6), shapeSize);
+
+ if (flags & 1) {
+ dst = newShape + 10;
+ if (_vm->gameFlags().useAltShapeHeader)
+ dst += 2;
+ src = &table[0x100];
+ memcpy(dst, src, sizeof(uint8)*16);
+ }
+
+ return newShape;
+}
+
+int16 Screen::encodeShapeAndCalculateSize(uint8 *from, uint8 *to, int 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++ = curPixel;
+ 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-1) == curPixel) {
+ if (*(from+size-1) != *(to+size-2))
+ continue;
+
+ byte *fromBackUp = from;
+ byte *toBackUp = to;
+ --to;
+ const int checkSize = fromPtrEnd - from;
+ for (int i = 0; i < checkSize; ++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 (diffSize <= 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);
+}
+
+uint16 Screen::shade16bitColor(uint16 col) {
+ uint8 r = (col & 0x1f);
+ uint8 g = (col & 0x3E0) >> 5;
+ uint8 b = (col & 0x7C00) >> 10;
+
+ r = (r > _16bitShadingLevel) ? r - _16bitShadingLevel : 0;
+ g = (g > _16bitShadingLevel) ? g - _16bitShadingLevel : 0;
+ b = (b > _16bitShadingLevel) ? b - _16bitShadingLevel : 0;
+
+ return (b << 10) | (g << 5) | r;
+}
+
+void Screen::hideMouse() {
+ ++_mouseLockCount;
+ CursorMan.showMouse(false);
+}
+
+void Screen::showMouse() {
+ if (_mouseLockCount == 1) {
+ CursorMan.showMouse(true);
+
+ // We need to call OSystem::updateScreen here, else the mouse cursor
+ // will only be visible on mouse movment.
+ _system->updateScreen();
+ }
+
+ if (_mouseLockCount > 0)
+ _mouseLockCount--;
+}
+
+
+bool Screen::isMouseVisible() const {
+ return _mouseLockCount == 0;
+}
+
+void Screen::setShapePages(int page1, int page2, int minY, int maxY) {
+ _shapePages[0] = _pagePtrs[page1];
+ _shapePages[1] = _pagePtrs[page2];
+ _maskMinY = minY;
+ _maskMaxY = maxY;
+}
+
+void Screen::setMouseCursor(int x, int y, const byte *shape) {
+ if (!shape)
+ return;
+
+ if (_vm->gameFlags().useAltShapeHeader)
+ shape += 2;
+
+ int mouseHeight = *(shape + 2);
+ int mouseWidth = (READ_LE_UINT16(shape + 3)) + 2;
+
+ if (_vm->gameFlags().useAltShapeHeader)
+ shape -= 2;
+
+ if (_vm->gameFlags().useHiRes) {
+ x <<= 1;
+ y <<= 1;
+ mouseWidth <<= 1;
+ mouseHeight <<= 1;
+ }
+
+ uint8 *cursor = new uint8[mouseHeight * mouseWidth];
+ fillRect(0, 0, mouseWidth, mouseHeight, _cursorColorKey, 8);
+ drawShape(8, shape, 0, 0, 0, 0);
+
+ int xOffset = 0;
+
+ if (_vm->gameFlags().useHiRes) {
+ xOffset = mouseWidth;
+ scale2x(getPagePtr(8) + mouseWidth, SCREEN_W, getPagePtr(8), SCREEN_W, mouseWidth, mouseHeight);
+ postProcessCursor(getPagePtr(8) + mouseWidth, mouseWidth, mouseHeight, SCREEN_W);
+ } else {
+ postProcessCursor(getPagePtr(8), mouseWidth, mouseHeight, SCREEN_W);
+ }
+
+ CursorMan.showMouse(false);
+ copyRegionToBuffer(8, xOffset, 0, mouseWidth, mouseHeight, cursor);
+ CursorMan.replaceCursor(cursor, mouseWidth, mouseHeight, 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();
+}
+
+Palette &Screen::getPalette(int num) {
+ assert(num >= 0 && (uint)num < _palettes.size());
+ return *_palettes[num];
+}
+
+void Screen::copyPalette(const int dst, const int src) {
+ getPalette(dst).copy(getPalette(src));
+}
+
+byte Screen::getShapeFlag1(int x, int 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) {
+ uint8 color = _shapePages[0][y * SCREEN_W + x];
+ color &= 0x7F;
+ color &= 0x87;
+ return color;
+}
+
+int Screen::getDrawLayer(int x, int y) {
+ int xpos = x - 8;
+ int ypos = y - 1;
+ int layer = 1;
+
+ for (int curX = xpos; curX < xpos + 16; ++curX) {
+ int tempLayer = getShapeFlag2(curX, ypos);
+
+ if (layer < tempLayer)
+ layer = tempLayer;
+
+ if (layer >= 7)
+ return 7;
+ }
+ return layer;
+}
+
+int Screen::getDrawLayer2(int x, int y, int height) {
+ int xpos = x - 8;
+ int ypos = y - 1;
+ int layer = 1;
+
+ for (int useX = xpos; useX < xpos + 16; ++useX) {
+ for (int useY = ypos - height; useY < ypos; ++useY) {
+ int tempLayer = getShapeFlag2(useX, useY);
+
+ if (tempLayer > layer)
+ layer = tempLayer;
+
+ if (tempLayer >= 7)
+ return 7;
+ }
+ }
+ return layer;
+}
+
+
+int Screen::setNewShapeHeight(uint8 *shape, int height) {
+ if (_vm->gameFlags().useAltShapeHeader)
+ shape += 2;
+
+ int oldHeight = shape[2];
+ shape[2] = height;
+ return oldHeight;
+}
+
+int Screen::resetShapeHeight(uint8 *shape) {
+ if (_vm->gameFlags().useAltShapeHeader)
+ shape += 2;
+
+ int oldHeight = shape[2];
+ shape[2] = shape[5];
+ return oldHeight;
+}
+
+void Screen::blockInRegion(int x, int y, int width, int height) {
+ assert(_shapePages[0]);
+ byte *toPtr = _shapePages[0] + (y * 320 + x);
+ for (int i = 0; i < height; ++i) {
+ byte *backUpTo = toPtr;
+ for (int i2 = 0; i2 < width; ++i2)
+ *toPtr++ &= 0x7F;
+ toPtr = (backUpTo + 320);
+ }
+}
+
+void Screen::blockOutRegion(int x, int y, int width, int height) {
+ assert(_shapePages[0]);
+ byte *toPtr = _shapePages[0] + (y * 320 + x);
+ for (int i = 0; i < height; ++i) {
+ byte *backUpTo = toPtr;
+ for (int i2 = 0; i2 < width; ++i2)
+ *toPtr++ |= 0x80;
+ toPtr = (backUpTo + 320);
+ }
+}
+
+void Screen::rectClip(int &x, int &y, int w, int h) {
+ if (x < 0)
+ x = 0;
+ else if (x + w >= 320)
+ x = 320 - w;
+
+ if (y < 0)
+ y = 0;
+ else if (y + h >= 200)
+ y = 200 - h;
+}
+
+void Screen::shakeScreen(int times) {
+ while (times--) {
+ // seems to be 1 line (320 pixels) offset in the original
+ // 4 looks more like dosbox though, maybe check this again
+ _system->setShakePos(4);
+ _system->updateScreen();
+ _system->setShakePos(0);
+ _system->updateScreen();
+ }
+}
+
+void Screen::loadBitmap(const char *filename, int tempPage, int dstPage, Palette *pal, bool skip) {
+ uint32 fileSize;
+ uint8 *srcData = _vm->resource()->fileData(filename, &fileSize);
+
+ if (!srcData) {
+ warning("couldn't load bitmap: '%s'", filename);
+ return;
+ }
+
+ if (skip)
+ srcData += 4;
+
+ const char *ext = filename + strlen(filename) - 3;
+ uint8 compType = srcData[2];
+ uint32 imgSize = (_vm->game() == GI_KYRA2 && !scumm_stricmp(ext, "CMP")) ? READ_LE_UINT16(srcData) : READ_LE_UINT32(srcData + 4);
+ uint16 palSize = READ_LE_UINT16(srcData + 8);
+
+ if (pal && palSize)
+ loadPalette(srcData + 10, *pal, palSize);
+
+ uint8 *srcPtr = srcData + 10 + palSize;
+ uint8 *dstData = getPagePtr(dstPage);
+ memset(dstData, 0, _screenPageSize);
+ if (dstPage == 0 || tempPage == 0)
+ _forceFullUpdate = true;
+
+ switch (compType) {
+ case 0:
+ memcpy(dstData, srcPtr, imgSize);
+ break;
+ case 1:
+ Screen::decodeFrame1(srcPtr, dstData, imgSize);
+ break;
+ case 3:
+ Screen::decodeFrame3(srcPtr, dstData, imgSize);
+ break;
+ case 4:
+ Screen::decodeFrame4(srcPtr, dstData, imgSize);
+ break;
+ default:
+ error("Unhandled bitmap compression %d", compType);
+ }
+
+ if (_isAmiga) {
+ if (!scumm_stricmp(ext, "MSC"))
+ Screen::convertAmigaMsc(dstData);
+ else
+ Screen::convertAmigaGfx(dstData, 320, 200);
+ }
+
+ if (skip)
+ srcData -= 4;
+
+ delete[] srcData;
+}
+
+bool Screen::loadPalette(const char *filename, Palette &pal) {
+ if (_renderMode == Common::kRenderCGA)
+ return true;
+
+ Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename);
+
+ if (!stream)
+ return false;
+
+ debugC(3, kDebugLevelScreen, "Screen::loadPalette('%s', %p)", filename, (const void *)&pal);
+
+ const int maxCols = _16bitPalette ? 256 : pal.getNumColors();
+ int numCols = 0;
+
+ if (_isAmiga) {
+ numCols = stream->size() / Palette::kAmigaBytesPerColor;
+ pal.loadAmigaPalette(*stream, 0, MIN(maxCols, numCols));
+ } else if (_vm->gameFlags().platform == Common::kPlatformPC98 && _use16ColorMode) {
+ numCols = stream->size() / Palette::kPC98BytesPerColor;
+ pal.loadPC98Palette(*stream, 0, MIN(maxCols, numCols));
+ } else if (_renderMode == Common::kRenderEGA) {
+ numCols = stream->size();
+ // There aren't any 16 color EGA palette files. So this shouldn't ever get triggered.
+ assert (numCols != 16);
+ numCols /= Palette::kVGABytesPerColor;
+ pal.loadVGAPalette(*stream, 0, numCols);
+ } else {
+ if (_bytesPerPixel == 2) {
+ numCols = stream->size() / 2;
+ pal.loadHiColorPalette(*stream, 0, numCols);
+ } else if (!_16bitPalette) {
+ numCols = stream->size() / Palette::kVGABytesPerColor;
+ pal.loadVGAPalette(*stream, 0, MIN(maxCols, numCols));
+ } else {
+ error("Screen::loadPalette(): Failed to load file '%s' with invalid size %d in HiColor mode", filename, stream->size());
+ }
+ }
+
+ if (numCols > maxCols)
+ warning("Palette file '%s' includes %d colors, but the target palette only support %d colors", filename, numCols, maxCols);
+
+ delete stream;
+ return true;
+}
+
+bool Screen::loadPaletteTable(const char *filename, int firstPalette) {
+ Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename);
+
+ if (!stream)
+ return false;
+
+ debugC(3, kDebugLevelScreen, "Screen::loadPaletteTable('%s', %d)", filename, firstPalette);
+
+ if (_isAmiga) {
+ const int numColors = getPalette(firstPalette).getNumColors();
+ const int palSize = getPalette(firstPalette).getNumColors() * Palette::kAmigaBytesPerColor;
+ const int numPals = stream->size() / palSize;
+
+ for (int i = 0; i < numPals; ++i)
+ getPalette(i + firstPalette).loadAmigaPalette(*stream, 0, numColors);
+ } else {
+ const int numColors = getPalette(firstPalette).getNumColors();
+ const int palSize = getPalette(firstPalette).getNumColors() * Palette::kVGABytesPerColor;
+ const int numPals = stream->size() / palSize;
+
+ for (int i = 0; i < numPals; ++i)
+ getPalette(i + firstPalette).loadVGAPalette(*stream, 0, numColors);
+ }
+
+ delete stream;
+ return true;
+}
+
+void Screen::loadPalette(const byte *data, Palette &pal, int bytes) {
+ Common::MemoryReadStream stream(data, bytes, DisposeAfterUse::NO);
+
+ if (_isAmiga)
+ pal.loadAmigaPalette(stream, 0, stream.size() / Palette::kAmigaBytesPerColor);
+ else if (_vm->gameFlags().platform == Common::kPlatformPC98 && _use16ColorMode)
+ pal.loadPC98Palette(stream, 0, stream.size() / Palette::kPC98BytesPerColor);
+ else if (_renderMode == Common::kRenderEGA) {
+ // EOB II checks the number of palette bytes to distinguish between real EGA palettes
+ // and normal palettes (which are used to generate a color map).
+ if (stream.size() == 16)
+ pal.loadEGAPalette(stream, 0, stream.size());
+ else
+ pal.loadVGAPalette(stream, 0, stream.size() / Palette::kVGABytesPerColor);
+ } else
+ pal.loadVGAPalette(stream, 0, stream.size() / Palette::kVGABytesPerColor);
+}
+
+// dirty rect handling
+
+void Screen::addDirtyRect(int x, int y, int w, int h) {
+ if (_dirtyRects.size() >= kMaxDirtyRects || _forceFullUpdate) {
+ _forceFullUpdate = true;
+ return;
+ }
+
+ Common::Rect r(x, y, x + w, y + h);
+
+ // Clip rectangle
+ r.clip(SCREEN_W, SCREEN_H);
+
+ // If it is empty after clipping, we are done
+ if (r.isEmpty())
+ return;
+
+ // Check if the new rectangle is contained within another in the list
+ Common::List<Common::Rect>::iterator it;
+ for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ) {
+ // If we find a rectangle which fully contains the new one,
+ // we can abort the search.
+ if (it->contains(r))
+ return;
+
+ // Conversely, if we find rectangles which are contained in
+ // the new one, we can remove them
+ if (r.contains(*it))
+ it = _dirtyRects.erase(it);
+ else
+ ++it;
+ }
+
+ // If we got here, we can safely add r to the list of dirty rects.
+ _dirtyRects.push_back(r);
+}
+
+// overlay functions
+
+byte *Screen::getOverlayPtr(int page) {
+ if (page == 0 || page == 1)
+ return _sjisOverlayPtrs[1];
+ else if (page == 2 || page == 3)
+ return _sjisOverlayPtrs[2];
+
+ if (_vm->game() == GI_KYRA2) {
+ if (page == 12 || page == 13)
+ return _sjisOverlayPtrs[3];
+ } else if (_vm->game() == GI_LOL) {
+ if (page == 4 || page == 5)
+ return _sjisOverlayPtrs[3];
+ if (page == 6 || page == 7)
+ return _sjisOverlayPtrs[4];
+ if (page == 12 || page == 13)
+ return _sjisOverlayPtrs[5];
+ }
+
+ return 0;
+}
+
+void Screen::clearOverlayPage(int page) {
+ byte *dst = getOverlayPtr(page);
+ if (!dst)
+ return;
+ memset(dst, _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE);
+}
+
+void Screen::clearOverlayRect(int page, int x, int y, int w, int h) {
+ byte *dst = getOverlayPtr(page);
+
+ if (!dst || w < 0 || h < 0)
+ return;
+
+ x <<= 1; y <<= 1;
+ w <<= 1; h <<= 1;
+
+ dst += y * 640 + x;
+
+ if (w == 640 && h == 400) {
+ memset(dst, _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE);
+ } else {
+ while (h--) {
+ memset(dst, _sjisInvisibleColor, w);
+ dst += 640;
+ }
+ }
+}
+
+void Screen::copyOverlayRegion(int x, int y, int x2, int y2, int w, int h, int srcPage, int dstPage) {
+ byte *dst = getOverlayPtr(dstPage);
+ const byte *src = getOverlayPtr(srcPage);
+
+ if (!dst || !src)
+ return;
+
+ x <<= 1; x2 <<= 1;
+ y <<= 1; y2 <<= 1;
+ w <<= 1; h <<= 1;
+
+ if (w == 640 && h == 400) {
+ memcpy(dst, src, SCREEN_OVL_SJIS_SIZE);
+ } else {
+ dst += y2 * 640 + x2;
+ src += y * 640 + x;
+
+ while (h--) {
+ for (x = 0; x < w; ++x)
+ memmove(dst, src, w);
+ dst += 640;
+ src += 640;
+ }
+ }
+}
+
+void Screen::crossFadeRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage) {
+ if (srcPage > 13 || dstPage > 13)
+ error("Screen::crossFadeRegion(): attempting to use temp page as source or dest page.");
+
+ hideMouse();
+
+ uint16 *wB = (uint16 *)_pagePtrs[14];
+ uint8 *hB = _pagePtrs[14] + 640 * _bytesPerPixel;
+
+ for (int i = 0; i < w; i++)
+ wB[i] = i;
+
+ for (int i = 0; i < h; i++)
+ hB[i] = i;
+
+ for (int i = 0; i < w; i++)
+ SWAP(wB[_vm->_rnd.getRandomNumberRng(0, w - 1)], wB[i]);
+
+ for (int i = 0; i < h; i++)
+ SWAP(hB[_vm->_rnd.getRandomNumberRng(0, h - 1)], hB[i]);
+
+ uint8 *s = _pagePtrs[srcPage];
+ uint8 *d = _pagePtrs[dstPage];
+
+ for (int i = 0; i < h; i++) {
+ int iH = i;
+ uint32 end = _system->getMillis() + 3;
+ for (int ii = 0; ii < w; ii++) {
+ int sX = (x1 + wB[ii]);
+ int sY = (y1 + hB[iH]);
+ int dX = (x2 + wB[ii]);
+ int dY = (y2 + hB[iH]);
+
+ if (++iH >= h)
+ iH = 0;
+
+ if (_bytesPerPixel == 2)
+ ((uint16*)d)[dY * 320 + dX] = ((uint16*)s)[sY * 320 + sX];
+ else
+ d[dY * 320 + dX] = s[sY * 320 + sX];
+ addDirtyRect(dX, dY, 1, 1);
+ }
+
+ // This tries to speed things up, to get similiar speeds as in DOSBox etc.
+ // We can't write single pixels directly into the video memory like the original did.
+ // We also (unlike the original) want to aim at similiar speeds for all platforms.
+ if (!(i % 10))
+ updateScreen();
+
+ uint32 cur = _system->getMillis();
+ if (end > cur)
+ _system->delayMillis(end - cur);
+ }
+
+ updateScreen();
+ showMouse();
+}
+
+#pragma mark -
+
+DOSFont::DOSFont() {
+ _data = _widthTable = _heightTable = 0;
+ _colorMap = 0;
+ _width = _height = _numGlyphs = 0;
+ _bitmapOffsets = 0;
+}
+
+bool DOSFont::load(Common::SeekableReadStream &file) {
+ unload();
+
+ _data = new uint8[file.size()];
+ assert(_data);
+
+ file.read(_data, file.size());
+ if (file.err())
+ return false;
+
+ const uint16 fontSig = READ_LE_UINT16(_data + 2);
+
+ if (fontSig != 0x0500) {
+ warning("DOSFont: invalid font: %.04X)", fontSig);
+ return false;
+ }
+
+ const uint16 descOffset = READ_LE_UINT16(_data + 4);
+
+ _width = _data[descOffset + 5];
+ _height = _data[descOffset + 4];
+ _numGlyphs = _data[descOffset + 3] + 1;
+
+ _bitmapOffsets = (uint16 *)(_data + READ_LE_UINT16(_data + 6));
+ _widthTable = _data + READ_LE_UINT16(_data + 8);
+ _heightTable = _data + READ_LE_UINT16(_data + 12);
+
+ for (int i = 0; i < _numGlyphs; ++i)
+ _bitmapOffsets[i] = READ_LE_UINT16(&_bitmapOffsets[i]);
+
+ return true;
+}
+
+int DOSFont::getCharWidth(uint16 c) const {
+ if (c >= _numGlyphs)
+ return 0;
+ return _widthTable[c];
+}
+
+void DOSFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
+ if (c >= _numGlyphs)
+ return;
+
+ if (!_bitmapOffsets[c])
+ return;
+
+ const uint8 *src = _data + _bitmapOffsets[c];
+ const uint8 charWidth = _widthTable[c];
+
+ if (!charWidth)
+ return;
+
+ pitch -= charWidth;
+
+ uint8 charH1 = _heightTable[c * 2 + 0];
+ uint8 charH2 = _heightTable[c * 2 + 1];
+ uint8 charH0 = _height - (charH1 + charH2);
+
+ while (charH1--) {
+ uint8 col = _colorMap[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 = _colorMap[b >> 4];
+ } else {
+ b = *src++;
+ col = _colorMap[b & 0xF];
+ }
+ if (col != 0) {
+ *dst = col;
+ }
+ ++dst;
+ }
+ dst += pitch;
+ }
+
+ while (charH0--) {
+ uint8 col = _colorMap[0];
+ for (int i = 0; i < charWidth; ++i) {
+ if (col != 0)
+ *dst = col;
+ ++dst;
+ }
+ dst += pitch;
+ }
+}
+
+void DOSFont::unload() {
+ delete[] _data;
+ _data = _widthTable = _heightTable = 0;
+ _colorMap = 0;
+ _width = _height = _numGlyphs = 0;
+ _bitmapOffsets = 0;
+}
+
+
+AMIGAFont::AMIGAFont() {
+ _width = _height = 0;
+ memset(_chars, 0, sizeof(_chars));
+}
+
+bool AMIGAFont::load(Common::SeekableReadStream &file) {
+ const uint16 dataSize = file.readUint16BE();
+ if (dataSize + 2 != file.size())
+ return false;
+
+ _width = file.readByte();
+ _height = file.readByte();
+
+ // Read the character definition offset table
+ uint16 offsets[ARRAYSIZE(_chars)];
+ for (int i = 0; i < ARRAYSIZE(_chars); ++i)
+ offsets[i] = file.readUint16BE() + 4;
+
+ if (file.err())
+ return false;
+
+ for (int i = 0; i < ARRAYSIZE(_chars); ++i) {
+ file.seek(offsets[i], SEEK_SET);
+
+ _chars[i].yOffset = file.readByte();
+ _chars[i].xOffset = file.readByte();
+ _chars[i].width = file.readByte();
+ file.readByte(); // unused
+
+ // If the y offset is 255, then the character
+ // does not have any bitmap representation
+ if (_chars[i].yOffset != 255) {
+ Character::Graphics &g = _chars[i].graphics;
+
+ g.width = file.readUint16BE();
+ g.height = file.readUint16BE();
+
+ int depth = file.readByte();
+ int specialWidth = file.readByte();
+ int flags = file.readByte();
+ int bytesPerPlane = file.readByte();
+
+ assert(depth != 0 && specialWidth == 0 && flags == 0 && bytesPerPlane != 0);
+
+ // Allocate a temporary buffer to store the plane data
+ const int planesSize = bytesPerPlane * g.height * depth;
+ uint8 *tempData = new uint8[MAX(g.width * g.height, planesSize)];
+ assert(tempData);
+
+ file.read(tempData, planesSize);
+
+ // Convert the plane based graphics to our graphic format
+ Screen::convertAmigaGfx(tempData, g.width, g.height, depth, false, bytesPerPlane);
+
+ // Create a buffer perfectly fitting the character
+ g.bitmap = new uint8[g.width * g.height];
+ assert(g.bitmap);
+
+ memcpy(g.bitmap, tempData, g.width * g.height);
+ delete[] tempData;
+ }
+
+ if (file.err())
+ return false;
+ }
+
+ return !file.err();
+}
+
+int AMIGAFont::getCharWidth(uint16 c) const {
+ if (c >= 255)
+ return 0;
+ return _chars[c].width;
+}
+
+void AMIGAFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
+ if (c >= 255)
+ return;
+
+ if (_chars[c].yOffset == 255)
+ return;
+
+ dst += _chars[c].yOffset * pitch;
+ dst += _chars[c].xOffset;
+
+ pitch -= _chars[c].graphics.width;
+
+ const uint8 *src = _chars[c].graphics.bitmap;
+ assert(src);
+
+ for (int y = 0; y < _chars[c].graphics.height; ++y) {
+ for (int x = 0; x < _chars[c].graphics.width; ++x) {
+ if (*src)
+ *dst = *src;
+ ++src;
+ ++dst;
+ }
+
+ dst += pitch;
+ }
+}
+
+void AMIGAFont::unload() {
+ _width = _height = 0;
+ for (int i = 0; i < ARRAYSIZE(_chars); ++i)
+ delete[] _chars[i].graphics.bitmap;
+ memset(_chars, 0, sizeof(_chars));
+}
+
+SJISFont::SJISFont(Graphics::FontSJIS *font, const uint8 invisColor, bool is16Color, bool drawOutline, bool fatPrint, int extraSpacing)
+ : _colorMap(0), _font(font), _invisColor(invisColor), _is16Color(is16Color), _drawOutline(drawOutline), _sjisWidthOffset(extraSpacing) {
+ assert(_font);
+ _font->setDrawingMode(_drawOutline ? Graphics::FontSJIS::kOutlineMode : Graphics::FontSJIS::kDefaultMode);
+ _font->toggleFatPrint(fatPrint);
+ _sjisWidth = _font->getMaxFontWidth() >> 1;
+ _fontHeight = _font->getFontHeight() >> 1;
+ _asciiWidth = _font->getCharWidth('a') >> 1;
+}
+
+void SJISFont::unload() {
+ delete _font;
+ _font = 0;
+}
+
+int SJISFont::getHeight() const {
+ return _fontHeight;
+}
+
+int SJISFont::getWidth() const {
+ return _sjisWidth + _sjisWidthOffset;
+}
+
+int SJISFont::getCharWidth(uint16 c) const {
+ if (c <= 0x7F || (c >= 0xA1 && c <= 0xDF))
+ return _asciiWidth;
+ else
+ return _sjisWidth + _sjisWidthOffset;
+}
+
+void SJISFont::setColorMap(const uint8 *src) {
+ _colorMap = src;
+
+ if (!_is16Color) {
+ if (_colorMap[0] == _invisColor)
+ _font->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
+ else
+ _font->setDrawingMode(_drawOutline ? Graphics::FontSJIS::kOutlineMode : Graphics::FontSJIS::kDefaultMode);
+ }
+}
+
+void SJISFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
+ uint8 color1, color2;
+
+ if (_is16Color) {
+ // PC98 16 color games specify a color value which is for the
+ // PC98 text mode palette, thus we need to remap it.
+ color1 = ((_colorMap[1] >> 5) & 0x7) + 16;
+ color2 = ((_colorMap[0] >> 5) & 0x7) + 16;
+ } else {
+ color1 = _colorMap[1];
+ color2 = _colorMap[0];
+ }
+
+ _font->drawChar(dst, c, 640, 1, color1, color2, 640, 400);
+}
+
+#pragma mark -
+
+Palette::Palette(const int numColors) : _palData(0), _numColors(numColors) {
+ _palData = new uint8[numColors * 3];
+ assert(_palData);
+
+ memset(_palData, 0, numColors * 3);
+}
+
+Palette::~Palette() {
+ delete[] _palData;
+ _palData = 0;
+}
+
+void Palette::loadVGAPalette(Common::ReadStream &stream, int startIndex, int colors) {
+ assert(startIndex + colors <= _numColors);
+
+ uint8 *pos = _palData + startIndex * 3;
+ for (int i = 0 ; i < colors * 3; i++)
+ *pos++ = stream.readByte() & 0x3F;
+}
+
+void Palette::loadHiColorPalette(Common::ReadStream &stream, int startIndex, int colors) {
+ uint16 *pos = (uint16*)(_palData + startIndex * 2);
+
+ Graphics::PixelFormat currentFormat = g_system->getScreenFormat();
+ Graphics::PixelFormat originalFormat(2, 5, 5, 5, 0, 5, 10, 0, 0);
+
+ for (int i = 0; i < colors; i++) {
+ uint8 r, g, b;
+ originalFormat.colorToRGB(stream.readUint16LE(), r, g, b);
+ *pos++ = currentFormat.RGBToColor(r, g, b);
+ }
+}
+
+void Palette::loadEGAPalette(Common::ReadStream &stream, int startIndex, int colors) {
+ assert(startIndex + colors <= 16);
+
+ uint8 *dst = _palData + startIndex * 3;
+ for (int i = 0; i < colors; i++) {
+ uint8 index = stream.readByte();
+ assert(index < _egaNumColors);
+ memcpy(dst, &_egaColors[index * 3], 3);
+ dst += 3;
+ }
+}
+
+void Palette::setCGAPalette(int palIndex, CGAIntensity intensity) {
+ assert(_numColors >= _cgaNumColors);
+ assert(!(palIndex & ~1));
+ memcpy(_palData, _cgaColors[palIndex * 2 + intensity], _numColors * 3);
+}
+
+void Palette::loadAmigaPalette(Common::ReadStream &stream, int startIndex, int colors) {
+ assert(startIndex + colors <= _numColors);
+
+ for (int i = 0; i < colors; ++i) {
+ uint16 col = stream.readUint16BE();
+ _palData[(i + startIndex) * 3 + 2] = ((col & 0xF) * 0x3F) / 0xF; col >>= 4;
+ _palData[(i + startIndex) * 3 + 1] = ((col & 0xF) * 0x3F) / 0xF; col >>= 4;
+ _palData[(i + startIndex) * 3 + 0] = ((col & 0xF) * 0x3F) / 0xF; col >>= 4;
+ }
+}
+
+void Palette::loadPC98Palette(Common::ReadStream &stream, int startIndex, int colors) {
+ assert(startIndex + colors <= _numColors);
+
+ for (int i = 0; i < colors; ++i) {
+ const byte g = stream.readByte(), r = stream.readByte(), b = stream.readByte();
+
+ _palData[(i + startIndex) * 3 + 0] = ((r & 0xF) * 0x3F) / 0xF;
+ _palData[(i + startIndex) * 3 + 1] = ((g & 0xF) * 0x3F) / 0xF;
+ _palData[(i + startIndex) * 3 + 2] = ((b & 0xF) * 0x3F) / 0xF;
+ }
+}
+
+void Palette::clear() {
+ memset(_palData, 0, _numColors * 3);
+}
+
+void Palette::fill(int firstCol, int numCols, uint8 value) {
+ assert(firstCol >= 0 && firstCol + numCols <= _numColors);
+
+ memset(_palData + firstCol * 3, CLIP<int>(value, 0, 63), numCols * 3);
+}
+
+void Palette::copy(const Palette &source, int firstCol, int numCols, int dstStart) {
+ if (numCols == -1)
+ numCols = MIN(source.getNumColors(), _numColors) - firstCol;
+ if (dstStart == -1)
+ dstStart = firstCol;
+
+ assert(numCols >= 0 && numCols <= _numColors);
+ assert(firstCol >= 0 && firstCol <= source.getNumColors());
+ assert(dstStart >= 0 && dstStart + numCols <= _numColors);
+
+ memmove(_palData + dstStart * 3, source._palData + firstCol * 3, numCols * 3);
+}
+
+void Palette::copy(const uint8 *source, int firstCol, int numCols, int dstStart) {
+ if (dstStart == -1)
+ dstStart = firstCol;
+
+ assert(numCols >= 0 && numCols <= _numColors);
+ assert(firstCol >= 0);
+ assert(dstStart >= 0 && dstStart + numCols <= _numColors);
+
+ memmove(_palData + dstStart * 3, source + firstCol * 3, numCols * 3);
+}
+
+uint8 *Palette::fetchRealPalette() const {
+ uint8 *buffer = new uint8[_numColors * 3];
+ assert(buffer);
+
+ uint8 *dst = buffer;
+ const uint8 *palData = _palData;
+
+ for (int i = 0; i < _numColors; ++i) {
+ dst[0] = (palData[0] << 2) | (palData[0] & 3);
+ dst[1] = (palData[1] << 2) | (palData[1] & 3);
+ dst[2] = (palData[2] << 2) | (palData[2] & 3);
+
+ dst += 3;
+ palData += 3;
+ }
+
+ return buffer;
+}
+
+const uint8 Palette::_egaColors[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
+ 0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
+ 0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
+};
+
+const int Palette::_egaNumColors = ARRAYSIZE(_egaColors) / 3;
+
+const uint8 Palette::_cgaColors[4][12] = {
+ { 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x2A, 0x15, 0x00 },
+ { 0x00, 0x00, 0x00, 0x15, 0x3F, 0x15, 0x3F, 0x15, 0x15, 0x3F, 0x3F, 0x15 },
+ { 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x00, 0x2A, 0x2A, 0x2A, 0x2A },
+ { 0x00, 0x00, 0x00, 0x15, 0x3F, 0x3F, 0x3F, 0x15, 0x3F, 0x3F, 0x3F, 0x3F }
+};
+
+const int Palette::_cgaNumColors = ARRAYSIZE(_cgaColors[0]) / 3;
+
+} // End of namespace Kyra