/* 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.
 *
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-12-0/engines/kyra/screen.cpp $
 * $Id: screen.cpp 32807 2008-06-26 19:42:59Z athrxx $
 *
 */


#include "common/endian.h"
#include "common/system.h"
#include "graphics/cursorman.h"
#include "kyra/screen.h"
#include "kyra/kyra_v1.h"
#include "kyra/resource.h"

namespace Kyra {

Screen::Screen(KyraEngine_v1 *vm, OSystem *system)
	: _system(system), _vm(vm), _sjisInvisibleColor(0) {
	_debugEnabled = false;
	_maskMinY = _maskMaxY = -1;
}

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].fontData;
		_fonts[f].fontData = NULL;
	}

	delete[] _sjisFontData;
	delete[] _sjisTempPage;
	delete[] _currentPalette;
	delete[] _screenPalette;
	delete[] _decodeShapeBuffer;
	delete[] _animBlockPtr;

	if (_vm->gameFlags().platform != Common::kPlatformAmiga) {
		for (int i = 0; i < ARRAYSIZE(_palettes); ++i)
			delete[] _palettes[i];
	}

	delete[] _dirtyRects;
}

bool Screen::init() {
	debugC(9, kDebugLevelScreen, "Screen::init()");
	_disableScreen = false;
	_debugEnabled = false;

	_system->setFeatureState(OSystem::kFeatureAutoComputeDirtyRects, false);

	memset(_sjisOverlayPtrs, 0, sizeof(_sjisOverlayPtrs));
	_useOverlays = false;
	_useSJIS = false;
	_sjisTempPage = _sjisFontData = 0;

	if (_vm->gameFlags().useHiResOverlay) {
		_useOverlays = true;
		_useSJIS = (_vm->gameFlags().lang == Common::JA_JPN);
		_sjisInvisibleColor = (_vm->gameFlags().gameID == GI_KYRA1) ? 0x80 : 0xF6;		
		
		for (int i = 0; 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) {
			if (!_sjisFontData) {
				// we use the FM-Towns font rom for PC-98, too, until we feel
				// like adding support for the PC-98 font
				//if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
					// FM-Towns
					_sjisFontData = _vm->resource()->fileData("FMT_FNT.ROM", 0);
					if (!_sjisFontData)
						error("missing font rom ('FMT_FNT.ROM') required for this version");
				/*} else {
					// PC-98
					_sjisFontData = _vm->resource()->fileData("FONT.ROM", 0);
					if (!_sjisFontData)
						error("missing font rom ('FONT.ROM') required for this version");
				}*/
			}

			if (!_sjisTempPage) {
				_sjisTempPage = new uint8[420];
				assert(_sjisTempPage);
				_sjisTempPage2 = _sjisTempPage + 60;
				_sjisSourceChar = _sjisTempPage + 384;
			}
		}
	}

	_curPage = 0;
	uint8 *pagePtr = new uint8[SCREEN_PAGE_SIZE * 8];
	for (int pageNum = 0; pageNum < SCREEN_PAGE_NUM; pageNum += 2)
		_pagePtrs[pageNum] = _pagePtrs[pageNum + 1] = pagePtr + (pageNum >> 1) * SCREEN_PAGE_SIZE;
	memset(pagePtr, 0, SCREEN_PAGE_SIZE * 8);

	memset(_shapePages, 0, sizeof(_shapePages));

	memset(_palettes, 0, sizeof(_palettes));
	_screenPalette = new uint8[768];
	assert(_screenPalette);
	memset(_screenPalette, 0, 768);

	if (_vm->gameFlags().platform == Common::kPlatformAmiga) {
		_currentPalette = new uint8[1248];
		assert(_currentPalette);
		memset(_currentPalette, 0, 1248);

		for (int i = 0; i < 6; ++i)
			_palettes[i] = _currentPalette + (i+1)*96;
	} else {
		_currentPalette = new uint8[768];
		assert(_currentPalette);
		memset(_currentPalette, 0, 768);
		for (int i = 0; i < 3; ++i) {
			_palettes[i] = new uint8[768];
			assert(_palettes[i]);
			memset(_palettes[i], 0, 768);
		}
	}

	setScreenPalette(_currentPalette);
	_curDim = 0;
	_charWidth = 0;
	_charOffset = 0;
	memset(_fonts, 0, sizeof(_fonts));
	for (int i = 0; i < ARRAYSIZE(_textColorsMap); ++i)
		_textColorsMap[i] = i;
	_decodeShapeBuffer = NULL;
	_decodeShapeBufferSize = 0;
	_animBlockPtr = NULL;
	_animBlockSize = 0;
	_mouseLockCount = 1;
	CursorMan.showMouse(false);

	_forceFullUpdate = false;
	_numDirtyRects = 0;
	_dirtyRects = new Rect[kMaxDirtyRects];
	assert(_dirtyRects);

	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[4*256];
	_system->grabPalette(palette, 0, 256);

	if (_vm->gameFlags().useHiResOverlay) {
		_system->beginGFXTransaction();
			_vm->initCommonGFX(true);
			if (_debugEnabled)
				_system->initSize(960, 400);
			else
				_system->initSize(640, 400);
		_system->endGFXTransaction();
	} else {
		_system->beginGFXTransaction();
			_vm->initCommonGFX(false);
			if (_debugEnabled)
				_system->initSize(640, 200);
			else
				_system->initSize(320, 200);
		_system->endGFXTransaction();
	}

	_system->setPalette(palette, 0, 256);
}

void Screen::updateScreen() {
	debugC(9, kDebugLevelScreen, "Screen::updateScreen()");
	if (_disableScreen)
		return;

	if (_useOverlays)
		updateDirtyRectsOvl();
	else
		updateDirtyRects();

	if (_debugEnabled) {
		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);
	}

	_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);
		for (int i = 0; i < _numDirtyRects; ++i) {
			Rect &cur = _dirtyRects[i];
			_system->copyRectToScreen(page0 + cur.y * SCREEN_W + cur.x, SCREEN_W, cur.x, cur.y, cur.x2, cur.y2);
		}
	}
	_forceFullUpdate = false;
	_numDirtyRects = 0;
}

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, 640, 0, 0, 640, 400);
	} else {
		const byte *page0 = getCPagePtr(0);
		byte *ovl0 = _sjisOverlayPtrs[0];

		for (int i = 0; i < _numDirtyRects; ++i) {
			Rect &cur = _dirtyRects[i];
			byte *dst = ovl0 + cur.y * 1280 + (cur.x<<1);
			const byte *src = page0 + cur.y * SCREEN_W + cur.x;

			scale2x(dst, 640, src, SCREEN_W, cur.x2, cur.y2);
			mergeOverlay(cur.x<<1, cur.y<<1, cur.x2<<1, cur.y2<<1);
			_system->copyRectToScreen(dst, 640, cur.x<<1, cur.y<<1, cur.x2<<1, cur.y2<<1);
		}
	}
	_forceFullUpdate = false;
	_numDirtyRects = 0;
}

void Screen::scale2x(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h) {
	byte *dstL1 = dst;
	byte *dstL2 = dst + dstPitch;

	int dstAdd = dstPitch * 2 - w * 2;
	int srcAdd = srcPitch - w;

	while (h--) {
		for (int x = 0; x < w; ++x, dstL1 += 2, dstL2 += 2) {
			uint16 col = *src++;
			col |= col << 8;
			*(uint16*)(dstL1) = col;
			*(uint16*)(dstL2) = col;
		}
		dstL1 += dstAdd; dstL2 += dstAdd;
		src += srcAdd;
	}
}

void Screen::mergeOverlay(int x, int y, int w, int h) {
	byte *dst = _sjisOverlayPtrs[0] + y * 640 + x;
	const byte *src = _sjisOverlayPtrs[1] + y * 640 + x;

	int add = 640 - w;

	while (h--) {
		for (x = 0; x < w; ++x, ++dst) {
			byte col = *src++;
			if (col != _sjisInvisibleColor)
				*dst = col;
		}
		dst += add;
		src += add;
	}
}

uint8 *Screen::getPagePtr(int pageNum) {
	debugC(9, kDebugLevelScreen, "Screen::getPagePtr(%d)", pageNum);
	assert(pageNum < SCREEN_PAGE_NUM);
	return _pagePtrs[pageNum];
}

const uint8 *Screen::getCPagePtr(int pageNum) const {
	debugC(9, kDebugLevelScreen, "Screen::getCPagePtr(%d)", pageNum);
	assert(pageNum < SCREEN_PAGE_NUM);
	return _pagePtrs[pageNum];
}

uint8 *Screen::getPageRect(int pageNum, int x, int y, int w, int h) {
	debugC(9, kDebugLevelScreen, "Screen::getPageRect(%d, %d, %d, %d, %d)", pageNum, x, y, w, 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) {
	debugC(9, kDebugLevelScreen, "Screen::clearPage(%d)", pageNum);
	assert(pageNum < SCREEN_PAGE_NUM);
	if (pageNum == 0 || pageNum == 1)
		_forceFullUpdate = true;
	memset(getPagePtr(pageNum), 0, SCREEN_PAGE_SIZE);
	clearOverlayPage(pageNum);
}

int Screen::setCurPage(int pageNum) {
	debugC(9, kDebugLevelScreen, "Screen::setCurPage(%d)", pageNum);
	assert(pageNum < SCREEN_PAGE_NUM);
	int previousPage = _curPage;
	_curPage = pageNum;
	return previousPage;
}

void Screen::clearCurPage() {
	debugC(9, kDebugLevelScreen, "Screen::clearCurPage()");
	if (_curPage == 0 || _curPage == 1)
		_forceFullUpdate = true;
	memset(getPagePtr(_curPage), 0, SCREEN_PAGE_SIZE);
	clearOverlayPage(_curPage);
}

uint8 Screen::getPagePixel(int pageNum, int x, int y) {
	debugC(9, kDebugLevelScreen, "Screen::getPagePixel(%d, %d, %d)", pageNum, x, y);
	assert(pageNum < SCREEN_PAGE_NUM);
	assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H);
	return _pagePtrs[pageNum][y * SCREEN_W + x];
}

void Screen::setPagePixel(int pageNum, int x, int y, uint8 color) {
	debugC(9, kDebugLevelScreen, "Screen::setPagePixel(%d, %d, %d, %d)", pageNum, x, y, color);
	assert(pageNum < SCREEN_PAGE_NUM);
	assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H);
	if (pageNum == 0 || pageNum == 1)
		addDirtyRect(x, y, 1, 1);
	_pagePtrs[pageNum][y * SCREEN_W + x] = color;
}

void Screen::fadeFromBlack(int delay, const UpdateFunctor *upFunc) {
	debugC(9, kDebugLevelScreen, "Screen::fadeFromBlack(%d, %p)", delay, (const void*)upFunc);
	fadePalette(_currentPalette, delay, upFunc);
}

void Screen::fadeToBlack(int delay, const UpdateFunctor *upFunc) {
	debugC(9, kDebugLevelScreen, "Screen::fadeToBlack(%d, %p)", delay, (const void*)upFunc);
	uint8 blackPal[768];
	memset(blackPal, 0, 768);
	fadePalette(blackPal, delay, upFunc);
}

void Screen::fadePalette(const uint8 *palData, int delay, const UpdateFunctor *upFunc) {
	debugC(9, kDebugLevelScreen, "Screen::fadePalette(%p, %d, %p)", (const void *)palData, delay, (const void*)upFunc);
	updateScreen();

	uint8 fadePal[768];
	memcpy(fadePal, _screenPalette, 768);
	uint8 diff, maxDiff = 0;
	for (int i = 0; i < 768; ++i) {
		diff = ABS(palData[i] - fadePal[i]);
		if (diff > maxDiff) {
			maxDiff = diff;
		}
	}

	int16 delayInc = delay << 8;
	if (maxDiff != 0)
		delayInc /= maxDiff;

	delay = delayInc;
	for (diff = 1; diff <= maxDiff; ++diff) {
		if (delayInc >= 512)
			break;
		delayInc += delay;
	}

	int delayAcc = 0;
	while (!_vm->quit()) {
		delayAcc += delayInc;
		bool needRefresh = false;
		for (int i = 0; i < 768; ++i) {
			int c1 = palData[i];
			int c2 = fadePal[i];
			if (c1 != c2) {
				needRefresh = true;
				if (c1 > c2) {
					c2 += diff;
					if (c1 < c2)
						c2 = c1;
				}

				if (c1 < c2) {
					c2 -= diff;
					if (c1 > c2)
						c2 = c1;
				}

				fadePal[i] = (uint8)c2;
			}
		}

		if (!needRefresh)
			break;

		setScreenPalette(fadePal);
		if (upFunc && upFunc->isValid())
			(*upFunc)();
		else
			_system->updateScreen();
		//_system->delayMillis((delayAcc >> 8) * 1000 / 60);
		_vm->delay((delayAcc >> 8) * 1000 / 60);
		delayAcc &= 0xFF;
	}

	if (_vm->quit()) {
		setScreenPalette(palData);
		if (upFunc && upFunc->isValid())
			(*upFunc)();
		else
			_system->updateScreen();
	}
}

void Screen::setPaletteIndex(uint8 index, uint8 red, uint8 green, uint8 blue) {
	debugC(9, kDebugLevelScreen, "Screen::setPaletteIndex(%u, %u, %u, %u)", index, red, green, blue);
	_currentPalette[index * 3 + 0] = red;
	_currentPalette[index * 3 + 1] = green;
	_currentPalette[index * 3 + 2] = blue;
	setScreenPalette(_currentPalette);
}

void Screen::setScreenPalette(const uint8 *palData) {
	debugC(9, kDebugLevelScreen, "Screen::setScreenPalette(%p)", (const void *)palData);

	int colors = (_vm->gameFlags().platform == Common::kPlatformAmiga ? 32 : 256);
	if (palData != _screenPalette)
		memcpy(_screenPalette, palData, colors*3);

	uint8 screenPal[256 * 4];
	for (int i = 0; i < colors; ++i) {
		screenPal[4 * i + 0] = (palData[0] << 2) | (palData[0] & 3);
		screenPal[4 * i + 1] = (palData[1] << 2) | (palData[1] & 3);
		screenPal[4 * i + 2] = (palData[2] << 2) | (palData[2] & 3);
		screenPal[4 * i + 3] = 0;
		palData += 3;
	}
	_system->setPalette(screenPal, 0, colors);
}

void Screen::copyToPage0(int y, int h, uint8 page, uint8 *seqBuf) {
	debugC(9, kDebugLevelScreen, "Screen::copyToPage0(%d, %d, %d, %p)", y, h, page, (const void *)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) {
	debugC(9, kDebugLevelScreen, "Screen::copyRegion(%d, %d, %d, %d, %d, %d, %d, %d, %d)", x1, y1, x2, y2, w, h, srcPage, dstPage, 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 + x1;
	uint8 *dst = getPagePtr(dstPage) + y2 * SCREEN_W + x2;

	if (dstPage == 0 || dstPage == 1)
		addDirtyRect(x2, y2, w, h);

	copyOverlayRegion(x1, y1, x2, y2, w, h, srcPage, dstPage);

	if (flags & CR_X_FLIPPED) {
		while (h--) {
			for (int i = 0; i < w; ++i) {
				if (src[i] || (flags & CR_NO_P_CHECK))
					dst[w-i] = src[i];
			}
			src += SCREEN_W;
			dst += SCREEN_W;
		}
	} else {
		while (h--) {
			for (int i = 0; i < w; ++i) {
				if (src[i] || (flags & CR_NO_P_CHECK))
					dst[i] = src[i];
			}
			src += SCREEN_W;
			dst += SCREEN_W;
		}
	}
}

void Screen::copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 *dest) {
	debugC(9, kDebugLevelScreen, "Screen::copyRegionToBuffer(%d, %d, %d, %d, %d)", pageNum, x, y, w, h);
	if (y < 0) {
		dest += (-y) * w;
		h += y;
		y = 0;
	} else if (y + h > SCREEN_H) {
		h = SCREEN_H - y;
	}

	if (x < 0) {
		dest += -x;
		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, pagePtr + i * SCREEN_W + x, w);
}

void Screen::copyPage(uint8 srcPage, uint8 dstPage) {
	debugC(9, kDebugLevelScreen, "Screen::copyPage(%d, %d)", srcPage, dstPage);

	uint8 *src = getPagePtr(srcPage);
	uint8 *dst = getPagePtr(dstPage);
	memcpy(dst, src, SCREEN_W * SCREEN_H);
	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) {
	debugC(9, kDebugLevelScreen, "Screen::copyBlockToPage(%d, %d, %d, %d, %d, %p)", pageNum, x, y, w, h, (const void *)src);
	if (y < 0) {
		src += (-y) * w;
		h += y;
		y = 0;
	} else if (y + h > SCREEN_H) {
		h = SCREEN_H - y;
	}

	if (x < 0) {
		src += -x;
		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 + x;

	if (pageNum == 0 || pageNum == 1)
		addDirtyRect(x, y, w, h);

	clearOverlayRect(pageNum, x, y, w, h);

	while (h--) {
		memcpy(dst, src, w);
		dst += SCREEN_W;
		src += w;
	}
}

void Screen::copyFromCurPageBlock(int x, int y, int w, int h, const uint8 *src) {
	debugC(9, kDebugLevelScreen, "Screen::copyFromCurPageBlock(%d, %d, %d, %d, %p)", x, y, w, h, (const void *)src);
	if (x < 0)
		x = 0;
	else if (x >= 40)
		return;

	if (x + w > 40)
		w = 40 - x;

	if (y < 0)
		y = 0;
	else if (y >= 200)
		return;

	if (y + h > 200)
		h = 200 - y;

	uint8 *dst = getPagePtr(_curPage) + y * SCREEN_W + x * 8;

	if (_curPage == 0 || _curPage == 1)
		addDirtyRect(x*8, y, w*8, h);

	clearOverlayRect(_curPage, x*8, y, w*8, h);

	while (h--) {
		memcpy(dst, src, w*8);
		dst += SCREEN_W;
		src += w*8;
	}
}

void Screen::copyCurPageBlock(int x, int y, int w, int h, uint8 *dst) {
	debugC(9, kDebugLevelScreen, "Screen::copyCurPageBlock(%d, %d, %d, %d, %p)", x, y, w, h, (const void *)dst);
	assert(dst);
	if (x < 0)
		x = 0;
	else if (x >= 40)
		return;

	if (x + w > 40)
		w = 40 - x;

	if (y < 0)
		y = 0;
	else if (y >= 200)
		return;

	if (y + h > 200)
		h = 200 - y;

	const uint8 *src = getPagePtr(_curPage) + y * SCREEN_W + x * 8;
	while (h--) {
		memcpy(dst, src, w*8);
		dst += w*8;
		src += SCREEN_W;
	}
}

void Screen::shuffleScreen(int sx, int sy, int w, int h, int srcPage, int dstPage, int ticks, bool transparent) {
	debugC(9, kDebugLevelScreen, "Screen::shuffleScreen(%d, %d, %d, %d, %d, %d, %d, %d)", sx, sy, w, h, srcPage, dstPage, ticks, transparent);
	assert(sx >= 0 && w <= SCREEN_W);
	int x;
	uint16 x_offs[SCREEN_W];
	for (x = 0; x < SCREEN_W; ++x)
		x_offs[x] = x;

	for (x = 0; x < w; ++x) {
		int i = _vm->_rnd.getRandomNumber(w - 1);
		SWAP(x_offs[x], x_offs[i]);
	}

	assert(sy >= 0 && h <= SCREEN_H);
	int y;
	uint8 y_offs[SCREEN_H];
	for (y = 0; y < SCREEN_H; ++y)
		y_offs[y] = y;

	for (y = 0; y < h; ++y) {
		int i = _vm->_rnd.getRandomNumber(h - 1);
		SWAP(y_offs[y], y_offs[i]);
	}

	int32 start, now;
	int wait;
	for (y = 0; y < h && !_vm->quit(); ++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->quit()) {
		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) {
	debugC(9, kDebugLevelScreen, "Screen::fillRect(%d, %d, %d, %d, %d, %d, %d)", x1, y1, x2, y2, color, pageNum, xored);
	assert(x2 < SCREEN_W && y2 < SCREEN_H);
	if (pageNum == -1)
		pageNum = _curPage;

	uint8 *dst = getPagePtr(pageNum) + y1 * SCREEN_W + x1;

	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 (xored) {
		for (; y1 <= y2; ++y1) {
			for (int x = x1; x <= x2; ++x)
				dst[x] ^= color;
			dst += SCREEN_W;
		}
	} else {
		for (; y1 <= y2; ++y1) {
			memset(dst, color, x2 - x1 + 1);
			dst += SCREEN_W;
		}
	}
}

void Screen::drawBox(int x1, int y1, int x2, int y2, int color) {
	debugC(9, kDebugLevelScreen, "Screen::drawBox(%i, %i, %i, %i, %i)", x1, y1, x2, y2, 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) {
	debugC(9, kDebugLevelScreen, "Screen::drawShadedBox(%i, %i, %i, %i, %i, %i)", x1, y1, x2, y2, color1, color2);
	assert(x1 >= 0 && y1 >= 0);
	hideMouse();

	fillRect(x1, y1, x2, y1 + 1, color1);
	fillRect(x2 - 1, y1, x2, y2, color1);

	drawClippedLine(x1, y1, x1, y2, color2);
	drawClippedLine(x1 + 1, y1 + 1, x1 + 1, y2 - 1, color2);
	drawClippedLine(x1, y2, x2, y2, color2);
	drawClippedLine(x1, y2 - 1, x2 - 1, y2 - 1, color2);

	showMouse();
}

void Screen::drawClippedLine(int x1, int y1, int x2, int y2, int color) {
	debugC(9, kDebugLevelScreen, "Screen::drawClippedLine(%i, %i, %i, %i, %i)", x1, y1, x2, y2, color);

	if (x1 < 0)
		x1 = 0;
	else if (x1 > 319)
		x1 = 319;

	if (x2 < 0)
		x2 = 0;
	else if (x2 > 319)
		x2 = 319;

	if (y1 < 0)
		y1 = 0;
	else if (y1 > 199)
		y1 = 199;

	if (y2 < 0)
		y2 = 0;
	else if (y2 > 199)
		y2 = 199;

	if (x1 == x2)
		if (y1 > y2)
			drawLine(true, x1, y2, y1 - y2 + 1, color);
		else
			drawLine(true, x1, y1, y2 - y1 + 1, color);
	else
		if (x1 > x2)
			drawLine(false, x2, y1, x1 - x2 + 1, color);
		else
			drawLine(false, x1, y1, x2 - x1 + 1, color);
}

void Screen::drawLine(bool vertical, int x, int y, int length, int color) {
	debugC(9, kDebugLevelScreen, "Screen::drawLine(%i, %i, %i, %i, %i)", vertical, x, y, length, color);

	uint8 *ptr = getPagePtr(_curPage) + y * SCREEN_W + x;

	if (vertical) {
		assert((y + length) <= SCREEN_H);
		int currLine = 0;
		while (currLine < length) {
			*ptr = color;
			ptr += SCREEN_W;
			currLine++;
		}
	} else {
		assert((x + length) <= SCREEN_W);
		memset(ptr, color, length);
	}

	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) {
	debugC(9, kDebugLevelScreen, "Screen::setAnimBlockPtr(%d)", size);
	delete[] _animBlockPtr;
	_animBlockPtr = new uint8[size];
	assert(_animBlockPtr);
	memset(_animBlockPtr, 0, size);
	_animBlockSize = size;
}

void Screen::setTextColor(const uint8 *cmap, int a, int b) {
	debugC(9, kDebugLevelScreen, "Screen::setTextColor(%p, %d, %d)", (const void *)cmap, a, b);
	memcpy(&_textColorsMap[a], cmap, b-a+1);
}

bool Screen::loadFont(FontId fontId, const char *filename) {
	debugC(9, kDebugLevelScreen, "Screen::loadFont(%d, '%s')", fontId, filename);
	Font *fnt = &_fonts[fontId];

	// FIXME: add font support for amiga version
	if (_vm->gameFlags().platform == Common::kPlatformAmiga)
		return true;

	if (!fnt)
		error("fontId %d is invalid", fontId);

	if (fnt->fontData)
		delete[] fnt->fontData;

	uint32 sz = 0;
	uint8 *fontData = fnt->fontData = _vm->resource()->fileData(filename, &sz);

	if (!fontData || !sz)
		error("Couldn't load font file '%s'", filename);

	uint16 fontSig = READ_LE_UINT16(fontData + 2);

	if (fontSig != 0x500)
		error("Invalid font data (file '%s', fontSig: %.04X)", filename, fontSig);

	fnt->charWidthTable = fontData + READ_LE_UINT16(fontData + 8);
	fnt->charSizeOffset = READ_LE_UINT16(fontData + 4);
	fnt->charBitmapOffset = READ_LE_UINT16(fontData + 6);
	fnt->charWidthTableOffset = READ_LE_UINT16(fontData + 8);
	fnt->charHeightTableOffset = READ_LE_UINT16(fontData + 0xC);

	return true;
}

Screen::FontId Screen::setFont(FontId fontId) {
	debugC(9, kDebugLevelScreen, "Screen::setFont(%d)", fontId);
	FontId prev = _currentFont;
	_currentFont = fontId;
	return prev;
}

int Screen::getFontHeight() const {
	// FIXME: add font support for amiga version
	if (_vm->gameFlags().platform == Common::kPlatformAmiga)
		return 0;
	return *(_fonts[_currentFont].fontData + _fonts[_currentFont].charSizeOffset + 4);
}

int Screen::getFontWidth() const {
	// FIXME: add font support for amiga version
	if (_vm->gameFlags().platform == Common::kPlatformAmiga)
		return 0;
	return *(_fonts[_currentFont].fontData + _fonts[_currentFont].charSizeOffset + 5);
}

int Screen::getCharWidth(uint16 c) const {
	// FIXME: add font support for amiga version
	if (_vm->gameFlags().platform == Common::kPlatformAmiga)
		return 0;
	debugC(9, kDebugLevelScreen, "Screen::getCharWidth('%c'|%d)", c & 0xFF, c);
	if (c & 0xFF00)
		return SJIS_CHARSIZE >> 1;
	return (int)_fonts[_currentFont].charWidthTable[c] + _charWidth;
}

int Screen::getTextWidth(const char *str) const {
	// FIXME: add font support for amiga version
	if (_vm->gameFlags().platform == Common::kPlatformAmiga)
		return 0;
	debugC(9, kDebugLevelScreen, "Screen::getTextWidth('%s')", str);

	int curLineLen = 0;
	int maxLineLen = 0;
	while (1) {
		uint c = *str++;
		c &= 0xFF;
		if (c == 0) {
			break;
		} else if (c == '\r') {
			if (curLineLen > maxLineLen)
				maxLineLen = curLineLen;
			else
				curLineLen = 0;
		} else {
			if (c <= 0x7F || !_useSJIS) {
				curLineLen += getCharWidth(c);
			} else {
				c = READ_LE_UINT16(str - 1);
				++str;
				curLineLen += getCharWidth(c);
			}
		}
	}

	return MAX(curLineLen, maxLineLen);
}

void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2) {
	// FIXME: add font support for amiga version
	if (_vm->gameFlags().platform == Common::kPlatformAmiga)
		return;
	debugC(9, kDebugLevelScreen, "Screen::printText('%s', %d, %d, 0x%X, 0x%X)", str, x, y, color1, color2);
	uint8 cmap[2];
	cmap[0] = color2;
	cmap[1] = color1;
	setTextColor(cmap, 0, 1);

	Font *fnt = &_fonts[_currentFont];
	const uint8 charHeightFnt = *(fnt->fontData + fnt->charSizeOffset + 4);
	uint8 charHeight = 0;

	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) {
		uint c = *str++;
		c &= 0xFF;
		if (c == 0) {
			break;
		} else if (c == '\r') {
			x = x_start;
			y += charHeight + _charOffset;
		} else {
			int charWidth = getCharWidth(c);
			if (x + charWidth > SCREEN_W) {
				x = x_start;
				y += charHeight + _charOffset;
				if (y >= SCREEN_H) {
					break;
				}
			}
			if (c <= 0x7F || !_useSJIS) {
				drawCharANSI(c, x, y);
				charHeight = charHeightFnt;
			} else {
				c = READ_LE_UINT16(str - 1);
				++str;
				charWidth = getCharWidth(c);
				charHeight = SJIS_CHARSIZE >> 1;
				drawCharSJIS(c, x, y);
			}
			x += charWidth;
		}
	}
}

void Screen::drawCharANSI(uint8 c, int x, int y) {
	debugC(9, kDebugLevelScreen, "Screen::drawChar('%c', %d, %d)", c, x, y);
	Font *fnt = &_fonts[_currentFont];
	uint8 *dst = getPagePtr(_curPage) + y * SCREEN_W + x;

	uint16 bitmapOffset = READ_LE_UINT16(fnt->fontData + fnt->charBitmapOffset + c * 2);
	if (bitmapOffset == 0)
		return;

	uint8 charWidth = *(fnt->fontData + fnt->charWidthTableOffset + c);
	if (charWidth + x > SCREEN_W)
		return;

	uint8 charH0 = *(fnt->fontData + fnt->charSizeOffset + 4);
	if (charH0 + y > SCREEN_H)
		return;

	uint8 charH1 = *(fnt->fontData + fnt->charHeightTableOffset + c * 2);
	uint8 charH2 = *(fnt->fontData + fnt->charHeightTableOffset + c * 2 + 1);
	charH0 -= charH1 + charH2;

	const uint8 *src = fnt->fontData + bitmapOffset;
	const int pitch = SCREEN_W - charWidth;

	while (charH1--) {
		uint8 col = _textColorsMap[0];
		for (int i = 0; i < charWidth; ++i) {
			if (col != 0)
				*dst = col;
			++dst;
		}
		dst += pitch;
	}

	while (charH2--) {
		uint8 b = 0;
		for (int i = 0; i < charWidth; ++i) {
			uint8 col;
			if (i & 1) {
				col = _textColorsMap[b >> 4];
			} else {
				b = *src++;
				col = _textColorsMap[b & 0xF];
			}
			if (col != 0) {
				*dst = col;
			}
			++dst;
		}
		dst += pitch;
	}

	while (charH0--) {
		uint8 col = _textColorsMap[0];
		for (int i = 0; i < charWidth; ++i) {
			if (col != 0)
				*dst = col;
			++dst;
		}
		dst += pitch;
	}

	if (_curPage == 0 || _curPage == 1)
		addDirtyRect(x, y, charWidth, *(fnt->fontData + fnt->charSizeOffset + 4));
}

void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...) {
	debugC(9, kDebugLevelScreen, "Screen::drawShape(%d, %p, %d, %d, %d, 0x%.04X, ...)", pageNum, (const void *)shapeData, x, y, sd, flags);
	if (!shapeData)
		return;

	int f = _vm->gameFlags().useAltShapeHeader ? 2 : 0;
	if (shapeData[f] & 1)
		flags |= 0x400;

	va_list args;
	va_start(args, flags);

	static int drawShapeVar2[] = {
		1, 3, 2, 5, 4, 3, 2, 1
	};

	_drawShapeVar1 = 0;
	_drawShapeVar3 = 1;
	_drawShapeVar4 = 0;
	_drawShapeVar5 = 0;

	_dsTable = 0;
	_dsTableLoopCount = 0;
	_dsTable2 = 0;
	_dsDrawLayer = 0;
	
	uint8 *table3 = 0;
	uint8 *table4 = 0;

	if (flags & 0x8000) {
		_dsTable2 = va_arg(args, uint8*);
	}

	if (flags & 0x100) {
		_dsTable = va_arg(args, uint8*);
		_dsTableLoopCount = va_arg(args, int);
		if (!_dsTableLoopCount)
			flags &= ~0x100;
	}

	if (flags & 0x1000) {
		table3 = va_arg(args, uint8*);
		table4 = va_arg(args, uint8*);
	}

	if (flags & 0x200) {
		_drawShapeVar1 += 1;
		_drawShapeVar1 &= 7;
		_drawShapeVar3 = drawShapeVar2[_drawShapeVar1];
		_drawShapeVar4 = 0;
		_drawShapeVar5 = 256;
	}

	if (flags & 0x4000)
		_drawShapeVar5 = va_arg(args, int);

	if (flags & 0x800)
		_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 & 0x2000) && _vm->gameFlags().gameID != GI_KYRA1)
		va_arg(args, int);

	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)
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0
	};

	int scaleCounterV = 0;
	
	f = flags & 0x0f;
	_dsProcessMargin = dsMarginFunc[f];
	_dsScaleSkip = dsSkipFunc[f];
	_dsProcessLine = dsLineFunc[f];

	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));
		va_end(args);
		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;

	if (_vm->gameFlags().useAltShapeHeader)
		src += 2;
	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) {
			va_end(args);
			return;
		}
	}

	if (flags & DSF_CENTER) {
		x -= (shpWidthScaled1 >> 1);
		y -= (shapeHeight >> 1);
	}

	src += 3;

	uint16 frameSize = READ_LE_UINT16(src); src += 2;

	int colorTableColors = ((_vm->gameFlags().gameID != GI_KYRA1) && (shapeFlags & 4)) ? *src++ : 16;
	
	if (!(flags & 0x8000) && (shapeFlags & 1))
		_dsTable2 = src;
	
	if (flags & 0x400)
		src += colorTableColors;

	if (!(shapeFlags & 2)) {
		decodeFrame4(src, _animBlockPtr, frameSize);
		src = _animBlockPtr;
	}

	int t = (flags & 2) ? y2 - y - shapeHeight : y - y1;

	const uint8 *s = src;

	if (t < 0) {
		shapeHeight += t;
		if (shapeHeight <= 0) {
			va_end(args);
			return;
		}

		t *= -1;
		uint8 *tmp = dst;

		do {
			_dsOffscreenScaleVal1 = 0;
			_dsTmpWidth = shapeWidth;
			int cnt = shapeWidth;
			(this->*_dsScaleSkip)(tmp, s, cnt);
			scaleCounterV += _dsScaleH;
			if (!(scaleCounterV & 0xff00))
				continue;
			uint8 r = scaleCounterV >> 8;
			scaleCounterV &= 0xff;
			t -= r;
		} while (t > 0);

		if (t < 0)
			scaleCounterV += (-t << 8);

		if (!(flags & 2))
			y = y1;
	}

	t = (flags & 2) ? y + shapeHeight - y1 : y2 - y;
	if (t <= 0) {
		va_end(args);
		return;
	}

	if (t < shapeHeight) {
		shapeHeight = t;
		if (flags & 2)
			y = y1;
	}

	_dsOffscreenLeft = 0;
	if (x < 0) {
		shpWidthScaled1 += x;
		_dsOffscreenLeft = -x;
		if (_dsOffscreenLeft >= shpWidthScaled2) {
			va_end(args);
			return;
		}
		x = 0;
	}

	_dsOffscreenRight = 0;
	t = x2 - x;

	if (t <= 0) {
		va_end(args);
		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 (pageNum == 0 || pageNum == 1)
		addDirtyRect(x, y, shpWidthScaled1, shapeHeight);
	clearOverlayRect(pageNum, x, y, shpWidthScaled1, shapeHeight);

	uint8 *d = dst;

	while (shapeHeight--) {
		bool normalPlot = true;
		if (flags & 0x800)
			normalPlot = (curY > _maskMinY && curY < _maskMaxY);

		while (!(scaleCounterV & 0xff00)) {
			scaleCounterV += _dsScaleH;
			if (!(scaleCounterV & 0xff00)) {
				_dsTmpWidth = shapeWidth;
				int cnt = shapeWidth;
				(this->*_dsScaleSkip)(d, s, cnt);
			}
		}

		const uint8 *b_src = s;

		do {
			s = b_src;
			_dsTmpWidth = shapeWidth;
			int cnt = _dsOffscreenLeft;
			int scaleState = (this->*_dsProcessMargin)(d, s, cnt);
			if (_dsTmpWidth) {
				cnt += shpWidthScaled1;
				if (cnt > 0) {
					_dsPlot = normalPlot ? dsPlot2 : dsPlot3;
					(this->*_dsProcessLine)(d, s, cnt, scaleState);
				}
				cnt += _dsOffscreenRight;
				if (cnt)
					(this->*_dsScaleSkip)(d, s, cnt);
			}
			dst += dsPitch;
			d = dst;
			++curY;
			scaleCounterV -= 256;
		} while (scaleCounterV & 0xff00);
	}

	va_end(args);
}

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;
	bool found = false;

	if (src == dst || !cnt)
		return _dsOffscreenScaleVal1;

	do {
		if (*src++)
			continue;
		found = true;
		cnt = cnt + 1 - (*src++);
	} while (--cnt > 0);

	if (!found || !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;
	bool found = false;

	if (src == dst || !cnt)
		return _dsOffscreenScaleVal1;

	do {
		if (*src++)
			continue;
		found = true;
		cnt = cnt + 1 - (*src++);
	} while (--cnt > 0);

	if (!found || !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 {
		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 {
		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, int) {
	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, int) {
	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, int scaleState) {
	int c = 0;

	do {
		if ((scaleState >> 8) <= 0) {
			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 {
			uint8 *d = dst++;
			(this->*_dsPlot)(d, c);
			scaleState -= 256;
			cnt--;
		}
	} while (cnt > 0);

	cnt = -1;
}

void Screen::drawShapeProcessLineScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt, int scaleState) {
	int c = 0;

	do {
		if ((scaleState >> 8) <= 0) {
			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 {
			uint8 *d = dst--;
			(this->*_dsPlot)(d, c);
			scaleState -= 256;
			cnt--;
		}
	} while (cnt > 0);

	cnt = -1;
}

void Screen::drawShapePlotType0(uint8 *dst, uint8 cmd) {
	*dst = cmd;
}

void Screen::drawShapePlotType1(uint8 *dst, uint8 cmd) {
	// uint32 relOffs = dst - _dsDstPage;
	for (int i = 0; i < _dsTableLoopCount; ++i)
		cmd = _dsTable[cmd];

	if (cmd)
		*dst = cmd;
}

void Screen::drawShapePlotType3_7(uint8 *dst, uint8 cmd) {
	cmd = *dst;
	for (int i = 0; i < _dsTableLoopCount; ++i)
		cmd = _dsTable[cmd];

	if (cmd)
		*dst = cmd;
}

void Screen::drawShapePlotType4(uint8 *dst, uint8 cmd) {
	*dst = _dsTable2[cmd];
}

void Screen::drawShapePlotType5(uint8 *dst, uint8 cmd) {
	// uint32 relOffs = dst - _dsDstPage;
	cmd = _dsTable2[cmd];
	for (int i = 0; i < _dsTableLoopCount; ++i)
		cmd = _dsTable[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 = _dsTable2[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 < _dsTableLoopCount; ++i)
			cmd = _dsTable[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 < _dsTableLoopCount; ++i)
			cmd = _dsTable[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 = _dsTable2[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 = _dsTable2[cmd];
		for (int i = 0; i < _dsTableLoopCount; ++i)
			cmd = _dsTable[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 = _dsTable2[cmd];
		}
	}

	_drawShapeVar4 = t;
	*dst = cmd;
}

void Screen::decodeFrame3(const uint8 *src, uint8 *dst, uint32 size) {
	debugC(9, kDebugLevelScreen, "Screen::decodeFrame3(%p, %p, %u)", (const void *)src, (const void *)dst, size);
	const uint8 *dstEnd = dst + size;
	while (dst < dstEnd) {
		int8 code = *src++;
		if (code == 0) {
			uint16 sz = READ_BE_UINT16(src);
			src += 2;
			memset(dst, *src++, sz);
			dst += sz;
		} else if (code < 0) {
			memset(dst, *src++, -code);
			dst -= code;
		} else {
			memcpy(dst, src, code);
			dst += code;
			src += code;
		}
	}
}

uint Screen::decodeFrame4(const uint8 *src, uint8 *dst, uint32 dstSize) {
	debugC(9, kDebugLevelScreen, "Screen::decodeFrame4(%p, %p, %u)", (const void *)src, (const void *)dst, dstSize);
	uint8 *dstOrig = dst;
	uint8 *dstEnd = dst + dstSize;
	while (1) {
		int count = dstEnd - dst;
		if (count == 0)
			break;

		uint8 code = *src++;
		if (!(code & 0x80)) { // 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) {
	debugC(9, kDebugLevelScreen, "Screen::decodeFrameDelta(%p, %p, %d)", (const void *)dst, (const void *)src, 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) {
	debugC(9, kDebugLevelScreen, "Screen::decodeFrameDeltaPage(%p, %p, %d, %d)", (const void *)dst, (const void *)src, pitch, 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, bool offscreen) {
	static uint8 tmp[320*200];

	if (offscreen) {
		uint8 *curLine = tmp;
		const uint8 *src = data;
		int hC = h;
		while (hC--) {
			uint8 *dst1 = curLine;
			uint8 *dst2 = dst1 + 8000;
			uint8 *dst3 = dst2 + 8000;
			uint8 *dst4 = dst3 + 8000;
			uint8 *dst5 = dst4 + 8000;

			int width = w >> 3;
			while (width--) {
				*dst1++ = *src++;
				*dst2++ = *src++;
				*dst3++ = *src++;
				*dst4++ = *src++;
				*dst5++ = *src++;
			}

			curLine += 40;
		}
	} else {
		memcpy(tmp, data, w*h);
	}

	for (int y = 0; y < h; ++y) {
		for (int x = 0; x < w; ++x) {
			int bytePos = x/8+y*40;
			int bitPos = 7-(x&7);

			byte colorIndex = 0;
			colorIndex |= (((tmp[bytePos + 8000 * 0] & (1 << bitPos)) >> bitPos) & 0x1) << 0;
			colorIndex |= (((tmp[bytePos + 8000 * 1] & (1 << bitPos)) >> bitPos) & 0x1) << 1;
			colorIndex |= (((tmp[bytePos + 8000 * 2] & (1 << bitPos)) >> bitPos) & 0x1) << 2;
			colorIndex |= (((tmp[bytePos + 8000 * 3] & (1 << bitPos)) >> bitPos) & 0x1) << 3;
			colorIndex |= (((tmp[bytePos + 8000 * 4] & (1 << bitPos)) >> bitPos) & 0x1) << 4;
			*data++ = colorIndex;
		}
	}
}

void Screen::convertAmigaMsc(uint8 *data) {
	byte *plane1 = data + 5760 * 1;
	byte *plane2 = data + 5760 * 2;
	byte *plane3 = data + 5760 * 3;
	byte *plane4 = data + 5760 * 4;
	byte *plane5 = data + 5760 * 5;
	byte *plane6 = data + 5760 * 6;
	for (int i = 0; i < 5760; ++i) {
		byte d = plane6[i];
		d = (plane5[i] |= d);
		d = (plane4[i] |= d);
		d = (plane3[i] |= d);
		d = (plane2[i] |= d);
		d = (plane1[i] |= d);
	}
	byte dst[320*144];
	memset(dst, 0, sizeof(dst));
	static const byte flagTable[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
	for (int y = 0; y < 144; ++y) {
		for (int x = 0; x < 320; ++x) {
			if (!(flagTable[x&7] & data[y*40+(x>>3)]))
				dst[y*320+x] |= 0x80;

			int layer = 0;
			for (int i = 0; i < 7; ++i) {
				if (flagTable[x&7] & data[y*40+(x>>3)+i*5760])
					layer = i;
			}

			if (layer)
				dst[y*320+x] |= (layer+1);
		}
	}
	memcpy(data, dst, 320*144);
}

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) {
	debugC(9, kDebugLevelScreen, "Screen::encodeShape(%d, %d, %d, %d, %d)", x, y, w, h, flags);
	uint8 *srcPtr = &_pagePtrs[_curPage][y * SCREEN_W + x];
	int16 shapeSize = 0;
	uint8 *tmp = srcPtr;
	int xpos = w;

	for (int i = h; i > 0; --i) {
		uint8 *start = tmp;
		shapeSize += w;
		xpos = w;
		while (xpos) {
			uint8 value = *tmp++;
			--xpos;

			if (!value) {
				shapeSize += 2;
				int16 curX = xpos;
				bool skip = false;

				while (xpos) {
					value = *tmp++;
					--xpos;

					if (value) {
						skip = true;
						break;
					}
				}

				if (!skip)
					++curX;

				curX -= xpos;
				shapeSize -= curX;

				while (curX > 0xFF) {
					curX -= 0xFF;
					shapeSize += 2;
				}
			}
		}

		tmp = start + SCREEN_W;
	}

	int16 shapeSize2 = shapeSize;
	if (_vm->gameFlags().useAltShapeHeader)
		shapeSize += 12;
	else
		shapeSize += 10;

	if (flags & 1)
		shapeSize += 16;

	static 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(uint8)*274);
		tableIndex = 1;
	}

	for (int ypos = h; ypos > 0; --ypos) {
		uint8 *srcBackUp = src;
		xpos = w;
		while (xpos) {
			uint8 value = *src++;
			if (value) {
				if (flags & 1) {
					if (!table[value]) {
						if (tableIndex == 16) {
							value = 1;
						} else {
							table[0x100+tableIndex] = value;
							table[value] = tableIndex;
							++tableIndex;
							value = 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) {
	debugC(9, kDebugLevelScreen, "Screen::encodeShapeAndCalculateSize(%p, %p, %d)", (const void *)from, (const void *)to, size_to);
	byte *fromPtrEnd = from + size_to;
	bool skipPixel = true;
	byte *tempPtr = 0;
	byte *toPtr = to;
	byte *fromPtr = from;
	byte *toPtr2 = to;

	*to++ = 0x81;
	*to++ = *from++;

	while (from < fromPtrEnd) {
		byte *curToPtr = to;
		to = fromPtr;
		int size = 1;

		while (true) {
			byte curPixel = *from;
			if (curPixel == *(from+0x40)) {
				byte *toBackUp = to;
				to = from;

				for (int i = 0; i < (fromPtrEnd - from); ++i) {
					if (*to++ != curPixel)
						break;
				}
				--to;
				uint16 diffSize = (to - from);
				if (diffSize >= 0x41) {
					skipPixel = false;
					from = to;
					to = curToPtr;
					*to++ = 0xFE;
					WRITE_LE_UINT16(to, diffSize); to += 2;
					*to++ = 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);
}

void Screen::hideMouse() {
	debugC(9, kDebugLevelScreen, "Screen::hideMouse()");
	++_mouseLockCount;
	CursorMan.showMouse(false);
}

void Screen::showMouse() {
	debugC(9, kDebugLevelScreen, "Screen::showMouse()");

	if (_mouseLockCount == 1)
		CursorMan.showMouse(true);

	if (_mouseLockCount > 0)
		_mouseLockCount--;
}


bool Screen::isMouseVisible() const {
	return _mouseLockCount == 0;
}

void Screen::setShapePages(int page1, int page2, int minY, int maxY) {
	debugC(9, kDebugLevelScreen, "Screen::setShapePages(%d, %d, %d, %d)", page1, page2, minY, maxY);
	_shapePages[0] = _pagePtrs[page1];
	_shapePages[1] = _pagePtrs[page2];
	_maskMinY = minY;
	_maskMaxY = maxY;
}

void Screen::setMouseCursor(int x, int y, byte *shape) {
	debugC(9, kDebugLevelScreen, "Screen::setMouseCursor(%d, %d, %p)", x, y, (const void *)shape);
	if (!shape)
		return;
	// if mouseDisabled
	//	return _mouseShape

	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().useHiResOverlay) {
		x <<= 1;
		y <<= 1;
		mouseWidth <<= 1;
		mouseHeight <<= 1;
		fillRect(mouseWidth, 0, mouseWidth, mouseHeight, 0, 8);
	}


	uint8 *cursor = new uint8[mouseHeight * mouseWidth];
	fillRect(0, 0, mouseWidth, mouseHeight, 0, 8);
	drawShape(8, shape, 0, 0, 0, 0);

	int xOffset = 0;

	if (_vm->gameFlags().useHiResOverlay) {
		xOffset = mouseWidth;
		scale2x(getPagePtr(8) + mouseWidth, SCREEN_W, getPagePtr(8), SCREEN_W, mouseWidth, mouseHeight);
	}

	CursorMan.showMouse(false);
	copyRegionToBuffer(8, xOffset, 0, mouseWidth, mouseHeight, cursor);
	CursorMan.replaceCursor(cursor, mouseWidth, mouseHeight, x, y, 0);
	CursorMan.showMouse(true);
	delete[] cursor;

	// makes sure that the cursor is drawn
	// we do not use Screen::updateScreen here
	// so we can be sure that changes to page 0
	// are NOT updated on the real screen here
	_system->updateScreen();
}

void Screen::copyScreenFromRect(int x, int y, int w, int h, const uint8 *ptr) {
	debugC(9, kDebugLevelScreen, "Screen::copyScreenFromRect(%d, %d, %d, %d, %p)", x, y, w, h, (const void *)ptr);
	x <<= 3; w <<= 3;
	const uint8 *src = ptr;
	uint8 *dst = &_pagePtrs[0][y * SCREEN_W + x];
	for (int i = 0; i < h; ++i) {
		memcpy(dst, src, w);
		src += w;
		dst += SCREEN_W;
	}

	addDirtyRect(x, y, w, h);
	clearOverlayRect(0, x, y, w, h);
}

void Screen::copyScreenToRect(int x, int y, int w, int h, uint8 *ptr) {
	debugC(9, kDebugLevelScreen, "Screen::copyScreenToRect(%d, %d, %d, %d, %p)", x, y, w, h, (const void *)ptr);
	x <<= 3; w <<= 3;
	const uint8 *src = &_pagePtrs[0][y * SCREEN_W + x];
	uint8 *dst = ptr;
	for (int i = 0; i < h; ++i) {
		memcpy(dst, src, w);
		dst += w;
		src += SCREEN_W;
	}
}

uint8 *Screen::getPalette(int num) {
	debugC(9, kDebugLevelScreen, "Screen::getPalette(%d)", num);
	assert(num >= 0 && num < (_vm->gameFlags().platform == Common::kPlatformAmiga ? 6 : 4));
	if (num == 0)
		return _currentPalette;

	return _palettes[num-1];
}

byte Screen::getShapeFlag1(int x, int y) {
	debugC(9, kDebugLevelScreen, "Screen::getShapeFlag1(%d, %d)", x, y);
	uint8 color = _shapePages[0][y * SCREEN_W + x];
	color &= 0x80;
	color ^= 0x80;

	if (color & 0x80)
		return 1;
	return 0;
}

byte Screen::getShapeFlag2(int x, int y) {
	debugC(9, kDebugLevelScreen, "Screen::getShapeFlag2(%d, %d)", x, y);
	uint8 color = _shapePages[0][y * SCREEN_W + x];
	color &= 0x7F;
	color &= 0x87;
	return color;
}

int Screen::getDrawLayer(int x, int y) {
	debugC(9, kDebugLevelScreen, "Screen::getDrawLayer(%d, %d)", x, 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) {
	debugC(9, kDebugLevelScreen, "Screen::getDrawLayer2(%d, %d, %d)", x, y, 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) {
	debugC(9, kDebugLevelScreen, "Screen::setNewShapeHeight(%p, %d)", (const void *)shape, height);
	if (_vm->gameFlags().useAltShapeHeader)
		shape += 2;

	int oldHeight = shape[2];
	shape[2] = height;
	return oldHeight;
}

int Screen::resetShapeHeight(uint8 *shape) {
	debugC(9, kDebugLevelScreen, "Screen::setNewShapeHeight(%p)", (const void *)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) {
	debugC(9, kDebugLevelScreen, "Screen::blockInRegion(%d, %d, %d, %d)", x, y, width, 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) {
	debugC(9, kDebugLevelScreen, "Screen::blockOutRegion(%d, %d, %d, %d)", x, y, width, 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) {
	debugC(9, kDebugLevelScreen, "Screen::shakeScreen(%d)", 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, uint8 *palData, bool skip) {
	debugC(9, kDebugLevelScreen, "KyraEngine_v1::loadBitmap('%s', %d, %d, %p, %d)", filename, tempPage, dstPage, (void *)palData, 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 = scumm_stricmp(ext, "CMP") ? READ_LE_UINT32(srcData + 4) : READ_LE_UINT16(srcData);
	uint16 palSize = READ_LE_UINT16(srcData + 8);

	if (palData && palSize) {
		debugC(9, kDebugLevelMain,"Loading a palette of size %i from %s", palSize, filename);
		loadPalette(srcData + 10, palData, palSize);
	}

	uint8 *srcPtr = srcData + 10 + palSize;
	uint8 *dstData = getPagePtr(dstPage);
	if (dstPage == 0 || tempPage == 0)
		_forceFullUpdate = true;

	switch (compType) {
	case 0:
		memcpy(dstData, srcPtr, 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);
		break;
	}

	if (_vm->gameFlags().platform == Common::kPlatformAmiga) {
		if (!scumm_stricmp(ext, "MSC"))
			Screen::convertAmigaMsc(dstData);
		else
			Screen::convertAmigaGfx(dstData, 320, 200, false);
	}

	if (skip)
		srcData -= 4;

	delete[] srcData;
}

bool Screen::loadPalette(const char *filename, uint8 *palData) {
	debugC(9, kDebugLevelScreen, "Screen::loadPalette('%s' %p)", filename, (void *)palData);
	uint32 fileSize = 0;
	uint8 *srcData = _vm->resource()->fileData(filename, &fileSize);
	if (!srcData)
		return false;

	if (palData && fileSize) {
		debugC(9, kDebugLevelScreen,"Loading a palette of size %u from '%s'", fileSize, filename);
		if (_vm->gameFlags().platform == Common::kPlatformAmiga) {
			assert(fileSize % 2 == 0);
			assert(fileSize / 2 <= 256);
			fileSize >>= 1;
			const uint16 *src = (const uint16 *)srcData;
			for (uint i = 0; i < fileSize; ++i) {
				uint16 col = READ_BE_UINT16(src); ++src;
				palData[2] = (col & 0xF) << 2; col >>= 4;
				palData[1] = (col & 0xF) << 2; col >>= 4;
				palData[0] = (col & 0xF) << 2; col >>= 4;
				palData += 3;
			}
		} else {
			memcpy(palData, srcData, fileSize);
		}
	}
	delete[] srcData;
	return true;
}

void Screen::loadPalette(const byte *data, uint8 *palData, int bytes) {
	debugC(9, kDebugLevelScreen, "Screen::loadPalette(%p, %p %d)", (const void *)data, (void *)palData, bytes);
	if (_vm->gameFlags().platform == Common::kPlatformAmiga) {
		assert(bytes % 2 == 0);
		assert(bytes / 2 <= 256);
		bytes >>= 1;
		const uint16 *src = (const uint16 *)data;
		for (int i = 0; i < bytes; ++i) {
			uint16 col = READ_BE_UINT16(src); ++src;
			palData[2] = (col & 0xF) << 2; col >>= 4;
			palData[1] = (col & 0xF) << 2; col >>= 4;
			palData[0] = (col & 0xF) << 2; col >>= 4;
			palData += 3;
		}
	} else {
		memcpy(palData, data, bytes);
	}
}

// dirty rect handling

void Screen::addDirtyRect(int x, int y, int w, int h) {
	if (_numDirtyRects == kMaxDirtyRects || _forceFullUpdate) {
		_forceFullUpdate = true;
		return;
	}

	if (w == 0 || h == 0 || x >= SCREEN_W || y >= SCREEN_H || x + w < 0 || y + h < 0)
		return;

	if (x < 0) {
		w += x;
		x = 0;
	}

	if (x + w >= 320)
		w = 320 - x;

	if (y < 0) {
		h += y;
		y = 0;
	}

	if (y + h >= 200)
		h = 200 - y;

	Rect &cur = _dirtyRects[_numDirtyRects++];
	cur.x = x;
	cur.x2 = w;
	cur.y = y;
	cur.y2 = h;
}

// 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->gameFlags().gameID == GI_KYRA2) {
		if (page == 12 || page == 13)
			return _sjisOverlayPtrs[3];
	}

	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)
				memcpy(dst, src, w);
			dst += 640;
			src += 640;
		}
	}
}

// SJIS rendering

namespace {
int SJIStoFMTChunk(int f, int s) { // copied from scumm\charset.cpp
	enum {
		KANA = 0,
		KANJI = 1,
		EKANJI = 2
	};
	int base = s - ((s + 1) % 32);
	int c = 0, p = 0, chunk_f = 0, chunk = 0, cr = 0, kanjiType = KANA;

	if (f >= 0x81 && f <= 0x84) kanjiType = KANA;
	if (f >= 0x88 && f <= 0x9f) kanjiType = KANJI;
	if (f >= 0xe0 && f <= 0xea) kanjiType = EKANJI;

	if ((f > 0xe8 || (f == 0xe8 && base >= 0x9f)) || (f > 0x90 || (f == 0x90 && base >= 0x9f))) {
		c = 48; //correction
		p = -8; //correction
	}

	if (kanjiType == KANA) {//Kana
		chunk_f = (f - 0x81) * 2;
	} else if (kanjiType == KANJI) {//Standard Kanji
		p += f - 0x88;
		chunk_f = c + 2 * p;
	} else if (kanjiType == EKANJI) {//Enhanced Kanji
		p += f - 0xe0;
		chunk_f = c + 2 * p;
	}

	// Base corrections
	if (base == 0x7f && s == 0x7f)
		base -= 0x20;
	if (base == 0x9f && s == 0xbe)
		base += 0x20;
	if (base == 0xbf && s == 0xde)
		base += 0x20;
	//if (base == 0x7f && s == 0x9e)
	//	base += 0x20;

	switch (base) {
	case 0x3f:
		cr = 0; //3f
		if (kanjiType == KANA) chunk = 1;
		else if (kanjiType == KANJI) chunk = 31;
		else if (kanjiType == EKANJI) chunk = 111;
		break;
	case 0x5f:
		cr = 0; //5f
		if (kanjiType == KANA) chunk = 17;
		else if (kanjiType == KANJI) chunk = 47;
		else if (kanjiType == EKANJI) chunk = 127;
		break;
	case 0x7f:
		cr = -1; //80
		if (kanjiType == KANA) chunk = 9;
		else if (kanjiType == KANJI) chunk = 63;
		else if (kanjiType == EKANJI) chunk = 143;
		break;
	case 0x9f:
		cr = 1; //9e
		if (kanjiType == KANA) chunk = 2;
		else if (kanjiType == KANJI) chunk = 32;
		else if (kanjiType == EKANJI) chunk = 112;
		break;
	case 0xbf:
		cr = 1; //be
		if (kanjiType == KANA) chunk = 18;
		else if (kanjiType == KANJI) chunk = 48;
		else if (kanjiType == EKANJI) chunk = 128;
		break;
	case 0xdf:
		cr = 1; //de
		if (kanjiType == KANA) chunk = 10;
		else if (kanjiType == KANJI) chunk = 64;
		else if (kanjiType == EKANJI) chunk = 144;
		break;
	default:
		debug(4, "Invalid Char! f %x s %x base %x c %d p %d", f, s, base, c, p);
		return 0;
	}

	debug(6, "Kanji: %c%c f 0x%x s 0x%x base 0x%x c %d p %d chunk %d cr %d index %d", f, s, f, s, base, c, p, chunk, cr, ((chunk_f + chunk) * 32 + (s - base)) + cr);
	return ((chunk_f + chunk) * 32 + (s - base)) + cr;
}
} // end of anonymous namespace

void Screen::drawCharSJIS(uint16 c, int x, int y) {
	debugC(9, kDebugLevelScreen, "Screen::drawCharSJIS('%c', %d, %d)", c, x, y);

	int color1 = _textColorsMap[1];
	int color2 = _textColorsMap[0];

	memset(_sjisTempPage2, _sjisInvisibleColor, 324);
	memset(_sjisSourceChar, 0, 36);
	memcpy(_sjisSourceChar, _sjisFontData + 0x20 * SJIStoFMTChunk(c & 0xff, c >> 8), 0x20);

	if (_curPage == 0 || _curPage == 1)
		addDirtyRect(x, y, SJIS_CHARSIZE >> 1, SJIS_CHARSIZE >> 1);

	x <<= 1;
	y <<= 1;

	uint8 *destPage = getOverlayPtr(_curPage);
	if (!destPage) {
		warning("trying to draw SJIS char on unsupported page %d", _curPage);
		return;
	}

	destPage += y * 640 + x;
	uint8 *src = 0, *dst = 0;

	if (color2 != _sjisInvisibleColor) {
		// draw color2 shadow
		src = _sjisSourceChar;
		dst = _sjisTempPage2;

		for (int i = 0; i < SJIS_CHARSIZE; i++) {
			*((uint16*)dst) = READ_LE_UINT16(src);
			dst += 2; src += 2;
			*dst++ = 0;
		}

		src = _sjisTempPage2;
		dst = _sjisTempPage;
		memset(dst, 0, 60);
		for (int i = 0; i < 48; i++)
			*dst++ |= *src++;

		src = _sjisTempPage2;
		dst = _sjisTempPage + 3;
		for (int i = 0; i < 48; i++)
			*dst++ |= *src++;

		src = _sjisTempPage2;
		dst = _sjisTempPage + 6;
		for (int i = 0; i < 48; i++)
			*dst++ |= *src++;

		for (int i = 0; i < 2; i++) {
			src = _sjisTempPage;
			for (int ii = 0; ii < SJIS_CHARSIZE; ii++) {
				uint8 cy2 = 0;
				uint8 cy1 = 0;
				for (int iii = 0; iii < 3; iii++) {
					cy1 = *src & 1;
					*src |= ((*src >> 1) | (cy2 << 7));
					cy2 = cy1;
					src++;
				}
			}
		}

		src = _sjisTempPage2;
		for (int i = 0; i < SJIS_CHARSIZE; i++) {
			uint8 cy2 = 0;
			uint8 cy1 = 0;
			for (int ii = 0; ii < 3; ii++) {
				cy1 = *src & 1;
				*src = ((*src >> 1) | (cy2 << 7));
				cy2 = cy1;
				src++;
			}
		}

		src = _sjisTempPage2;
		dst = _sjisTempPage + 3;
		for (int i = 0; i < 48; i++)
			*dst++ ^= *src++;

		memset(_sjisTempPage2, _sjisInvisibleColor, 324);
		src = _sjisTempPage;
		dst = _sjisTempPage2;

		uint8 height = SJIS_CHARSIZE * 3;
		uint8 width = SJIS_CHARSIZE;
		if (color2 & 0xff00) {
			height -= 3;
			width--;
			dst += 0x13;
		}

		for (int i = 0; i < height; i++) {
			uint8 rs = *src++;
			for (int ii = 0; ii < 8; ii++) {
				if (rs & 0x80)
					*dst = (color2 & 0xff);
				rs <<= 1;
				dst++;

				if (!--width) {
					width = SJIS_CHARSIZE;
					if (color2 & 0xff00) {
						width--;
						dst++;
					}
					break;
				}
			}
		}
	}

	//	draw color1 char
	src = _sjisSourceChar;
	dst = _sjisTempPage;

	for (int i = 0; i < SJIS_CHARSIZE; i++) {
		*(uint16*)dst = READ_LE_UINT16(src);
		dst += 2; src += 2;
		*dst++ = 0;
	}

	src = _sjisTempPage;
	dst = _sjisTempPage2;
	if (color2 != _sjisInvisibleColor)
		color1 = (color1 & 0xff) | 0x100;

	uint8 height = SJIS_CHARSIZE * 3;
	uint8 width = SJIS_CHARSIZE;
	if (color1 & 0xff00) {
		height -= 3;
		width--;
		dst += 0x13;
	}

	for (int i = 0; i < height; i++) {
		uint8 rs = *src++;
		for (int ii = 0; ii < 8; ii++) {
			if (rs & 0x80)
				*dst = (color1 & 0xff);
			rs <<= 1;
			dst++;

			if (!--width) {
				width = SJIS_CHARSIZE;
				if (color1 & 0xff00) {
					width--;
					dst++;
				}
				break;
			}
		}
	}

	//	copy char to surface
	src = _sjisTempPage2;
	dst = destPage;
	int pitch = 640 - SJIS_CHARSIZE;

	for (int i = 0; i < SJIS_CHARSIZE; i++) {
		for (int ii = 0; ii < SJIS_CHARSIZE; ii++) {
			if (*src != _sjisInvisibleColor)
				*dst = *src;
			src++;
			dst++;
		}
		dst += pitch;
	}
}

#pragma mark -

} // End of namespace Kyra

