1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "kyra/graphics/screen.h"
24 #include "kyra/kyra_v1.h"
25 #include "kyra/resource/resource.h"
26 
27 #include "common/endian.h"
28 #include "common/memstream.h"
29 #include "common/system.h"
30 #include "common/config-manager.h"
31 
32 #include "engines/util.h"
33 
34 #include "graphics/cursorman.h"
35 #include "graphics/palette.h"
36 #include "graphics/sjis.h"
37 
38 namespace Kyra {
39 
Screen(KyraEngine_v1 * vm,OSystem * system,const ScreenDim * dimTable,const int dimTableSize)40 Screen::Screen(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, const int dimTableSize)
41 	: _system(system), _vm(vm), _sjisInvisibleColor(0), _dimTable(dimTable), _dimTableCount(dimTableSize),
42 	_cursorColorKey((vm->game() == GI_KYRA1 || vm->game() == GI_EOB1 || vm->game() == GI_EOB2) ? 0xFF : 0),
43 	_screenHeight(vm->gameFlags().platform == Common::kPlatformSegaCD ? SCREEN_H_SEGA_NTSC : SCREEN_H) {
44 	_debugEnabled = false;
45 	_maskMinY = _maskMaxY = -1;
46 
47 	_drawShapeVar1 = 0;
48 	_drawShapeVar3 = 1;
49 	_drawShapeVar4 = 0;
50 	_drawShapeVar5 = 0;
51 
52 	memset(_fonts, 0, sizeof(_fonts));
53 
54 	memset(_pagePtrs, 0, sizeof(_pagePtrs));
55 	memset(_pageMapping, 0, sizeof(_pageMapping));
56 	memset(_sjisOverlayPtrs, 0, sizeof(_sjisOverlayPtrs));
57 
58 	_renderMode = Common::kRenderDefault;
59 	_sjisMixedFontMode = false;
60 
61 	_screenPalette = _internFadePalette = 0;
62 	_animBlockPtr = _textRenderBuffer = 0;
63 	_textRenderBufferSize = 0;
64 
65 	_useHiColorScreen = _vm->gameFlags().useHiColorMode;
66 	_useShapeShading = true;
67 	_screenPageSize = SCREEN_PAGE_SIZE;
68 	_16bitPalette = 0;
69 	_16bitConversionPalette = 0;
70 	_16bitShadingLevel = 0;
71 	_bytesPerPixel = 1;
72 	_4bitPixelPacking = _useAmigaExtraColors = _isAmiga = _isSegaCD = _use16ColorMode = false;
73 	_useSJIS = _useOverlays = false;
74 
75 	_currentFont = FID_8_FNT;
76 	_fontStyles = 0;
77 	_paletteChanged = true;
78 	_textMarginRight = SCREEN_W;
79 	_customDimTable = 0;
80 	_curDim = 0;
81 
82 	_yTransOffs = 0;
83 }
84 
~Screen()85 Screen::~Screen() {
86 	for (int i = 0; i < SCREEN_OVLS_NUM; ++i)
87 		delete[] _sjisOverlayPtrs[i];
88 
89 	delete[] _pagePtrs[0];
90 
91 	for (int f = 0; f < ARRAYSIZE(_fonts); ++f)
92 		delete _fonts[f];
93 
94 	delete _screenPalette;
95 	delete _internFadePalette;
96 	delete[] _animBlockPtr;
97 	delete[] _16bitPalette;
98 	delete[] _16bitConversionPalette;
99 
100 	_sjisFontShared.reset();
101 
102 	for (uint i = 0; i < _palettes.size(); ++i)
103 		delete _palettes[i];
104 
105 	if (_customDimTable) {
106 		for (int i = 0; i < _dimTableCount; ++i)
107 			delete _customDimTable[i];
108 		delete[] _customDimTable;
109 	}
110 }
111 
init()112 bool Screen::init() {
113 	_debugEnabled = false;
114 	_useOverlays = false;
115 	_useSJIS = false;
116 	_use16ColorMode = _vm->gameFlags().use16ColorMode;
117 	_4bitPixelPacking = (_use16ColorMode && _vm->game() == GI_LOL);
118 	_isAmiga = (_vm->gameFlags().platform == Common::kPlatformAmiga);
119 	_isSegaCD = (_vm->gameFlags().platform == Common::kPlatformSegaCD);
120 	// Amiga copper palette magic requires the use of more than 32 colors for some purposes.
121 	_useAmigaExtraColors = (_isAmiga && _vm->game() == GI_EOB2);
122 
123 	// We only check the "render_mode" setting for both Eye of the Beholder
124 	// games here, since all the other games do not support the render_mode
125 	// setting or handle it differently, like Kyra 1 PC-98. This avoids
126 	// graphics glitches and crashes in other games, when the user sets his
127 	// global render_mode setting to EGA for example.
128 	// TODO/FIXME: It would be nice not to hardcode this. But there is no
129 	// trivial/non annoying way to do mode checks in an easy fashion right
130 	// now.
131 	// In a more general sense, we might want to think about a way to only
132 	// pass valid config values, as in values which the engine can work with,
133 	// to the engines. We already limit the selection via our GUIO flags in
134 	// the game specific settings, but this is not enough due to global
135 	// settings allowing everything.
136 	if (_vm->game() == GI_EOB1 || _vm->game() == GI_EOB2) {
137 		if (ConfMan.hasKey("render_mode"))
138 			_renderMode = Common::parseRenderMode(ConfMan.get("render_mode"));
139 	}
140 
141 	// In VGA mode the odd and even page pointers point to the same buffers.
142 	for (int i = 0; i < SCREEN_PAGE_NUM; i++)
143 		_pageMapping[i] = i & ~1;
144 	// CGA and EGA modes use additional pages to do the CGA/EGA specific graphics conversions.
145 	if (_vm->game() == GI_EOB1 && (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA)) {
146 		for (int i = 0; i < 8; i++)
147 			_pageMapping[i] = i;
148 	}
149 
150 	memset(_fonts, 0, sizeof(_fonts));
151 
152 	_useOverlays = (_vm->gameFlags().useHiRes && _renderMode != Common::kRenderEGA);
153 
154 	if (_useOverlays) {
155 		_useSJIS = (_vm->gameFlags().lang == Common::JA_JPN);
156 		_sjisInvisibleColor = (_vm->game() == GI_KYRA1) ? 0x80 : 0xF6;
157 		_sjisMixedFontMode = !_use16ColorMode;
158 
159 		if (!_sjisOverlayPtrs[0]) {
160 			// 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.
161 			// We do this regardsless of the paramater sent to enableHiColorMode() so as not to have to change the backend color mode.
162 			// Conversions from 8bit to 16bit have to take place when copying data to this surface here.
163 			int bpp = _useHiColorScreen ? 2 : 1;
164 			_sjisOverlayPtrs[0] = new uint8[SCREEN_OVL_SJIS_SIZE * bpp];
165 			assert(_sjisOverlayPtrs[0]);
166 			memset(_sjisOverlayPtrs[0], _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE * bpp);
167 		}
168 
169 		for (int i = 1; i < SCREEN_OVLS_NUM; ++i) {
170 			if (!_sjisOverlayPtrs[i]) {
171 				_sjisOverlayPtrs[i] = new uint8[SCREEN_OVL_SJIS_SIZE];
172 				assert(_sjisOverlayPtrs[i]);
173 				memset(_sjisOverlayPtrs[i], _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE);
174 			}
175 		}
176 
177 		if (_useSJIS) {
178 			_sjisFontShared = Common::SharedPtr<Graphics::FontSJIS>(Graphics::FontSJIS::createFont(_vm->gameFlags().platform));
179 			if (!_sjisFontShared.get())
180 				error("Could not load any SJIS font, neither the original nor ScummVM's 'SJIS.FNT'");
181 
182 			if (_use16ColorMode)
183 				_fonts[FID_SJIS_TEXTMODE_FNT] = new SJISFont(_sjisFontShared, _sjisInvisibleColor, true, false, 0);
184 			else
185 				_fonts[FID_SJIS_FNT] = new SJISFont(_sjisFontShared, _sjisInvisibleColor, false, _vm->game() != GI_LOL && _vm->game() != GI_EOB2, _vm->game() == GI_LOL ? 1 : 0);
186 		}
187 	}
188 
189 	_curPage = 0;
190 
191 	enableHiColorMode(false);
192 
193 	memset(_shapePages, 0, sizeof(_shapePages));
194 
195 	const int paletteCount = _isAmiga ? 13 : 4;
196 	// We allow 256 color palettes in EGA mode, since original EOB II code does the same and requires it
197 	const int numColors = _use16ColorMode ? 16 : (_isAmiga ? 32 : (_renderMode == Common::kRenderCGA ? 4 : 256));
198 	const int numColorsInternal = _useAmigaExtraColors ? 64 : numColors;
199 
200 	_dualPaletteModeSplitY = 0;
201 
202 	_screenPalette = new Palette(numColorsInternal);
203 	assert(_screenPalette);
204 
205 	_palettes.resize(paletteCount);
206 	_palettes[0] = new Palette(numColorsInternal);
207 	assert(_palettes[0]);
208 
209 	for (int i = 1; i < paletteCount; ++i) {
210 		_palettes[i] = new Palette(numColors);
211 		assert(_palettes[i]);
212 	}
213 
214 	// Setup CGA colors (if CGA mode is selected)
215 	if (_renderMode == Common::kRenderCGA) {
216 		Palette pal(5);
217 		pal.setCGAPalette(1, Palette::kIntensityHigh);
218 		// create additional black color 4 for use with the mouse cursor manager
219 		pal.fill(4, 1, 0);
220 		Screen::setScreenPalette(pal);
221 	}
222 
223 	_internFadePalette = new Palette(numColorsInternal);
224 	assert(_internFadePalette);
225 
226 	setScreenPalette(getPalette(0));
227 
228 	// We setup the PC98 text mode palette at [16, 24], since that will be used
229 	// for KANJI characters in Lands of Lore.
230 	if (_use16ColorMode && _vm->gameFlags().platform == Common::kPlatformPC98) {
231 		uint8 palette[8 * 3];
232 
233 		for (int i = 0; i < 8; ++i) {
234 			palette[i * 3 + 0] = ((i >> 1) & 1) * 0xFF;
235 			palette[i * 3 + 1] = ((i >> 2) & 1) * 0xFF;
236 			palette[i * 3 + 2] = ((i >> 0) & 1) * 0xFF;
237 		}
238 
239 		_system->getPaletteManager()->setPalette(palette, 16, 8);
240 	}
241 
242 	_customDimTable = new ScreenDim *[_dimTableCount];
243 	memset(_customDimTable, 0, sizeof(ScreenDim *) * _dimTableCount);
244 
245 	_curDimIndex = -1;
246 	_curDim = 0;
247 	_charSpacing = 0;
248 	_lineSpacing = 0;
249 	for (int i = 0; i < ARRAYSIZE(_textColorsMap); ++i)
250 		_textColorsMap[i] = i;
251 	_textColorsMap16bit[0] = _textColorsMap16bit[1] = 0;
252 	_animBlockPtr = NULL;
253 	_animBlockSize = 0;
254 	_mouseLockCount = 1;
255 	CursorMan.showMouse(false);
256 
257 	_forceFullUpdate = false;
258 
259 	return true;
260 }
261 
enableScreenDebug(bool enable)262 bool Screen::enableScreenDebug(bool enable) {
263 	bool temp = _debugEnabled;
264 
265 	if (_debugEnabled != enable) {
266 		_debugEnabled = enable;
267 		setResolution();
268 		_forceFullUpdate = true;
269 		updateScreen();
270 	}
271 
272 	return temp;
273 }
274 
setResolution()275 void Screen::setResolution() {
276 	byte palette[3 * 256];
277 	if (!_useHiColorScreen)
278 		_system->getPaletteManager()->grabPalette(palette, 0, 256);
279 
280 	int width = 320, height = 200;
281 
282 	if (_vm->gameFlags().useHiRes) {
283 		height = 400;
284 
285 		if (_debugEnabled)
286 			width = 960;
287 		else
288 			width = 640;
289 	} else {
290 		if (_debugEnabled)
291 			width = 640;
292 		else
293 			width = 320;
294 	}
295 
296 	if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
297 		height = 224;
298 
299 	if (_useHiColorScreen) {
300 		Graphics::PixelFormat px(2, 5, 5, 5, 0, 10, 5, 0, 0);
301 		Common::List<Graphics::PixelFormat> tryModes = _system->getSupportedFormats();
302 		for (Common::List<Graphics::PixelFormat>::iterator g = tryModes.begin(); g != tryModes.end(); ++g) {
303 			if (g->bytesPerPixel != 2 || g->aBits()) {
304 				g = tryModes.reverse_erase(g);
305 			} else if (*g == px) {
306 				tryModes.clear();
307 				tryModes.push_back(px);
308 				break;
309 			}
310 		}
311 		initGraphics(width, height, tryModes);
312 		if (_system->getScreenFormat().bytesPerPixel != 2)
313 			error("Required graphics mode not supported by platform.");
314 
315 	} else {
316 		initGraphics(width, height);
317 		_system->getPaletteManager()->setPalette(palette, 0, 256);
318 	}
319 }
320 
enableHiColorMode(bool enabled)321 void Screen::enableHiColorMode(bool enabled) {
322 	if (_useHiColorScreen && enabled) {
323 		if (!_16bitPalette)
324 			_16bitPalette = new uint16[1024];
325 		memset(_16bitPalette, 0, 1024 * sizeof(uint16));
326 		delete[] _16bitConversionPalette;
327 		_16bitConversionPalette = 0;
328 		_bytesPerPixel = 2;
329 	} else {
330 		if (_useHiColorScreen) {
331 			if (!_16bitConversionPalette)
332 				_16bitConversionPalette = new uint16[256];
333 			memset(_16bitConversionPalette, 0, 256 * sizeof(uint16));
334 		}
335 
336 		delete[] _16bitPalette;
337 		_16bitPalette = 0;
338 		_bytesPerPixel = 1;
339 	}
340 
341 	resetPagePtrsAndBuffers(_isSegaCD ? SCREEN_W * _screenHeight : SCREEN_PAGE_SIZE * _bytesPerPixel);
342 }
343 
updateScreen()344 void Screen::updateScreen() {
345 	bool needRealUpdate = _forceFullUpdate || !_dirtyRects.empty() || _paletteChanged;
346 	_paletteChanged = false;
347 
348 	if (_useOverlays)
349 		updateDirtyRectsOvl();
350 	else if (_isAmiga && _dualPaletteModeSplitY)
351 		updateDirtyRectsAmiga();
352 	else
353 		updateDirtyRects();
354 
355 	if (_debugEnabled) {
356 		needRealUpdate = true;
357 
358 		if (!_useOverlays)
359 			_system->copyRectToScreen(getPagePtr(2), SCREEN_W, 320, 0, SCREEN_W, SCREEN_H);
360 		else
361 			_system->copyRectToScreen(getPagePtr(2), SCREEN_W, 640, 0, SCREEN_W, SCREEN_H);
362 	}
363 
364 	if (needRealUpdate)
365 		_system->updateScreen();
366 }
367 
updateDirtyRects()368 void Screen::updateDirtyRects() {
369 	if (_forceFullUpdate) {
370 		_system->copyRectToScreen(getCPagePtr(0), SCREEN_W, 0, _yTransOffs, SCREEN_W, _screenHeight - _yTransOffs);
371 	} else {
372 		const byte *page0 = getCPagePtr(0);
373 		Common::List<Common::Rect>::iterator it;
374 		for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
375 			_system->copyRectToScreen(page0 + it->top * SCREEN_W + it->left, SCREEN_W, it->left, it->top + _yTransOffs, it->width(), it->height());
376 		}
377 	}
378 	_forceFullUpdate = false;
379 	_dirtyRects.clear();
380 }
381 
updateDirtyRectsAmiga()382 void Screen::updateDirtyRectsAmiga() {
383 	if (_forceFullUpdate) {
384 		uint32 *pos = (uint32*)(_pagePtrs[0] + _dualPaletteModeSplitY * SCREEN_W);
385 		uint16 h = (SCREEN_H - _dualPaletteModeSplitY) * (SCREEN_W >> 2);
386 		while (h--)
387 			*pos++ |= 0x20202020;
388 		_system->copyRectToScreen(getCPagePtr(0), SCREEN_W, 0, 0, SCREEN_W, SCREEN_H);
389 	} else {
390 		Common::List<Common::Rect>::iterator it;
391 		for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
392 			if (it->bottom >= _dualPaletteModeSplitY) {
393 				int16 startY = MAX<int16>(_dualPaletteModeSplitY, it->top);
394 				int16 h = it->bottom - startY + 1;
395 				int16 w = it->width();
396 				uint8 *pos = _pagePtrs[0] + startY * SCREEN_W + it->left;
397 				while (h--) {
398 					for (int x = 0; x < w; ++x)
399 						*pos++ |= 0x20;
400 					pos += (SCREEN_W - w);
401 				}
402 			}
403 			_system->copyRectToScreen(_pagePtrs[0] + it->top * SCREEN_W + it->left, SCREEN_W, it->left, it->top, it->width(), it->height());
404 		}
405 	}
406 
407 	_forceFullUpdate = false;
408 	_dirtyRects.clear();
409 }
410 
411 #define mScale2x(dst, dstPitch, src, srcPitch, w, h) \
412 	if (!_useHiColorScreen) \
413 		scale2x<uint8, uint16>(dst, dstPitch, src, srcPitch, w, h); \
414 	else if (_bytesPerPixel == 2) \
415 		scale2x<uint16, uint32>(dst, dstPitch, src, srcPitch, w, h); \
416 	 else \
417 		scale2x<uint8, uint32>(dst, dstPitch, src, srcPitch, w, h)
418 
updateDirtyRectsOvl()419 void Screen::updateDirtyRectsOvl() {
420 	if (_forceFullUpdate) {
421 		const byte *src = getCPagePtr(0);
422 		byte *dst = _sjisOverlayPtrs[0];
423 		mScale2x(dst, 640, src, SCREEN_W, SCREEN_W, SCREEN_H);
424 		mergeOverlay(0, 0, 640, 400);
425 		_system->copyRectToScreen(dst, _useHiColorScreen ? 1280 : 640, 0, 0, 640, 400);
426 	} else {
427 		const byte *page0 = getCPagePtr(0);
428 		byte *ovl0 = _sjisOverlayPtrs[0];
429 		int dstBpp = _useHiColorScreen ? 2 : 1;
430 
431 		Common::List<Common::Rect>::iterator it;
432 		for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
433 			byte *dst = ovl0 + it->top * 1280 * dstBpp + (it->left << dstBpp);
434 			const byte *src = page0 + it->top * SCREEN_W * _bytesPerPixel + it->left * _bytesPerPixel;
435 			mScale2x(dst, 640, src, SCREEN_W, it->width(), it->height());
436 			mergeOverlay(it->left<<1, it->top<<1, it->width()<<1, it->height()<<1);
437 			_system->copyRectToScreen(dst, _useHiColorScreen ? 1280 : 640, it->left << 1, it->top << 1, it->width() << 1, it->height() << 1);
438 		}
439 	}
440 
441 	_forceFullUpdate = false;
442 	_dirtyRects.clear();
443 }
444 
445 #undef mScale2x
446 
447 template<typename srcType, typename scaleToType>
scale2x(uint8 * dst,int dstPitch,const uint8 * src,int srcPitch,int w,int h)448 void Screen::scale2x(uint8 *dst, int dstPitch, const uint8 *src, int srcPitch, int w, int h) {
449 	int dstAdd = dstPitch - w;
450 	int srcAdd = srcPitch - w;
451 	scaleToType *dstL1 = (scaleToType*)dst;
452 	scaleToType *dstL2 = (scaleToType*)(dst + dstPitch * (sizeof(scaleToType) >> 1));
453 	const srcType *src1 = (const srcType*)src;
454 
455 	while (h--) {
456 		for (int x = 0; x < w; x++) {
457 			scaleToType col = (sizeof(srcType) == 1 && sizeof(scaleToType) == 4) ? _16bitConversionPalette[*src1++] : *src1++;
458 			*dstL1++ = *dstL2++ = col | (col << (sizeof(scaleToType) << 2));
459 		}
460 		dstL1 += dstAdd; dstL2 += dstAdd;
461 		src1 += srcAdd;
462 	}
463 }
464 
465 template void Screen::scale2x<uint8, uint16>(uint8 *dst, int dstPitch, const uint8 *src, int srcPitch, int w, int h);
466 template void Screen::scale2x<uint16, uint32>(uint8 *dst, int dstPitch, const uint8 *src, int srcPitch, int w, int h);
467 template void Screen::scale2x<uint8, uint32>(uint8 *dst, int dstPitch, const uint8 *src, int srcPitch, int w, int h);
468 
469 template<typename pixelType>
mergeOverlayImpl(int x,int y,int w,int h)470 void Screen::mergeOverlayImpl(int x, int y, int w, int h) {
471 	const uint8 *src = _sjisOverlayPtrs[1] + y * 640 + x;
472 	uint16 *p16 = _16bitPalette ? _16bitPalette : _16bitConversionPalette;
473 	pixelType *dst = (pixelType*)(_sjisOverlayPtrs[0] + y * 640 * sizeof(pixelType) + x * sizeof(pixelType));
474 	int add = 640 - w;
475 
476 	while (h--) {
477 		for (x = 0; x < w; ++x) {
478 			uint8 col = *src++;
479 			if (col != _sjisInvisibleColor)
480 				*dst = (sizeof(pixelType) == 2) ? p16[col] : col;
481 			dst++;
482 		}
483 		dst += add;
484 		src += add;
485 	}
486 }
487 
488 template void Screen::mergeOverlayImpl<uint8>(int x, int y, int w, int h);
489 template void Screen::mergeOverlayImpl<uint16>(int x, int y, int w, int h);
490 
getScreenDim(int dim) const491 const ScreenDim *Screen::getScreenDim(int dim) const {
492 	assert(dim < _dimTableCount);
493 	return _customDimTable[dim] ? _customDimTable[dim] : &_dimTable[dim];
494 }
495 
modifyScreenDim(int dim,int x,int y,int w,int h)496 void Screen::modifyScreenDim(int dim, int x, int y, int w, int h) {
497 	if (!_customDimTable[dim])
498 		_customDimTable[dim] = new ScreenDim;
499 
500 	memcpy(_customDimTable[dim], &_dimTable[dim], sizeof(ScreenDim));
501 	_customDimTable[dim]->sx = x;
502 	_customDimTable[dim]->sy = y;
503 	_customDimTable[dim]->w = w;
504 	_customDimTable[dim]->h = h;
505 	if (dim == _curDimIndex || _vm->game() == GI_LOL)
506 		setScreenDim(dim);
507 }
508 
setScreenDim(int dim)509 void Screen::setScreenDim(int dim) {
510 	_curDim = getScreenDim(dim);
511 	_curDimIndex = dim;
512 }
513 
resetPagePtrsAndBuffers(int pageSize)514 void Screen::resetPagePtrsAndBuffers(int pageSize) {
515 	_screenPageSize = pageSize;
516 
517 	delete[] _pagePtrs[0];
518 	memset(_pagePtrs, 0, sizeof(_pagePtrs));
519 
520 	Common::Array<uint8> realPages;
521 	for (int i = 0; i < SCREEN_PAGE_NUM; i++) {
522 		if (Common::find(realPages.begin(), realPages.end(), _pageMapping[i]) == realPages.end())
523 			realPages.push_back(_pageMapping[i]);
524 	}
525 
526 	int numPages = realPages.size();
527 	uint32 bufferSize = numPages * _screenPageSize;
528 
529 	uint8 *pagePtr = new uint8[bufferSize];
530 	memset(pagePtr, 0, bufferSize);
531 
532 	memset(_pagePtrs, 0, sizeof(_pagePtrs));
533 	for (int i = 0; i < SCREEN_PAGE_NUM; i++) {
534 		if (_pagePtrs[_pageMapping[i]]) {
535 			_pagePtrs[i] = _pagePtrs[_pageMapping[i]];
536 		} else {
537 			_pagePtrs[i] = pagePtr;
538 			pagePtr += _screenPageSize;
539 		}
540 	}
541 }
542 
getPagePtr(int pageNum)543 uint8 *Screen::getPagePtr(int pageNum) {
544 	assert(pageNum < SCREEN_PAGE_NUM);
545 	return _pagePtrs[pageNum];
546 }
547 
getCPagePtr(int pageNum) const548 const uint8 *Screen::getCPagePtr(int pageNum) const {
549 	assert(pageNum < SCREEN_PAGE_NUM);
550 	return _pagePtrs[pageNum];
551 }
552 
getPageRect(int pageNum,int x,int y,int w,int h)553 uint8 *Screen::getPageRect(int pageNum, int x, int y, int w, int h) {
554 	assert(pageNum < SCREEN_PAGE_NUM);
555 	if (pageNum == 0 || pageNum == 1)
556 		addDirtyRect(x, y, w, h);
557 	return _pagePtrs[pageNum] + y * SCREEN_W + x;
558 }
559 
clearPage(int pageNum)560 void Screen::clearPage(int pageNum) {
561 	assert(pageNum < SCREEN_PAGE_NUM);
562 	if (pageNum == 0 || pageNum == 1)
563 		_forceFullUpdate = true;
564 	memset(getPagePtr(pageNum), 0, _screenPageSize);
565 	clearOverlayPage(pageNum);
566 }
567 
setCurPage(int pageNum)568 int Screen::setCurPage(int pageNum) {
569 	assert(pageNum < SCREEN_PAGE_NUM);
570 	int previousPage = _curPage;
571 	_curPage = pageNum;
572 	return previousPage;
573 }
574 
clearCurPage()575 void Screen::clearCurPage() {
576 	if (_curPage == 0 || _curPage == 1)
577 		_forceFullUpdate = true;
578 	memset(getPagePtr(_curPage), 0, _screenPageSize);
579 	clearOverlayPage(_curPage);
580 }
581 
copyWsaRect(int x,int y,int w,int h,int dimState,int plotFunc,const uint8 * src,int unk1,const uint8 * unkPtr1,const uint8 * unkPtr2)582 void Screen::copyWsaRect(int x, int y, int w, int h, int dimState, int plotFunc, const uint8 *src,
583 						int unk1, const uint8 *unkPtr1, const uint8 *unkPtr2) {
584 	uint8 *dstPtr = getPagePtr(_curPage);
585 	uint8 *origDst = dstPtr;
586 
587 	const ScreenDim *dim = getScreenDim(dimState);
588 	int dimX1 = dim->sx << 3;
589 	int dimX2 = dim->w << 3;
590 	dimX2 += dimX1;
591 
592 	int dimY1 = dim->sy;
593 	int dimY2 = dim->h;
594 	dimY2 += dimY1;
595 
596 	int temp = y - dimY1;
597 	if (temp < 0) {
598 		if ((temp += h) <= 0)
599 			return;
600 		else {
601 			SWAP(temp, h);
602 			y += temp - h;
603 			src += (temp - h) * w;
604 		}
605 	}
606 
607 	temp = dimY2 - y;
608 	if (temp <= 0)
609 		return;
610 
611 	if (temp < h)
612 		h = temp;
613 
614 	int srcOffset = 0;
615 	temp = x - dimX1;
616 	if (temp < 0) {
617 		temp = -temp;
618 		srcOffset = temp;
619 		x += temp;
620 		w -= temp;
621 	}
622 
623 	int srcAdd = 0;
624 
625 	temp = dimX2 - x;
626 	if (temp <= 0)
627 		return;
628 
629 	if (temp < w) {
630 		SWAP(w, temp);
631 		temp -= w;
632 		srcAdd = temp;
633 	}
634 
635 	dstPtr += y * SCREEN_W + x;
636 	uint8 *dst = dstPtr;
637 
638 	if (_curPage == 0 || _curPage == 1)
639 		addDirtyRect(x, y, w, h);
640 
641 	if (!_use16ColorMode)
642 		clearOverlayRect(_curPage, x, y, w, h);
643 
644 	temp = h;
645 	int curY = y;
646 	while (h--) {
647 		src += srcOffset;
648 		++curY;
649 		int cW = w;
650 
651 		switch (plotFunc) {
652 		case 0:
653 			memcpy(dst, src, cW);
654 			dst += cW; src += cW;
655 			break;
656 
657 		case 1:
658 			while (cW--) {
659 				uint8 d = *src++;
660 				uint8 t = unkPtr1[d];
661 				if (t != 0xFF)
662 					d = unkPtr2[*dst + (t << 8)];
663 				*dst++ = d;
664 			}
665 			break;
666 
667 		case 4:
668 			while (cW--) {
669 				uint8 d = *src++;
670 				if (d)
671 					*dst = d;
672 				++dst;
673 			}
674 			break;
675 
676 		case 5:
677 			while (cW--) {
678 				uint8 d = *src++;
679 				if (d) {
680 					uint8 t = unkPtr1[d];
681 					if (t != 0xFF)
682 						d = unkPtr2[*dst + (t << 8)];
683 					*dst = d;
684 				}
685 				++dst;
686 			}
687 			break;
688 
689 		case 8:
690 		case 9:
691 			while (cW--) {
692 				uint8 d = *src++;
693 				uint8 t = _shapePages[0][dst - origDst] & 7;
694 				if (unk1 < t && (curY > _maskMinY && curY < _maskMaxY))
695 					d = _shapePages[1][dst - origDst];
696 				*dst++ = d;
697 			}
698 			break;
699 
700 		case 12:
701 		case 13:
702 			while (cW--) {
703 				uint8 d = *src++;
704 				if (d) {
705 					uint8 t = _shapePages[0][dst - origDst] & 7;
706 					if (unk1 < t && (curY > _maskMinY && curY < _maskMaxY))
707 						d = _shapePages[1][dst - origDst];
708 					*dst++ = d;
709 				} else {
710 					d = _shapePages[1][dst - origDst];
711 					*dst++ = d;
712 				}
713 			}
714 			break;
715 
716 		default:
717 			break;
718 		}
719 
720 		dst = (dstPtr += SCREEN_W);
721 		src += srcAdd;
722 	}
723 }
724 
getPagePixel(int pageNum,int x,int y)725 int Screen::getPagePixel(int pageNum, int x, int y) {
726 	assert(pageNum < SCREEN_PAGE_NUM);
727 	assert(x >= 0 && x < SCREEN_W && y >= 0 && y < _screenHeight);
728 	if (_bytesPerPixel == 1)
729 		return _pagePtrs[pageNum][y * SCREEN_W + x];
730 	else
731 		return ((uint16*)_pagePtrs[pageNum])[y * SCREEN_W + x];
732 }
733 
setPagePixel(int pageNum,int x,int y,uint8 color)734 void Screen::setPagePixel(int pageNum, int x, int y, uint8 color) {
735 	assert(pageNum < SCREEN_PAGE_NUM);
736 	assert(x >= 0 && x < SCREEN_W && y >= 0 && y < _screenHeight);
737 
738 	if (pageNum == 0 || pageNum == 1)
739 		addDirtyRect(x, y, 1, 1);
740 
741 	if (_4bitPixelPacking) {
742 		color &= 0x0F;
743 		color |= (color << 4);
744 	} else if (_renderMode == Common::kRenderCGA) {
745 		color &= 0x03;
746 	} else if (_use16ColorMode || (_renderMode == Common::kRenderEGA && !_useHiResEGADithering)) {
747 		color &= 0x0F;
748 	}
749 
750 	if (_bytesPerPixel == 2) {
751 		((uint16*)_pagePtrs[pageNum])[y * SCREEN_W + x] = _16bitPalette[color];
752 	} else {
753 		_pagePtrs[pageNum][y * SCREEN_W + x] = color;
754 	}
755 }
756 
fadeFromBlack(int delay,const UpdateFunctor * upFunc)757 void Screen::fadeFromBlack(int delay, const UpdateFunctor *upFunc) {
758 	fadePalette(getPalette(0), delay, upFunc);
759 }
760 
fadeToBlack(int delay,const UpdateFunctor * upFunc)761 void Screen::fadeToBlack(int delay, const UpdateFunctor *upFunc) {
762 	if (_renderMode == Common::kRenderEGA)
763 		return;
764 
765 	Palette pal(getPalette(0).getNumColors());
766 	fadePalette(pal, delay, upFunc);
767 }
768 
fadePalette(const Palette & pal,int delay,const UpdateFunctor * upFunc)769 void Screen::fadePalette(const Palette &pal, int delay, const UpdateFunctor *upFunc) {
770 	if (_renderMode == Common::kRenderEGA || _bytesPerPixel == 2)
771 		setScreenPalette(pal);
772 
773 	updateScreen();
774 
775 	if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA || _bytesPerPixel == 2)
776 		return;
777 
778 	int diff = 0, delayInc = 0;
779 	getFadeParams(pal, delay, delayInc, diff);
780 
781 	int delayAcc = 0;
782 	while (!_vm->shouldQuit()) {
783 		delayAcc += delayInc;
784 
785 		int refreshed = fadePalStep(pal, diff);
786 
787 		if (upFunc && upFunc->isValid())
788 			(*upFunc)();
789 		else if (_useHiColorScreen)
790 			updateScreen();
791 		else
792 			_system->updateScreen();
793 
794 		if (!refreshed)
795 			break;
796 
797 		_vm->delay((delayAcc >> 8) * 1000 / 60);
798 		delayAcc &= 0xFF;
799 	}
800 
801 	// In case we should quit we setup the final palette here. This avoids
802 	// ugly palette glitches when quitting while fading. This can for example
803 	// be noticed when quitting while viewing the family album in Kyra3.
804 	if (_vm->shouldQuit()) {
805 		setScreenPalette(pal);
806 	}
807 }
808 
getFadeParams(const Palette & pal,int delay,int & delayInc,int & diff)809 void Screen::getFadeParams(const Palette &pal, int delay, int &delayInc, int &diff) {
810 	uint8 maxDiff = 0;
811 
812 	for (int i = 0; i < pal.getNumColors() * 3; ++i) {
813 		diff = ABS(pal[i] - (*_screenPalette)[i]);
814 		maxDiff = MAX<uint8>(maxDiff, diff);
815 	}
816 
817 	delayInc = (delay << 8) & 0x7FFF;
818 	if (maxDiff != 0)
819 		delayInc /= maxDiff;
820 
821 	delay = delayInc;
822 	for (diff = 1; diff <= maxDiff; ++diff) {
823 		if (delayInc >= 512)
824 			break;
825 		delayInc += delay;
826 	}
827 }
828 
fadePalStep(const Palette & pal,int diff)829 int Screen::fadePalStep(const Palette &pal, int diff) {
830 	_internFadePalette->copy(*_screenPalette);
831 
832 	bool needRefresh = false;
833 
834 	for (int i = 0; i < pal.getNumColors() * 3; ++i) {
835 		int c1 = pal[i];
836 		int c2 = (*_internFadePalette)[i];
837 		if (c1 != c2) {
838 			needRefresh = true;
839 			if (c1 > c2) {
840 				c2 += diff;
841 				if (c1 < c2)
842 					c2 = c1;
843 			}
844 
845 			if (c1 < c2) {
846 				c2 -= diff;
847 				if (c1 > c2)
848 					c2 = c1;
849 			}
850 
851 			(*_internFadePalette)[i] = (uint8)c2;
852 		}
853 	}
854 
855 	if (needRefresh)
856 		setScreenPalette(*_internFadePalette);
857 
858 	return needRefresh ? 1 : 0;
859 }
860 
setPaletteIndex(uint8 index,uint8 red,uint8 green,uint8 blue)861 void Screen::setPaletteIndex(uint8 index, uint8 red, uint8 green, uint8 blue) {
862 	Palette &pal = getPalette(0);
863 
864 	const int offset = index * 3;
865 
866 	if (pal[offset + 0] == red && pal[offset + 1] == green && pal[offset + 2] == blue)
867 		return;
868 
869 	pal[offset + 0] = red;
870 	pal[offset + 1] = green;
871 	pal[offset + 2] = blue;
872 
873 	setScreenPalette(pal);
874 }
875 
getRealPalette(int num,uint8 * dst)876 void Screen::getRealPalette(int num, uint8 *dst) {
877 	const int colors = _use16ColorMode ? 16 : (_isAmiga ? 32 : 256);
878 	const uint8 *palData = getPalette(num).getData();
879 
880 	if (!palData) {
881 		memset(dst, 0, colors * 3);
882 		return;
883 	}
884 
885 	for (int i = 0; i < colors; ++i) {
886 		dst[0] = (palData[0] * 0xFF) / 0x3F;
887 		dst[1] = (palData[1] * 0xFF) / 0x3F;
888 		dst[2] = (palData[2] * 0xFF) / 0x3F;
889 		dst += 3;
890 		palData += 3;
891 	}
892 }
893 
setScreenPalette(const Palette & pal)894 void Screen::setScreenPalette(const Palette &pal) {
895 	uint8 screenPal[256 * 3];
896 	_screenPalette->copy(pal);
897 
898 	for (int i = 0; i < pal.getNumColors(); ++i) {
899 		screenPal[3 * i + 0] = (pal[i * 3 + 0] * 0xFF) / 0x3F;
900 		screenPal[3 * i + 1] = (pal[i * 3 + 1] * 0xFF) / 0x3F;
901 		screenPal[3 * i + 2] = (pal[i * 3 + 2] * 0xFF) / 0x3F;
902 	}
903 
904 	_paletteChanged = true;
905 
906 	if (_useHiColorScreen) {
907 		if (_16bitPalette)
908 			memcpy(_16bitPalette, pal.getData(), 512);
909 
910 		// Generate 16bit palette for the 8bit/16 bit conversion in scale2x()
911 		if (_16bitConversionPalette) {
912 			Graphics::PixelFormat pixelFormat = _system->getScreenFormat();
913 			for (int i = 0; i < 256; ++i)
914 				_16bitConversionPalette[i] = pixelFormat.RGBToColor(screenPal[i * 3], screenPal[i * 3 + 1], screenPal[i * 3 + 2]);
915 			// The whole Surface has to be converted again after each palette chance
916 			_forceFullUpdate = true;
917 		}
918 		return;
919 	}
920 
921 	_system->getPaletteManager()->setPalette(screenPal, 0, pal.getNumColors());
922 }
923 
transposeScreenOutputY(int yAdd)924 void Screen::transposeScreenOutputY(int yAdd) {
925 	updateScreen();
926 	_yTransOffs = yAdd;
927 }
928 
enableDualPaletteMode(int splitY)929 void Screen::enableDualPaletteMode(int splitY) {
930 	_dualPaletteModeSplitY = splitY;
931 
932 	_forceFullUpdate = true;
933 	_dirtyRects.clear();
934 
935 	// TODO: We might need to reset the mouse cursor
936 
937 	updateScreen();
938 }
939 
disableDualPaletteMode()940 void Screen::disableDualPaletteMode() {
941 	_dualPaletteModeSplitY = 0;
942 	_forceFullUpdate = true;
943 }
944 
copyToPage0(int y,int h,uint8 page,uint8 * seqBuf)945 void Screen::copyToPage0(int y, int h, uint8 page, uint8 *seqBuf) {
946 	assert(y + h <= _screenHeight);
947 	const uint8 *src = getPagePtr(page) + y * SCREEN_W;
948 	uint8 *dstPage = getPagePtr(0) + y * SCREEN_W;
949 	for (int i = 0; i < h; ++i) {
950 		for (int x = 0; x < SCREEN_W; ++x) {
951 			if (seqBuf[x] != src[x]) {
952 				seqBuf[x] = src[x];
953 				dstPage[x] = src[x];
954 			}
955 		}
956 		src += SCREEN_W;
957 		seqBuf += SCREEN_W;
958 		dstPage += SCREEN_W;
959 	}
960 	addDirtyRect(0, y, SCREEN_W, h);
961 	// This would remove the text in the end sequence of
962 	// the (Kyrandia 1) FM-TOWNS version.
963 	// Since this method is just used for the Seqplayer
964 	// this shouldn't be a problem anywhere else, so it's
965 	// safe to disable the call here.
966 	//clearOverlayRect(0, 0, y, SCREEN_W, h);
967 }
968 
copyRegion(int x1,int y1,int x2,int y2,int w,int h,int srcPage,int dstPage,int flags)969 void Screen::copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage, int flags) {
970 	if (x2 < 0) {
971 		if (x2  <= -w)
972 			return;
973 		w += x2;
974 		x1 -= x2;
975 		x2 = 0;
976 	} else if (x2 + w >= SCREEN_W) {
977 		if (x2 > SCREEN_W)
978 			return;
979 		w = SCREEN_W - x2;
980 	}
981 
982 	if (y2 < 0) {
983 		if (y2 <= -h)
984 			return;
985 		h += y2;
986 		y1 -= y2;
987 		y2 = 0;
988 	} else if (y2 + h >= _screenHeight) {
989 		if (y2 > _screenHeight)
990 			return;
991 		h = _screenHeight - y2;
992 	}
993 
994 	const uint8 *src = getPagePtr(srcPage) + y1 * SCREEN_W * _bytesPerPixel + x1 * _bytesPerPixel;
995 	uint8 *dst = getPagePtr(dstPage) + y2 * SCREEN_W * _bytesPerPixel + x2 * _bytesPerPixel;
996 
997 	if (src == dst)
998 		return;
999 
1000 	if (dstPage == 0 || dstPage == 1)
1001 		addDirtyRect(x2, y2, w, h);
1002 
1003 	copyOverlayRegion(x1, y1, x2, y2, w, h, srcPage, dstPage);
1004 
1005 	if (flags & CR_NO_P_CHECK) {
1006 		while (h--) {
1007 			memmove(dst, src, w * _bytesPerPixel);
1008 			src += SCREEN_W * _bytesPerPixel;
1009 			dst += SCREEN_W * _bytesPerPixel;
1010 		}
1011 	} else {
1012 		while (h--) {
1013 			for (int i = 0; i < w; ++i) {
1014 				if (_bytesPerPixel == 2) {
1015 					uint px = *(const uint16*)&src[i << 1];
1016 					if (px)
1017 						*(uint16*)&dst[i << 1] = px;
1018 				} else {
1019 					if (src[i])
1020 						dst[i] = src[i];
1021 				}
1022 			}
1023 			src += SCREEN_W * _bytesPerPixel;
1024 			dst += SCREEN_W * _bytesPerPixel;
1025 		}
1026 	}
1027 }
1028 
copyRegionToBuffer(int pageNum,int x,int y,int w,int h,uint8 * dest)1029 void Screen::copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 *dest) {
1030 	if (y < 0) {
1031 		dest += (-y) * w * _bytesPerPixel;
1032 		h += y;
1033 		y = 0;
1034 	} else if (y + h > _screenHeight) {
1035 		h = _screenHeight - y;
1036 	}
1037 
1038 	int pitch = w;
1039 	if (x < 0) {
1040 		dest += -x * _bytesPerPixel;
1041 		w += x;
1042 		x = 0;
1043 	} else if (x + w > SCREEN_W) {
1044 		w = SCREEN_W - x;
1045 	}
1046 
1047 	if (w < 0 || h < 0)
1048 		return;
1049 
1050 	uint8 *pagePtr = getPagePtr(pageNum);
1051 
1052 	for (int i = y; i < y + h; ++i)
1053 		memcpy(dest + (i - y) * pitch * _bytesPerPixel, pagePtr + i * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel, w * _bytesPerPixel);
1054 }
1055 
copyPage(uint8 srcPage,uint8 dstPage)1056 void Screen::copyPage(uint8 srcPage, uint8 dstPage) {
1057 	uint8 *src = getPagePtr(srcPage);
1058 	uint8 *dst = getPagePtr(dstPage);
1059 	if (src != dst)
1060 		memcpy(dst, src, SCREEN_W * _screenHeight * _bytesPerPixel);
1061 	copyOverlayRegion(0, 0, 0, 0, SCREEN_W, _screenHeight, srcPage, dstPage);
1062 
1063 	if (dstPage == 0 || dstPage == 1)
1064 		_forceFullUpdate = true;
1065 }
1066 
copyBlockToPage(int pageNum,int x,int y,int w,int h,const uint8 * src)1067 void Screen::copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint8 *src) {
1068 	if (y < 0) {
1069 		src += (-y) * w * _bytesPerPixel;
1070 		h += y;
1071 		y = 0;
1072 	} else if (y + h > _screenHeight) {
1073 		h = _screenHeight - y;
1074 	}
1075 
1076 	int pitch = w;
1077 	if (x < 0) {
1078 		src += -x * _bytesPerPixel;
1079 		w += x;
1080 		x = 0;
1081 	} else if (x + w > SCREEN_W) {
1082 		w = SCREEN_W - x;
1083 	}
1084 
1085 	if (w < 0 || h < 0)
1086 		return;
1087 
1088 	uint8 *dst = getPagePtr(pageNum) + y * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel;
1089 
1090 	if (pageNum == 0 || pageNum == 1)
1091 		addDirtyRect(x, y, w, h);
1092 
1093 	clearOverlayRect(pageNum, x, y, w, h);
1094 
1095 	while (h--) {
1096 		memcpy(dst, src, w * _bytesPerPixel);
1097 		dst += SCREEN_W * _bytesPerPixel;
1098 		src += pitch * _bytesPerPixel;
1099 	}
1100 }
1101 
shuffleScreen(int sx,int sy,int w,int h,int srcPage,int dstPage,int ticks,bool transparent)1102 void Screen::shuffleScreen(int sx, int sy, int w, int h, int srcPage, int dstPage, int ticks, bool transparent) {
1103 	assert(sx >= 0 && w <= SCREEN_W);
1104 	int x;
1105 	uint16 x_offs[SCREEN_W];
1106 	for (x = 0; x < SCREEN_W; ++x)
1107 		x_offs[x] = x;
1108 
1109 	for (x = 0; x < w; ++x) {
1110 		int i = _vm->_rnd.getRandomNumber(w - 1);
1111 		SWAP(x_offs[x], x_offs[i]);
1112 	}
1113 
1114 	assert(sy >= 0 && h <= SCREEN_H);
1115 	int y;
1116 	uint8 y_offs[SCREEN_H];
1117 	for (y = 0; y < SCREEN_H; ++y)
1118 		y_offs[y] = y;
1119 
1120 	for (y = 0; y < h; ++y) {
1121 		int i = _vm->_rnd.getRandomNumber(h - 1);
1122 		SWAP(y_offs[y], y_offs[i]);
1123 	}
1124 
1125 	int32 start, now;
1126 	int wait;
1127 	for (y = 0; y < h && !_vm->shouldQuit(); ++y) {
1128 		start = (int32)_system->getMillis();
1129 		int y_cur = y;
1130 		for (x = 0; x < w; ++x) {
1131 			int i = sx + x_offs[x];
1132 			int j = sy + y_offs[y_cur];
1133 			++y_cur;
1134 			if (y_cur >= h)
1135 				y_cur = 0;
1136 
1137 			uint8 color = getPagePixel(srcPage, i, j);
1138 			if (!transparent || color != 0)
1139 				setPagePixel(dstPage, i, j, color);
1140 		}
1141 		// forcing full update for now
1142 		_forceFullUpdate = true;
1143 		updateScreen();
1144 		now = (int32)_system->getMillis();
1145 		wait = ticks * _vm->tickLength() - (now - start);
1146 		if (wait > 0)
1147 			_vm->delay(wait);
1148 	}
1149 
1150 	copyOverlayRegion(sx, sy, sx, sy, w, h, srcPage, dstPage);
1151 
1152 	if (_vm->shouldQuit()) {
1153 		copyRegion(sx, sy, sx, sy, w, h, srcPage, dstPage);
1154 		_system->updateScreen();
1155 	}
1156 }
1157 
fillRect(int x1,int y1,int x2,int y2,uint8 color,int pageNum,bool xored)1158 void Screen::fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum, bool xored) {
1159 	assert(x2 < SCREEN_W && y2 < _screenHeight);
1160 	uint16 color16 = 0;
1161 	if (pageNum == -1)
1162 		pageNum = _curPage;
1163 
1164 	uint8 *dst = getPagePtr(pageNum) + y1 * SCREEN_W * _bytesPerPixel + x1 * _bytesPerPixel;
1165 
1166 	if (pageNum == 0 || pageNum == 1)
1167 		addDirtyRect(x1, y1, x2-x1+1, y2-y1+1);
1168 
1169 	clearOverlayRect(pageNum, x1, y1, x2-x1+1, y2-y1+1);
1170 
1171 	if (_4bitPixelPacking) {
1172 		color &= 0x0F;
1173 		color |= (color << 4);
1174 	} else if (_renderMode == Common::kRenderCGA) {
1175 		color &= 0x03;
1176 	} else if (_use16ColorMode || (_renderMode == Common::kRenderEGA && !_useHiResEGADithering)) {
1177 		color &= 0x0F;
1178 	} else if (_bytesPerPixel == 2)
1179 		color16 = shade16bitColor(_16bitPalette[color]);
1180 
1181 	if (xored) {
1182 		// no 16 bit support for this (unneeded)
1183 		for (; y1 <= y2; ++y1) {
1184 			for (int x = x1; x <= x2; ++x)
1185 				dst[x] ^= color;
1186 			dst += SCREEN_W;
1187 		}
1188 	} else {
1189 		for (; y1 <= y2; ++y1) {
1190 			if (_bytesPerPixel == 2) {
1191 				uint16 *ptr = (uint16*)dst;
1192 				for (int i = 0; i < x2 - x1 + 1; i++)
1193 					*ptr++ = color16;
1194 			} else {
1195 				memset(dst, color, x2 - x1 + 1);
1196 			}
1197 			dst += SCREEN_W * _bytesPerPixel;
1198 		}
1199 	}
1200 }
1201 
drawBox(int x1,int y1,int x2,int y2,int color)1202 void Screen::drawBox(int x1, int y1, int x2, int y2, int color) {
1203 	drawClippedLine(x1, y1, x2, y1, color);
1204 	drawClippedLine(x1, y1, x1, y2, color);
1205 	drawClippedLine(x2, y1, x2, y2, color);
1206 	drawClippedLine(x1, y2, x2, y2, color);
1207 }
1208 
drawShadedBox(int x1,int y1,int x2,int y2,int color1,int color2)1209 void Screen::drawShadedBox(int x1, int y1, int x2, int y2, int color1, int color2) {
1210 	assert(x1 >= 0 && y1 >= 0);
1211 	fillRect(x1, y1, x2, y1 + 1, color1);
1212 	fillRect(x2 - 1, y1, x2, y2, color1);
1213 
1214 	drawClippedLine(x1, y1, x1, y2, color2);
1215 	drawClippedLine(x1 + 1, y1 + 1, x1 + 1, y2 - 1, color2);
1216 	drawClippedLine(x1, y2 - 1, x2 - 1, y2 - 1, color2);
1217 	drawClippedLine(x1, y2, x2, y2, color2);
1218 }
1219 
drawClippedLine(int x1,int y1,int x2,int y2,int color)1220 void Screen::drawClippedLine(int x1, int y1, int x2, int y2, int color) {
1221 	if (x1 < 0)
1222 		x1 = 0;
1223 	else if (x1 > 319)
1224 		x1 = 319;
1225 
1226 	if (x2 < 0)
1227 		x2 = 0;
1228 	else if (x2 > 319)
1229 		x2 = 319;
1230 
1231 	if (y1 < 0)
1232 		y1 = 0;
1233 	else if (y1 > 199)
1234 		y1 = 199;
1235 
1236 	if (y2 < 0)
1237 		y2 = 0;
1238 	else if (y2 > 199)
1239 		y2 = 199;
1240 
1241 	if (x1 == x2)
1242 		if (y1 > y2)
1243 			drawLine(true, x1, y2, y1 - y2 + 1, color);
1244 		else
1245 			drawLine(true, x1, y1, y2 - y1 + 1, color);
1246 	else
1247 		if (x1 > x2)
1248 			drawLine(false, x2, y1, x1 - x2 + 1, color);
1249 		else
1250 			drawLine(false, x1, y1, x2 - x1 + 1, color);
1251 }
1252 
drawLine(bool vertical,int x,int y,int length,int color)1253 void Screen::drawLine(bool vertical, int x, int y, int length, int color) {
1254 	uint8 *ptr = getPagePtr(_curPage) + y * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel;
1255 
1256 	if (_4bitPixelPacking) {
1257 		color &= 0x0F;
1258 		color |= (color << 4);
1259 	} else if (_renderMode == Common::kRenderCGA) {
1260 		color &= 0x03;
1261 	} else if (_use16ColorMode || (_renderMode == Common::kRenderEGA && !_useHiResEGADithering)) {
1262 		color &= 0x0F;
1263 	} else if (_bytesPerPixel == 2)
1264 		color = shade16bitColor(_16bitPalette[color]);
1265 
1266 	if (vertical) {
1267 		assert((y + length) <= SCREEN_H);
1268 		int currLine = 0;
1269 		while (currLine < length) {
1270 			if (_bytesPerPixel == 2)
1271 				*(uint16*)ptr = color;
1272 			else
1273 				*ptr = color;
1274 			ptr += SCREEN_W * _bytesPerPixel;
1275 			currLine++;
1276 		}
1277 	} else {
1278 		assert((x + length) <= SCREEN_W);
1279 		if (_bytesPerPixel == 2) {
1280 			for (int i = 0; i < length; i++) {
1281 				*(uint16*)ptr = color;
1282 				ptr += 2;
1283 			}
1284 		} else {
1285 			memset(ptr, color, length);
1286 		}
1287 	}
1288 
1289 	if (_curPage == 0 || _curPage == 1)
1290 		addDirtyRect(x, y, (vertical) ? 1 : length, (vertical) ? length : 1);
1291 
1292 	clearOverlayRect(_curPage, x, y, (vertical) ? 1 : length, (vertical) ? length : 1);
1293 }
1294 
setAnimBlockPtr(int size)1295 void Screen::setAnimBlockPtr(int size) {
1296 	delete[] _animBlockPtr;
1297 	_animBlockPtr = new uint8[size];
1298 	assert(_animBlockPtr);
1299 	memset(_animBlockPtr, 0, size);
1300 	_animBlockSize = size;
1301 }
1302 
setTextColor(const uint8 * cmap8,int a,int b)1303 void Screen::setTextColor(const uint8 *cmap8, int a, int b) {
1304 	memcpy(&_textColorsMap[a], cmap8, (b - a + 1));
1305 	// We need to update the color tables of all fonts, we
1306 	// setup so far here.
1307 	for (int i = 0; i < FID_NUM; ++i) {
1308 		if (_fonts[i])
1309 			_fonts[i]->setColorMap(_textColorsMap);
1310 	}
1311 }
1312 
setTextColor16bit(const uint16 * cmap16)1313 void Screen::setTextColor16bit(const uint16 *cmap16) {
1314 	assert(cmap16);
1315 	_textColorsMap16bit[0] = cmap16[0];
1316 	_textColorsMap16bit[1] = cmap16[1];
1317 	// We need to update the color tables of all fonts, we
1318 	// setup so far here.
1319 	for (int i = 0; i < FID_NUM; ++i) {
1320 		if (_fonts[i])
1321 			_fonts[i]->set16bitColorMap(_textColorsMap16bit);
1322 	}
1323 }
1324 
setFontStyles(FontId fontId,int styles)1325 int Screen::setFontStyles(FontId fontId, int styles) {
1326 	assert(_fonts[fontId]);
1327 	SWAP(_fontStyles, styles);
1328 	_fonts[fontId]->setStyles(_fontStyles);
1329 	return styles;
1330 }
1331 
loadFont(FontId fontId,const char * filename)1332 bool Screen::loadFont(FontId fontId, const char *filename) {
1333 	if (fontId == FID_SJIS_FNT) {
1334 		warning("Trying to replace system SJIS font");
1335 		return true;
1336 	}
1337 
1338 	Font *&fnt = _fonts[fontId];
1339 	int temp = 0;
1340 
1341 	if (!fnt) {
1342 		if (_vm->game() == GI_KYRA1 && _isAmiga)
1343 			fnt = new AMIGAFont();
1344 		else if (_vm->game() == GI_KYRA3 && fontId == FID_CHINESE_FNT)
1345 			fnt = new Big5Font(_vm->staticres()->loadRawData(k3FontData, temp), SCREEN_W);
1346 		else
1347 			fnt = new DOSFont();
1348 
1349 		assert(fnt);
1350 	}
1351 
1352 	Common::SeekableReadStream *file = _vm->resource()->createReadStream(filename);
1353 	if (!file)
1354 		error("Font file '%s' is missing", filename);
1355 
1356 	bool ret = fnt->load(*file);
1357 	fnt->setColorMap(_textColorsMap);
1358 	delete file;
1359 	return ret;
1360 }
1361 
setFont(FontId fontId)1362 Screen::FontId Screen::setFont(FontId fontId) {
1363 	FontId prev = _currentFont;
1364 	_currentFont = fontId;
1365 
1366 	assert(_fonts[_currentFont]);
1367 	return prev;
1368 }
1369 
getFontHeight() const1370 int Screen::getFontHeight() const {
1371 	return _fonts[_currentFont]->getHeight();
1372 }
1373 
getFontWidth() const1374 int Screen::getFontWidth() const {
1375 	return _fonts[_currentFont]->getWidth();
1376 }
1377 
getCharWidth(uint16 c) const1378 int Screen::getCharWidth(uint16 c) const {
1379 	const int width = _fonts[_currentFont]->getCharWidth(c);
1380 	return width + (_isSegaCD || _fonts[_currentFont]->getType() == Font::kASCII ? _charSpacing : 0);
1381 }
1382 
getCharHeight(uint16 c) const1383 int Screen::getCharHeight(uint16 c) const {
1384 	return _fonts[_currentFont]->getCharHeight(c);
1385 }
1386 
getTextWidth(const char * str,bool nextWordOnly)1387 int Screen::getTextWidth(const char *str, bool nextWordOnly) {
1388 	int curLineLen = 0;
1389 	int maxLineLen = 0;
1390 
1391 	FontId curFont = _currentFont;
1392 	Font::Type curType = _fonts[curFont]->getType();
1393 
1394 	while (1) {
1395 		if (_sjisMixedFontMode && curType == Font::kASCII)
1396 			setFont((*str & 0x80) ? ((_vm->game() == GI_EOB2 && curFont == FID_6_FNT) ? FID_SJIS_SMALL_FNT : FID_SJIS_FNT) : curFont);
1397 
1398 		uint c = fetchChar(str);
1399 
1400 		if (c == 0 || (nextWordOnly && (c == 2 || c == 6 || c == 13 || c == 32 || c == 0x4081))) {
1401 			break;
1402 		} else if (c == '\r') {
1403 			if (curLineLen > maxLineLen)
1404 				maxLineLen = curLineLen;
1405 			else
1406 				curLineLen = 0;
1407 		} else {
1408 			curLineLen += getCharWidth(c);
1409 		}
1410 	}
1411 
1412 	return MAX(curLineLen, maxLineLen);
1413 }
1414 
getNumberOfCharacters(const char * str)1415 int Screen::getNumberOfCharacters(const char *str) {
1416 	int res = 0;
1417 	while (fetchChar(str))
1418 		++res;
1419 	return res;
1420 }
1421 
printText(const char * str,int x,int y,uint8 color1,uint8 color2,int pitch)1422 void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2, int pitch) {
1423 	uint16 cmap16[2];
1424 	if (_16bitPalette) {
1425 		cmap16[0] = color2 ? shade16bitColor(_16bitPalette[color2]) : 0xFFFF;
1426 		cmap16[1] = _16bitPalette[color1];
1427 		setTextColor16bit(cmap16);
1428 	}
1429 
1430 	uint8 cmap8[2];
1431 	cmap8[0] = color2;
1432 	cmap8[1] = color1;
1433 	setTextColor(cmap8, 0, 1);
1434 
1435 	FontId curFont = _currentFont;
1436 	Font::Type curType = _fonts[curFont]->getType();
1437 
1438 	if (x < 0)
1439 		x = 0;
1440 	else if (x >= SCREEN_W)
1441 		return;
1442 
1443 	int x_start = x;
1444 	if (y < 0)
1445 		y = 0;
1446 	else if (y >= _screenHeight)
1447 		return;
1448 
1449 	int charHeight = 0;
1450 	bool enableWordWrap = _isSegaCD && _vm->gameFlags().lang != Common::JA_JPN;
1451 
1452 	while (1) {
1453 		if (_sjisMixedFontMode && curType == Font::kASCII)
1454 			setFont((*str & 0x80) ? ((_vm->game() == GI_EOB2 && curFont == FID_6_FNT) ? FID_SJIS_SMALL_FNT : FID_SJIS_FNT) : curFont);
1455 
1456 		uint c = fetchChar(str);
1457 		charHeight = MAX<int>(charHeight, getCharHeight(c));
1458 
1459 		if (c == 0) {
1460 			break;
1461 		} else if (c == '\r') {
1462 			x = x_start;
1463 			y += (charHeight + _lineSpacing);
1464 		} else {
1465 			int charWidth = getCharWidth(c);
1466 			int needSpace = enableWordWrap ? getTextWidth(str, true) + charWidth : charWidth;
1467 			if (x + needSpace > _textMarginRight) {
1468 				x = x_start;
1469 				y += (charHeight + _lineSpacing);
1470 				if (enableWordWrap) {
1471 					// skip space at beginning of the line
1472 					c = fetchChar(str);
1473 					if (c == 0)
1474 						return;
1475 					charWidth = getCharWidth(c);
1476 				}
1477 				if (y >= _screenHeight)
1478 					break;
1479 			}
1480 
1481 			drawChar(c, x, y, pitch);
1482 			x += charWidth;
1483 		}
1484 	}
1485 }
1486 
fetchChar(const char * & s) const1487 uint16 Screen::fetchChar(const char *&s) const {
1488 	if (_fonts[_currentFont]->getType() == Font::kASCII)
1489 		return (uint8)*s++;
1490 
1491 	uint16 ch = (uint8)*s++;
1492 
1493 	if ((_fonts[_currentFont]->getType() == Font::kSJIS && (ch <= 0x7F || (ch >= 0xA1 && ch <= 0xDF))) || (_fonts[_currentFont]->getType() == Font::kBIG5 && ch < 0x7F))
1494 		return ch;
1495 
1496 	ch |= (uint8)(*s++) << 8;
1497 	return ch;
1498 }
1499 
drawChar(uint16 c,int x,int y,int pitch)1500 void Screen::drawChar(uint16 c, int x, int y, int pitch) {
1501 	Font *fnt = _fonts[_currentFont];
1502 	assert(fnt);
1503 
1504 	const bool useOverlay = fnt->usesOverlay();
1505 	const int charWidth = fnt->getCharWidth(c);
1506 	const int charHeight = fnt->getHeight();
1507 
1508 	if (x < 0 || y < 0)
1509 		return;
1510 	if (x + charWidth > SCREEN_W || y + charHeight > _screenHeight)
1511 		return;
1512 
1513 	if (_isSegaCD) {
1514 		fnt->drawChar(c, _textRenderBuffer + (((y >> 3) * pitch + (x >> 3)) << 5) + ((y & 7) << 2) + ((x & 7) >> 1), pitch, x & 7, y & 7);
1515 	} else if (useOverlay) {
1516 		uint8 *destPage = getOverlayPtr(_curPage);
1517 		if (!destPage) {
1518 			warning("trying to draw SJIS char on unsupported page %d", _curPage);
1519 			return;
1520 		}
1521 
1522 		int bpp = (_currentFont == Screen::FID_SJIS_LARGE_FNT) ? 2 : 1;
1523 		destPage += (y * 2) * 640 * bpp + (x * 2 * bpp);
1524 
1525 		fnt->drawChar(c, destPage, 640, bpp);
1526 	} else {
1527 		fnt->drawChar(c, getPagePtr(_curPage) + y * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel, SCREEN_W, _bytesPerPixel);
1528 	}
1529 
1530 	if (!_isSegaCD && (_curPage == 0 || _curPage == 1))
1531 		addDirtyRect(x, y, charWidth, charHeight);
1532 }
1533 
drawShape(uint8 pageNum,const uint8 * shapeData,int x,int y,int sd,int flags,...)1534 void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...) {
1535 	if (!shapeData)
1536 		return;
1537 
1538 	if (_vm->gameFlags().useAltShapeHeader)
1539 		shapeData += 2;
1540 
1541 	if (*shapeData & 1)
1542 		flags |= 0x400;
1543 
1544 	va_list args;
1545 	va_start(args, flags);
1546 
1547 	static const int drawShapeVar2[] = {
1548 		1, 3, 2, 5, 4, 3, 2, 1
1549 	};
1550 
1551 	_dsShapeFadingTable = 0;
1552 	_dsShapeFadingLevel = 0;
1553 	_dsColorTable = 0;
1554 	_dsTransparencyTable1 = 0;
1555 	_dsTransparencyTable2 = 0;
1556 	_dsBackgroundFadingTable = 0;
1557 	_dsDrawLayer = 0;
1558 
1559 	if (flags & DSF_CUSTOM_PALETTE) {
1560 		_dsColorTable = va_arg(args, uint8 *);
1561 	}
1562 
1563 	if (flags & DSF_SHAPE_FADING) {
1564 		_dsShapeFadingTable = va_arg(args, uint8 *);
1565 		_dsShapeFadingLevel = va_arg(args, int);
1566 		if (!_dsShapeFadingLevel)
1567 			flags &= ~DSF_SHAPE_FADING;
1568 	}
1569 
1570 	if (flags & DSF_TRANSPARENCY) {
1571 		_dsTransparencyTable1 = va_arg(args, uint8 *);
1572 		_dsTransparencyTable2 = va_arg(args, uint8 *);
1573 	}
1574 
1575 	if (flags & 0x200) {
1576 		_drawShapeVar1 = (_drawShapeVar1 + 1) & 0x7;
1577 		_drawShapeVar3 = drawShapeVar2[_drawShapeVar1];
1578 		_drawShapeVar4 = 0;
1579 		_drawShapeVar5 = 256;
1580 	}
1581 
1582 	if (flags & 0x4000)
1583 		_drawShapeVar5 = va_arg(args, int);
1584 
1585 	if (flags & 0x800)
1586 		_dsDrawLayer = va_arg(args, int);
1587 
1588 	if (flags & DSF_SCALE) {
1589 		_dsScaleW = va_arg(args, int);
1590 		_dsScaleH = va_arg(args, int);
1591 	} else {
1592 		_dsScaleW = 0x100;
1593 		_dsScaleH = 0x100;
1594 	}
1595 
1596 	if ((flags & DSF_BACKGROUND_FADING) && _vm->game() != GI_KYRA1)
1597 		_dsBackgroundFadingTable = va_arg(args, uint8 *);
1598 
1599 	va_end(args);
1600 
1601 	static const DsMarginSkipFunc dsMarginFunc[] = {
1602 		&Screen::drawShapeMarginNoScaleUpwind,
1603 		&Screen::drawShapeMarginNoScaleDownwind,
1604 		&Screen::drawShapeMarginNoScaleUpwind,
1605 		&Screen::drawShapeMarginNoScaleDownwind,
1606 		&Screen::drawShapeMarginScaleUpwind,
1607 		&Screen::drawShapeMarginScaleDownwind,
1608 		&Screen::drawShapeMarginScaleUpwind,
1609 		&Screen::drawShapeMarginScaleDownwind
1610 	};
1611 
1612 	static const DsMarginSkipFunc dsSkipFunc[] = {
1613 		&Screen::drawShapeMarginNoScaleUpwind,
1614 		&Screen::drawShapeMarginNoScaleDownwind,
1615 		&Screen::drawShapeMarginNoScaleUpwind,
1616 		&Screen::drawShapeMarginNoScaleDownwind,
1617 		&Screen::drawShapeSkipScaleUpwind,
1618 		&Screen::drawShapeSkipScaleDownwind,
1619 		&Screen::drawShapeSkipScaleUpwind,
1620 		&Screen::drawShapeSkipScaleDownwind
1621 	};
1622 
1623 	static const DsLineFunc dsLineFunc[] = {
1624 		&Screen::drawShapeProcessLineNoScaleUpwind,
1625 		&Screen::drawShapeProcessLineNoScaleDownwind,
1626 		&Screen::drawShapeProcessLineNoScaleUpwind,
1627 		&Screen::drawShapeProcessLineNoScaleDownwind,
1628 		&Screen::drawShapeProcessLineScaleUpwind,
1629 		&Screen::drawShapeProcessLineScaleDownwind,
1630 		&Screen::drawShapeProcessLineScaleUpwind,
1631 		&Screen::drawShapeProcessLineScaleDownwind
1632 	};
1633 
1634 	static const DsPlotFunc dsPlotFunc[] = {
1635 		&Screen::drawShapePlotType0,		// used by Kyra 1 + 2
1636 		&Screen::drawShapePlotType1,		// used by Kyra 3
1637 		0,
1638 		&Screen::drawShapePlotType3_7,		// used by Kyra 3 (shadow)
1639 		&Screen::drawShapePlotType4,		// used by Kyra 1, 2 + 3
1640 		&Screen::drawShapePlotType5,		// used by Kyra 1
1641 		&Screen::drawShapePlotType6,		// used by Kyra 1 (invisibility)
1642 		&Screen::drawShapePlotType3_7,		// used by Kyra 1 (invisibility)
1643 		&Screen::drawShapePlotType8,		// used by Kyra 2
1644 		&Screen::drawShapePlotType9,		// used by Kyra 1 + 3
1645 		0,
1646 		&Screen::drawShapePlotType11_15,	// used by Kyra 1 (invisibility) + Kyra 3 (shadow)
1647 		&Screen::drawShapePlotType12,		// used by Kyra 2
1648 		&Screen::drawShapePlotType13,		// used by Kyra 1
1649 		&Screen::drawShapePlotType14,		// used by Kyra 1 (invisibility)
1650 		&Screen::drawShapePlotType11_15,	// used by Kyra 1 (invisibility)
1651 		&Screen::drawShapePlotType16,		// used by LoL PC-98/16 Colors (teleporters),
1652 		0, 0, 0,
1653 		&Screen::drawShapePlotType20,		// used by LoL (heal spell effect)
1654 		&Screen::drawShapePlotType21,		// used by LoL (white tower spirits)
1655 		0, 0, 0, 0,	0, 0, 0, 0, 0, 0,
1656 		0,
1657 		&Screen::drawShapePlotType33,		// used by LoL (blood spots on the floor)
1658 		0, 0, 0,
1659 		&Screen::drawShapePlotType37,		// used by LoL (monsters)
1660 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1661 		&Screen::drawShapePlotType48,		// used by LoL (slime spots on the floor)
1662 		0, 0, 0,
1663 		&Screen::drawShapePlotType52,		// used by LoL (projectiles)
1664 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1665 		0
1666 	};
1667 
1668 	int scaleCounterV = 0;
1669 
1670 	const int drawFunc = flags & 0x0F;
1671 	_dsProcessMargin = dsMarginFunc[drawFunc];
1672 	_dsScaleSkip = dsSkipFunc[drawFunc];
1673 	_dsProcessLine = dsLineFunc[drawFunc];
1674 
1675 	const int ppc = (flags >> 8) & 0x3F;
1676 	_dsPlot = dsPlotFunc[ppc];
1677 	DsPlotFunc dsPlot2 = dsPlotFunc[ppc], dsPlot3 = dsPlotFunc[ppc];
1678 	if (flags & 0x800)
1679 		dsPlot3 = dsPlotFunc[((flags >> 8) & 0xF7) & 0x3F];
1680 
1681 	if (!_dsPlot || !dsPlot2 || !dsPlot3) {
1682 		if (!dsPlot2)
1683 			warning("Missing drawShape plotting method type %d", ppc);
1684 		if (dsPlot3 != dsPlot2 && !dsPlot3)
1685 			warning("Missing drawShape plotting method type %d", (((flags >> 8) & 0xF7) & 0x3F));
1686 		return;
1687 	}
1688 
1689 	int curY = y;
1690 	const uint8 *src = shapeData;
1691 	uint8 *dst = _dsDstPage = getPagePtr(pageNum);
1692 
1693 	const ScreenDim *dsDim = getScreenDim(sd);
1694 	dst += (dsDim->sx << 3);
1695 
1696 	if (!(flags & 0x10))
1697 		x -= (dsDim->sx << 3);
1698 
1699 	int x2 = (dsDim->w << 3);
1700 	int y1 = dsDim->sy;
1701 	if (flags & 0x10)
1702 		y += y1;
1703 
1704 	int y2 = y1 + dsDim->h;
1705 
1706 	uint16 shapeFlags = READ_LE_UINT16(src); src += 2;
1707 
1708 	int shapeHeight = *src++;
1709 	uint16 shapeWidth = READ_LE_UINT16(src); src += 2;
1710 
1711 	int shpWidthScaled1 = shapeWidth;
1712 	int shpWidthScaled2 = shapeWidth;
1713 
1714 	if (flags & DSF_SCALE) {
1715 		shapeHeight = (shapeHeight * _dsScaleH) >> 8;
1716 		shpWidthScaled1 = shpWidthScaled2 = (shapeWidth * _dsScaleW) >> 8;
1717 
1718 		if (!shapeHeight || !shpWidthScaled1)
1719 			return;
1720 	}
1721 
1722 	if (flags & DSF_CENTER) {
1723 		x -= (shpWidthScaled1 >> 1);
1724 		y -= (shapeHeight >> 1);
1725 	}
1726 
1727 	src += 3;
1728 
1729 	uint16 frameSize = READ_LE_UINT16(src); src += 2;
1730 
1731 	int colorTableColors = ((_vm->game() != GI_KYRA1) && (shapeFlags & 4)) ? *src++ : 16;
1732 
1733 	if (!(flags & 0x8000) && (shapeFlags & 1))
1734 		_dsColorTable = src;
1735 
1736 	if (flags & 0x400)
1737 		src += colorTableColors;
1738 
1739 	if (!(shapeFlags & 2)) {
1740 		decodeFrame4(src, _animBlockPtr, frameSize);
1741 		src = _animBlockPtr;
1742 	}
1743 
1744 	int t = (flags & 2) ? y2 - y - shapeHeight : y - y1;
1745 
1746 	if (t < 0) {
1747 		shapeHeight += t;
1748 		if (shapeHeight <= 0) {
1749 			return;
1750 		}
1751 
1752 		t *= -1;
1753 		const uint8 *srcBackUp = 0;
1754 
1755 		do {
1756 			_dsOffscreenScaleVal1 = 0;
1757 			srcBackUp = src;
1758 			_dsTmpWidth = shapeWidth;
1759 
1760 			int cnt = shapeWidth;
1761 			(this->*_dsScaleSkip)(dst, src, cnt);
1762 
1763 			scaleCounterV += _dsScaleH;
1764 
1765 			if (scaleCounterV & 0xFF00) {
1766 				uint8 r = scaleCounterV >> 8;
1767 				scaleCounterV &= 0xFF;
1768 				t -= r;
1769 			}
1770 		} while (!(scaleCounterV & 0xFF00) && (t > 0));
1771 
1772 		if (t < 0) {
1773 			src = srcBackUp;
1774 			scaleCounterV += (-t << 8);
1775 		}
1776 
1777 		if (!(flags & 2))
1778 			y = y1;
1779 	}
1780 
1781 	t = (flags & 2) ? y + shapeHeight - y1 : y2 - y;
1782 	if (t <= 0)
1783 		return;
1784 
1785 	if (t < shapeHeight) {
1786 		shapeHeight = t;
1787 		if (flags & 2)
1788 			y = y1;
1789 	}
1790 
1791 	_dsOffscreenLeft = 0;
1792 	if (x < 0) {
1793 		shpWidthScaled1 += x;
1794 		_dsOffscreenLeft = -x;
1795 		if (_dsOffscreenLeft >= shpWidthScaled2)
1796 			return;
1797 		x = 0;
1798 	}
1799 
1800 	_dsOffscreenRight = 0;
1801 	t = x2 - x;
1802 
1803 	if (t <= 0)
1804 		return;
1805 
1806 	if (t < shpWidthScaled1) {
1807 		shpWidthScaled1 = t;
1808 		_dsOffscreenRight = shpWidthScaled2 - _dsOffscreenLeft - shpWidthScaled1;
1809 	}
1810 
1811 	int dsPitch = 320;
1812 	int ty = y;
1813 
1814 	if (flags & 2) {
1815 		dsPitch *= -1;
1816 		ty = ty - 1 + shapeHeight;
1817 	}
1818 
1819 	if (flags & DSF_X_FLIPPED) {
1820 		SWAP(_dsOffscreenLeft, _dsOffscreenRight);
1821 		dst += (shpWidthScaled1 - 1);
1822 	}
1823 
1824 	dst += (320 * ty + x);
1825 
1826 	if (flags & DSF_SCALE) {
1827 		_dsOffscreenRight = 0;
1828 		_dsOffscreenScaleVal2 = _dsOffscreenLeft;
1829 		_dsOffscreenLeft <<= 8;
1830 		_dsOffscreenScaleVal1 = (_dsOffscreenLeft % _dsScaleW) * -1;
1831 		_dsOffscreenLeft /= _dsScaleW;
1832 	}
1833 
1834 	if (shapeHeight <= 0 || shpWidthScaled1 <= 0)
1835 		return;
1836 
1837 	if (pageNum == 0 || pageNum == 1)
1838 		addDirtyRect(x, y, shpWidthScaled1, shapeHeight);
1839 	clearOverlayRect(pageNum, x, y, shpWidthScaled1, shapeHeight);
1840 
1841 	uint8 *d = dst;
1842 
1843 	bool normalPlot = true;
1844 	while (true) {
1845 		while (!(scaleCounterV & 0xFF00)) {
1846 			scaleCounterV += _dsScaleH;
1847 			if (!(scaleCounterV & 0xFF00)) {
1848 				_dsTmpWidth = shapeWidth;
1849 				int cnt = shapeWidth;
1850 				(this->*_dsScaleSkip)(d, src, cnt);
1851 			}
1852 		}
1853 
1854 		const uint8 *b_src = src;
1855 
1856 		do {
1857 			src = b_src;
1858 			_dsTmpWidth = shapeWidth;
1859 			int cnt = _dsOffscreenLeft;
1860 			int scaleState = (this->*_dsProcessMargin)(d, src, cnt);
1861 
1862 			if (_dsTmpWidth) {
1863 				cnt += shpWidthScaled1;
1864 				if (cnt > 0) {
1865 					if (flags & 0x800)
1866 						normalPlot = (curY > _maskMinY && curY < _maskMaxY);
1867 					_dsPlot = normalPlot ? dsPlot2 : dsPlot3;
1868 					(this->*_dsProcessLine)(d, src, cnt, scaleState);
1869 				}
1870 				cnt += _dsOffscreenRight;
1871 				if (cnt)
1872 					(this->*_dsScaleSkip)(d, src, cnt);
1873 			}
1874 			dst += dsPitch;
1875 			d = dst;
1876 			++curY;
1877 
1878 			if (!--shapeHeight)
1879 				return;
1880 
1881 			scaleCounterV -= 0x100;
1882 		} while (scaleCounterV & 0xFF00);
1883 	}
1884 }
1885 
drawShapeMarginNoScaleUpwind(uint8 * & dst,const uint8 * & src,int & cnt)1886 int Screen::drawShapeMarginNoScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt) {
1887 	while (cnt-- > 0) {
1888 		if (*src++)
1889 			continue;
1890 		cnt = cnt + 1 - (*src++);
1891 	}
1892 
1893 	cnt++;
1894 	dst -= cnt;
1895 	return 0;
1896 }
1897 
drawShapeMarginNoScaleDownwind(uint8 * & dst,const uint8 * & src,int & cnt)1898 int Screen::drawShapeMarginNoScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt) {
1899 	while (cnt-- > 0) {
1900 		if (*src++)
1901 			continue;
1902 		cnt = cnt + 1 - (*src++);
1903 	}
1904 
1905 	cnt++;
1906 	dst += cnt;
1907 	return 0;
1908 }
1909 
drawShapeMarginScaleUpwind(uint8 * & dst,const uint8 * & src,int & cnt)1910 int Screen::drawShapeMarginScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt) {
1911 	_dsTmpWidth -= cnt;
1912 
1913 	while (cnt > 0) {
1914 		--cnt;
1915 		if (*src++)
1916 			continue;
1917 
1918 		cnt = cnt + 1 - (*src++);
1919 	}
1920 
1921 	if (!cnt)
1922 		return _dsOffscreenScaleVal1;
1923 
1924 	_dsTmpWidth += cnt;
1925 
1926 	int i = (_dsOffscreenLeft - cnt) * _dsScaleW;
1927 	int res = i & 0xFF;
1928 	i >>= 8;
1929 	i -= _dsOffscreenScaleVal2;
1930 	dst += i;
1931 	cnt = -i;
1932 
1933 	return res;
1934 }
1935 
drawShapeMarginScaleDownwind(uint8 * & dst,const uint8 * & src,int & cnt)1936 int Screen::drawShapeMarginScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt) {
1937 	_dsTmpWidth -= cnt;
1938 
1939 	while (cnt > 0) {
1940 		--cnt;
1941 		if (*src++)
1942 			continue;
1943 
1944 		cnt = cnt + 1 - (*src++);
1945 	}
1946 
1947 	if (!cnt)
1948 		return _dsOffscreenScaleVal1;
1949 
1950 	_dsTmpWidth += cnt;
1951 
1952 	int i = (_dsOffscreenLeft - cnt) * _dsScaleW;
1953 	int res = i & 0xFF;
1954 	i >>= 8;
1955 	i -= _dsOffscreenScaleVal2;
1956 	dst -= i;
1957 	cnt = -i;
1958 
1959 	return res;
1960 }
1961 
drawShapeSkipScaleUpwind(uint8 * & dst,const uint8 * & src,int & cnt)1962 int Screen::drawShapeSkipScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt) {
1963 	cnt = _dsTmpWidth;
1964 
1965 	if (cnt <= 0)
1966 		return 0;
1967 
1968 	do {
1969 		--cnt;
1970 		if (*src++)
1971 			continue;
1972 		cnt = cnt + 1 - (*src++);
1973 	} while (cnt > 0);
1974 
1975 	return 0;
1976 }
1977 
drawShapeSkipScaleDownwind(uint8 * & dst,const uint8 * & src,int & cnt)1978 int Screen::drawShapeSkipScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt) {
1979 	cnt = _dsTmpWidth;
1980 	bool found = false;
1981 
1982 	if (cnt == 0)
1983 		return 0;
1984 
1985 	do {
1986 		--cnt;
1987 		if (*src++)
1988 			continue;
1989 		found = true;
1990 		cnt = cnt + 1 - (*src++);
1991 	} while (cnt > 0);
1992 
1993 	return found ? 0 : _dsOffscreenScaleVal1;
1994 }
1995 
drawShapeProcessLineNoScaleUpwind(uint8 * & dst,const uint8 * & src,int & cnt,int16)1996 void Screen::drawShapeProcessLineNoScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt, int16) {
1997 	do {
1998 		uint8 c = *src++;
1999 		if (c) {
2000 			uint8 *d = dst++;
2001 			(this->*_dsPlot)(d, c);
2002 			cnt--;
2003 		} else {
2004 			c = *src++;
2005 			dst += c;
2006 			cnt -= c;
2007 		}
2008 	} while (cnt > 0);
2009 }
2010 
drawShapeProcessLineNoScaleDownwind(uint8 * & dst,const uint8 * & src,int & cnt,int16)2011 void Screen::drawShapeProcessLineNoScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt, int16) {
2012 	do {
2013 		uint8 c = *src++;
2014 		if (c) {
2015 			uint8 *d = dst--;
2016 			(this->*_dsPlot)(d, c);
2017 			cnt--;
2018 		} else {
2019 			c = *src++;
2020 			dst -= c;
2021 			cnt -= c;
2022 		}
2023 	} while (cnt > 0);
2024 }
2025 
drawShapeProcessLineScaleUpwind(uint8 * & dst,const uint8 * & src,int & cnt,int16 scaleState)2026 void Screen::drawShapeProcessLineScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt, int16 scaleState) {
2027 	int c = 0;
2028 
2029 	do {
2030 		if ((scaleState & 0x8000) || !(scaleState & 0xFF00)) {
2031 			c = *src++;
2032 			_dsTmpWidth--;
2033 			if (c) {
2034 				scaleState += _dsScaleW;
2035 			} else {
2036 				_dsTmpWidth++;
2037 				c = *src++;
2038 				_dsTmpWidth -= c;
2039 				int r = c * _dsScaleW + scaleState;
2040 				dst += (r >> 8);
2041 				cnt -= (r >> 8);
2042 				scaleState = r & 0xFF;
2043 			}
2044 		} else if (scaleState) {
2045 			(this->*_dsPlot)(dst++, c);
2046 			scaleState -= 0x100;
2047 			cnt--;
2048 		}
2049 	} while (cnt > 0);
2050 
2051 	cnt = -1;
2052 }
2053 
drawShapeProcessLineScaleDownwind(uint8 * & dst,const uint8 * & src,int & cnt,int16 scaleState)2054 void Screen::drawShapeProcessLineScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt, int16 scaleState) {
2055 	int c = 0;
2056 
2057 	do {
2058 		if ((scaleState & 0x8000) || !(scaleState & 0xFF00)) {
2059 			c = *src++;
2060 			_dsTmpWidth--;
2061 			if (c) {
2062 				scaleState += _dsScaleW;
2063 			} else {
2064 				_dsTmpWidth++;
2065 				c = *src++;
2066 				_dsTmpWidth -= c;
2067 				int r = c * _dsScaleW + scaleState;
2068 				dst -= (r >> 8);
2069 				cnt -= (r >> 8);
2070 				scaleState = r & 0xFF;
2071 			}
2072 		} else {
2073 			(this->*_dsPlot)(dst--, c);
2074 			scaleState -= 0x100;
2075 			cnt--;
2076 		}
2077 	} while (cnt > 0);
2078 
2079 	cnt = -1;
2080 }
2081 
drawShapePlotType0(uint8 * dst,uint8 cmd)2082 void Screen::drawShapePlotType0(uint8 *dst, uint8 cmd) {
2083 	*dst = cmd;
2084 }
2085 
drawShapePlotType1(uint8 * dst,uint8 cmd)2086 void Screen::drawShapePlotType1(uint8 *dst, uint8 cmd) {
2087 	for (int i = 0; i < _dsShapeFadingLevel; ++i)
2088 		cmd = _dsShapeFadingTable[cmd];
2089 
2090 	if (cmd)
2091 		*dst = cmd;
2092 }
2093 
drawShapePlotType3_7(uint8 * dst,uint8 cmd)2094 void Screen::drawShapePlotType3_7(uint8 *dst, uint8 cmd) {
2095 	cmd = *dst;
2096 	for (int i = 0; i < _dsShapeFadingLevel; ++i)
2097 		cmd = _dsShapeFadingTable[cmd];
2098 
2099 	if (cmd)
2100 		*dst = cmd;
2101 }
2102 
drawShapePlotType4(uint8 * dst,uint8 cmd)2103 void Screen::drawShapePlotType4(uint8 *dst, uint8 cmd) {
2104 	*dst = _dsColorTable[cmd];
2105 }
2106 
drawShapePlotType5(uint8 * dst,uint8 cmd)2107 void Screen::drawShapePlotType5(uint8 *dst, uint8 cmd) {
2108 	cmd = _dsColorTable[cmd];
2109 	for (int i = 0; i < _dsShapeFadingLevel; ++i)
2110 		cmd = _dsShapeFadingTable[cmd];
2111 
2112 	if (cmd)
2113 		*dst = cmd;
2114 }
2115 
drawShapePlotType6(uint8 * dst,uint8 cmd)2116 void Screen::drawShapePlotType6(uint8 *dst, uint8 cmd) {
2117 	int t = _drawShapeVar4 + _drawShapeVar5;
2118 	if (t & 0xFF00) {
2119 		cmd = dst[_drawShapeVar3];
2120 		t &= 0xFF;
2121 	} else {
2122 		cmd = _dsColorTable[cmd];
2123 	}
2124 
2125 	_drawShapeVar4 = t;
2126 	*dst = cmd;
2127 }
2128 
drawShapePlotType8(uint8 * dst,uint8 cmd)2129 void Screen::drawShapePlotType8(uint8 *dst, uint8 cmd) {
2130 	uint32 relOffs = dst - _dsDstPage;
2131 	int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
2132 	if (_dsDrawLayer < t)
2133 		cmd = _shapePages[1][relOffs];
2134 
2135 	*dst = cmd;
2136 }
2137 
drawShapePlotType9(uint8 * dst,uint8 cmd)2138 void Screen::drawShapePlotType9(uint8 *dst, uint8 cmd) {
2139 	uint32 relOffs = dst - _dsDstPage;
2140 	int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
2141 	if (_dsDrawLayer < t) {
2142 		cmd = _shapePages[1][relOffs];
2143 	} else {
2144 		for (int i = 0; i < _dsShapeFadingLevel; ++i)
2145 			cmd = _dsShapeFadingTable[cmd];
2146 	}
2147 
2148 	if (cmd)
2149 		*dst = cmd;
2150 }
2151 
drawShapePlotType11_15(uint8 * dst,uint8 cmd)2152 void Screen::drawShapePlotType11_15(uint8 *dst, uint8 cmd) {
2153 	uint32 relOffs = dst - _dsDstPage;
2154 	int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
2155 
2156 	if (_dsDrawLayer < t) {
2157 		cmd = _shapePages[1][relOffs];
2158 	} else {
2159 		cmd = *dst;
2160 		for (int i = 0; i < _dsShapeFadingLevel; ++i)
2161 			cmd = _dsShapeFadingTable[cmd];
2162 	}
2163 
2164 	if (cmd)
2165 		*dst = cmd;
2166 }
2167 
drawShapePlotType12(uint8 * dst,uint8 cmd)2168 void Screen::drawShapePlotType12(uint8 *dst, uint8 cmd) {
2169 	uint32 relOffs = dst - _dsDstPage;
2170 	int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
2171 	if (_dsDrawLayer < t) {
2172 		cmd = _shapePages[1][relOffs];
2173 	} else {
2174 		cmd = _dsColorTable[cmd];
2175 	}
2176 
2177 	*dst = cmd;
2178 }
2179 
drawShapePlotType13(uint8 * dst,uint8 cmd)2180 void Screen::drawShapePlotType13(uint8 *dst, uint8 cmd) {
2181 	uint32 relOffs = dst - _dsDstPage;
2182 	int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
2183 	if (_dsDrawLayer < t) {
2184 		cmd = _shapePages[1][relOffs];
2185 	} else {
2186 		cmd = _dsColorTable[cmd];
2187 		for (int i = 0; i < _dsShapeFadingLevel; ++i)
2188 			cmd = _dsShapeFadingTable[cmd];
2189 	}
2190 
2191 	if (cmd)
2192 		*dst = cmd;
2193 }
2194 
drawShapePlotType14(uint8 * dst,uint8 cmd)2195 void Screen::drawShapePlotType14(uint8 *dst, uint8 cmd) {
2196 	uint32 relOffs = dst - _dsDstPage;
2197 	int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
2198 	if (_dsDrawLayer < t) {
2199 		cmd = _shapePages[1][relOffs];
2200 	} else {
2201 		t = _drawShapeVar4 + _drawShapeVar5;
2202 		if (t & 0xFF00) {
2203 			cmd = dst[_drawShapeVar3];
2204 			t &= 0xFF;
2205 		} else {
2206 			cmd = _dsColorTable[cmd];
2207 		}
2208 	}
2209 
2210 	_drawShapeVar4 = t;
2211 	*dst = cmd;
2212 }
2213 
drawShapePlotType16(uint8 * dst,uint8 cmd)2214 void Screen::drawShapePlotType16(uint8 *dst, uint8 cmd) {
2215 	uint8 tOffs = _dsTransparencyTable1[cmd];
2216 	if (!(tOffs & 0x80))
2217 		cmd = _dsTransparencyTable2[tOffs << 8 | *dst];
2218 	*dst = cmd;
2219 }
2220 
drawShapePlotType20(uint8 * dst,uint8 cmd)2221 void Screen::drawShapePlotType20(uint8 *dst, uint8 cmd) {
2222 	cmd = _dsColorTable[cmd];
2223 	uint8 tOffs = _dsTransparencyTable1[cmd];
2224 	if (!(tOffs & 0x80))
2225 		cmd = _dsTransparencyTable2[tOffs << 8 | *dst];
2226 
2227 	*dst = cmd;
2228 }
2229 
drawShapePlotType21(uint8 * dst,uint8 cmd)2230 void Screen::drawShapePlotType21(uint8 *dst, uint8 cmd) {
2231 	cmd = _dsColorTable[cmd];
2232 	uint8 tOffs = _dsTransparencyTable1[cmd];
2233 	if (!(tOffs & 0x80))
2234 		cmd = _dsTransparencyTable2[tOffs << 8 | *dst];
2235 
2236 	for (int i = 0; i < _dsShapeFadingLevel; ++i)
2237 		cmd = _dsShapeFadingTable[cmd];
2238 
2239 	if (cmd)
2240 		*dst = cmd;
2241 }
2242 
drawShapePlotType33(uint8 * dst,uint8 cmd)2243 void Screen::drawShapePlotType33(uint8 *dst, uint8 cmd) {
2244 	if (cmd == 255) {
2245 		*dst = _dsBackgroundFadingTable[*dst];
2246 	} else {
2247 		for (int i = 0; i < _dsShapeFadingLevel; ++i)
2248 			cmd = _dsShapeFadingTable[cmd];
2249 		if (cmd)
2250 			*dst = cmd;
2251 	}
2252 }
2253 
drawShapePlotType37(uint8 * dst,uint8 cmd)2254 void Screen::drawShapePlotType37(uint8 *dst, uint8 cmd) {
2255 	cmd = _dsColorTable[cmd];
2256 
2257 	if (cmd == 255) {
2258 		cmd = _dsBackgroundFadingTable[*dst];
2259 	} else {
2260 		for (int i = 0; i < _dsShapeFadingLevel; ++i)
2261 			cmd = _dsShapeFadingTable[cmd];
2262 	}
2263 
2264 	if (cmd)
2265 		*dst = cmd;
2266 }
2267 
drawShapePlotType48(uint8 * dst,uint8 cmd)2268 void Screen::drawShapePlotType48(uint8 *dst, uint8 cmd) {
2269 	uint8 offs = _dsTransparencyTable1[cmd];
2270 	if (!(offs & 0x80))
2271 		cmd = _dsTransparencyTable2[(offs << 8) | *dst];
2272 	*dst = cmd;
2273 }
2274 
drawShapePlotType52(uint8 * dst,uint8 cmd)2275 void Screen::drawShapePlotType52(uint8 *dst, uint8 cmd) {
2276 	cmd = _dsColorTable[cmd];
2277 	uint8 offs = _dsTransparencyTable1[cmd];
2278 
2279 	if (!(offs & 0x80))
2280 		cmd = _dsTransparencyTable2[(offs << 8) | *dst];
2281 
2282 	*dst = cmd;
2283 }
2284 
decodeFrame1(const uint8 * src,uint8 * dst,uint32 size)2285 void Screen::decodeFrame1(const uint8 *src, uint8 *dst, uint32 size) {
2286 	const uint8 *dstEnd = dst + size;
2287 
2288 	struct Pattern {
2289 		const uint8 *pos;
2290 		uint16 len;
2291 	};
2292 
2293 	Pattern *patterns = new Pattern[3840];
2294 	uint16 numPatterns = 0;
2295 	uint8 nib = 0;
2296 
2297 	uint16 code = decodeEGAGetCode(src, nib);
2298 	uint8 last = code & 0xFF;
2299 
2300 	uint8 *dstPrev = dst;
2301 	uint16 count = 1;
2302 	uint16 countPrev = 1;
2303 
2304 	*dst++ = last;
2305 
2306 	while (dst < dstEnd) {
2307 		code = decodeEGAGetCode(src, nib);
2308 		uint8 cmd = code >> 8;
2309 
2310 		if (cmd--) {
2311 			code = (cmd << 8) | (code & 0xFF);
2312 			uint8 *tmpDst = dst;
2313 
2314 			if (code < numPatterns) {
2315 				const uint8 *tmpSrc = patterns[code].pos;
2316 				countPrev = patterns[code].len;
2317 				last = *tmpSrc;
2318 				for (int i = 0; i < countPrev; i++)
2319 					*dst++ = *tmpSrc++;
2320 
2321 			} else {
2322 				const uint8 *tmpSrc = dstPrev;
2323 				count = countPrev;
2324 				for (int i = 0; i < countPrev; i++)
2325 					*dst++ = *tmpSrc++;
2326 				*dst++ = last;
2327 				countPrev++;
2328 			}
2329 
2330 			if (numPatterns < 3840) {
2331 				patterns[numPatterns].pos = dstPrev;
2332 				patterns[numPatterns++].len = ++count;
2333 			}
2334 
2335 			dstPrev = tmpDst;
2336 			count = countPrev;
2337 
2338 		} else {
2339 			*dst++ = last = (code & 0xFF);
2340 
2341 			if (numPatterns < 3840) {
2342 				patterns[numPatterns].pos = dstPrev;
2343 				patterns[numPatterns++].len = ++count;
2344 			}
2345 
2346 			dstPrev = dst - 1;
2347 			count = 1;
2348 			countPrev = 1;
2349 		}
2350 	}
2351 	delete[] patterns;
2352 }
2353 
decodeEGAGetCode(const uint8 * & pos,uint8 & nib)2354 uint16 Screen::decodeEGAGetCode(const uint8 *&pos, uint8 &nib) {
2355 	uint16 res = READ_BE_UINT16(pos++);
2356 	if ((++nib) & 1) {
2357 		res >>= 4;
2358 	} else {
2359 		pos++;
2360 		res &= 0xFFF;
2361 	}
2362 	return res;
2363 }
2364 
decodeFrame3(const uint8 * src,uint8 * dst,uint32 size,bool isAmiga)2365 void Screen::decodeFrame3(const uint8 *src, uint8 *dst, uint32 size, bool isAmiga) {
2366 	const uint8 *dstEnd = dst + size;
2367 	while (dst < dstEnd) {
2368 		int8 code = *src++;
2369 		if (code == 0) {
2370 			uint16 sz = isAmiga ? READ_LE_UINT16(src) : READ_BE_UINT16(src);
2371 			src += 2;
2372 			memset(dst, *src++, sz);
2373 			dst += sz;
2374 		} else if (code < 0) {
2375 			memset(dst, *src++, -code);
2376 			dst -= code;
2377 		} else {
2378 			memcpy(dst, src, code);
2379 			dst += code;
2380 			src += code;
2381 		}
2382 	}
2383 }
2384 
decodeFrame4(const uint8 * src,uint8 * dst,uint32 dstSize)2385 uint Screen::decodeFrame4(const uint8 *src, uint8 *dst, uint32 dstSize) {
2386 	uint8 *dstOrig = dst;
2387 	uint8 *dstEnd = dst + dstSize;
2388 	while (1) {
2389 		int count = dstEnd - dst;
2390 		if (count == 0)
2391 			break;
2392 
2393 		uint8 code = *src++;
2394 		if (!(code & 0x80)) { // 8th bit isn't set
2395 			int len = MIN(count, (code >> 4) + 3); //upper half of code is the length
2396 			int offs = ((code & 0xF) << 8) | *src++; //lower half of code as byte 2 of offset.
2397 			const uint8 *dstOffs = dst - offs;
2398 			while (len--)
2399 				*dst++ = *dstOffs++;
2400 		} else if (code & 0x40) { // 7th bit is set
2401 			int len = (code & 0x3F) + 3;
2402 			if (code == 0xFE) {
2403 				len = READ_LE_UINT16(src); src += 2;
2404 				if (len > count)
2405 					len = count;
2406 
2407 				memset(dst, *src++, len); dst += len;
2408 			} else {
2409 				if (code == 0xFF) {
2410 					len = READ_LE_UINT16(src);
2411 					src += 2;
2412 				}
2413 
2414 				int offs = READ_LE_UINT16(src); src += 2;
2415 				if (len > count)
2416 					len = count;
2417 
2418 				const uint8 *dstOffs = dstOrig + offs;
2419 				while (len--)
2420 					*dst++ = *dstOffs++;
2421 			}
2422 		} else if (code != 0x80) { // not just the 8th bit set.
2423 			//Copy some bytes from source to dest.
2424 			int len = MIN(count, code & 0x3F);
2425 			while (len--)
2426 				*dst++ = *src++;
2427 		} else {
2428 			break;
2429 		}
2430 	}
2431 	return dst - dstOrig;
2432 }
2433 
decodeFrameDelta(uint8 * dst,const uint8 * src,bool noXor)2434 void Screen::decodeFrameDelta(uint8 *dst, const uint8 *src, bool noXor) {
2435 	if (noXor)
2436 		wrapped_decodeFrameDelta<true>(dst, src);
2437 	else
2438 		wrapped_decodeFrameDelta<false>(dst, src);
2439 }
2440 
2441 template<bool noXor>
wrapped_decodeFrameDelta(uint8 * dst,const uint8 * src)2442 void Screen::wrapped_decodeFrameDelta(uint8 *dst, const uint8 *src) {
2443 	while (1) {
2444 		uint8 code = *src++;
2445 		if (code == 0) {
2446 			uint8 len = *src++;
2447 			code = *src++;
2448 			while (len--) {
2449 				if (noXor)
2450 					*dst++ = code;
2451 				else
2452 					*dst++ ^= code;
2453 			}
2454 		} else if (code & 0x80) {
2455 			code -= 0x80;
2456 			if (code != 0) {
2457 				dst += code;
2458 			} else {
2459 				uint16 subcode = READ_LE_UINT16(src); src += 2;
2460 				if (subcode == 0) {
2461 					break;
2462 				} else if (subcode & 0x8000) {
2463 					subcode -= 0x8000;
2464 					if (subcode & 0x4000) {
2465 						uint16 len = subcode - 0x4000;
2466 						code = *src++;
2467 						while (len--) {
2468 							if (noXor)
2469 								*dst++ = code;
2470 							else
2471 								*dst++ ^= code;
2472 						}
2473 					} else {
2474 						while (subcode--) {
2475 							if (noXor)
2476 								*dst++ = *src++;
2477 							else
2478 								*dst++ ^= *src++;
2479 						}
2480 					}
2481 				} else {
2482 					dst += subcode;
2483 				}
2484 			}
2485 		} else {
2486 			while (code--) {
2487 				if (noXor)
2488 					*dst++ = *src++;
2489 				else
2490 					*dst++ ^= *src++;
2491 			}
2492 		}
2493 	}
2494 }
2495 
decodeFrameDeltaPage(uint8 * dst,const uint8 * src,int pitch,bool noXor)2496 void Screen::decodeFrameDeltaPage(uint8 *dst, const uint8 *src, int pitch, bool noXor) {
2497 	if (noXor)
2498 		wrapped_decodeFrameDeltaPage<true>(dst, src, pitch);
2499 	else
2500 		wrapped_decodeFrameDeltaPage<false>(dst, src, pitch);
2501 }
2502 
convertAmigaGfx(uint8 * data,int w,int h,int depth,bool wsa,int bytesPerPlane)2503 void Screen::convertAmigaGfx(uint8 *data, int w, int h, int depth, bool wsa, int bytesPerPlane) {
2504 	const int planeWidth = (bytesPerPlane == -1) ? (w + 7) / 8 : bytesPerPlane;
2505 	const int planeSize = planeWidth * h;
2506 	const uint imageSize = planeSize * depth;
2507 
2508 	// Our static buffer which holds the plane data. We need this
2509 	// because the "data" pointer is both source and destination pointer.
2510 	// The buffer has enough space to fit the AMIGA MSC files, which are
2511 	// the biggest graphics files found in the AMIGA version.
2512 	static uint8 temp[40320];
2513 	assert(imageSize <= sizeof(temp));
2514 
2515 	// WSA files store their graphics data in a little different format, than
2516 	// the usual AMIGA graphics format used in BitMaps. Thus we need to do
2517 	// some special handling for them here. Means we convert them into
2518 	// the usual format.
2519 	//
2520 	// TODO: We might think of moving this conversion into the WSAMovieAmiga
2521 	// class.
2522 	if (wsa) {
2523 		const byte *src = data;
2524 		for (int y = 0; y < h; ++y) {
2525 			for (int x = 0; x < planeWidth; ++x)
2526 				for (int i = 0; i < depth; ++i)
2527 					temp[y * planeWidth + x + planeSize * i] = *src++;
2528 		}
2529 	} else {
2530 		memcpy(temp, data, imageSize);
2531 	}
2532 
2533 	for (int y = 0; y < h; ++y) {
2534 		for (int x = 0; x < w; ++x) {
2535 			const int bytePos = x / 8 + y * planeWidth;
2536 			const int bitPos = 7 - (x & 7); // x & 7 == x % 8
2537 
2538 			byte col = 0;
2539 
2540 			for (int i = 0; i < depth; ++i)
2541 				col |= ((temp[bytePos + planeSize * i] >> bitPos) & 1) << i;
2542 
2543 			*data++ = col;
2544 		}
2545 	}
2546 }
2547 
convertAmigaMsc(uint8 * data)2548 void Screen::convertAmigaMsc(uint8 *data) {
2549 	// MSC files are always 320x144, thus we can safely assume
2550 	// this to be correct. Also they contain 7 planes instead
2551 	// of the normal 5 planes, which is used in 32 color mode.
2552 	// The need for 7 planes can be explained, because the MSC
2553 	// files have 6 bits for the layer number (bits 1 to 6)
2554 	// and one bit for the "blocked" flag (bit 0), and every
2555 	// plane contains one bit per pixel.
2556 	convertAmigaGfx(data, 320, 144, 7);
2557 
2558 	// We need to do some post conversion, since
2559 	// the AMIGA MSC format is different from the DOS
2560 	// one we use internally for our code.That is even
2561 	// after converting it from the AMIGA plane based
2562 	// approach to one byte per pixel approach.
2563 	for (int i = 0; i < 320 * 144; ++i) {
2564 		// The lowest bit indicates, whether the position
2565 		// is walkable or not. If the bit is set, the
2566 		// position is walkable, elsewise it is blocked.
2567 		if (data[i] & 1)
2568 			data[i] &= 0xFE;
2569 		else
2570 			data[i] |= 0x80;
2571 
2572 		// The graphics layer for the pixel is saved
2573 		// in the following format:
2574 		// The highest bit set indicates the number of
2575 		// the graphics layer. We count the first
2576 		// bit as 0 here, thus we need to add one,
2577 		// to get the correct number.
2578 		//
2579 		// Funnily since the first bit (bit 0) is
2580 		// resevered for testing whether the position
2581 		// is walkable or not, there is no possibility
2582 		// for layer 1 to be present.
2583 		int layer = 0;
2584 		for (int k = 0; k < 7; ++k)
2585 			if (data[i] & (1 << k))
2586 				layer = k + 1;
2587 
2588 		data[i] &= 0x80;
2589 		data[i] |= layer;
2590 	}
2591 }
2592 
2593 template<bool noXor>
wrapped_decodeFrameDeltaPage(uint8 * dst,const uint8 * src,int pitch)2594 void Screen::wrapped_decodeFrameDeltaPage(uint8 *dst, const uint8 *src, int pitch) {
2595 	int count = 0;
2596 	uint8 *dstNext = dst;
2597 	while (1) {
2598 		uint8 code = *src++;
2599 		if (code == 0) {
2600 			uint8 len = *src++;
2601 			code = *src++;
2602 			while (len--) {
2603 				if (noXor)
2604 					*dst++ = code;
2605 				else
2606 					*dst++ ^= code;
2607 
2608 				if (++count == pitch) {
2609 					count = 0;
2610 					dstNext += SCREEN_W;
2611 					dst = dstNext;
2612 				}
2613 			}
2614 		} else if (code & 0x80) {
2615 			code -= 0x80;
2616 			if (code != 0) {
2617 				dst += code;
2618 
2619 				count += code;
2620 				while (count >= pitch) {
2621 					count -= pitch;
2622 					dstNext += SCREEN_W;
2623 					dst = dstNext + count;
2624 				}
2625 			} else {
2626 				uint16 subcode = READ_LE_UINT16(src); src += 2;
2627 				if (subcode == 0) {
2628 					break;
2629 				} else if (subcode & 0x8000) {
2630 					subcode -= 0x8000;
2631 					if (subcode & 0x4000) {
2632 						uint16 len = subcode - 0x4000;
2633 						code = *src++;
2634 						while (len--) {
2635 							if (noXor)
2636 								*dst++ = code;
2637 							else
2638 								*dst++ ^= code;
2639 
2640 							if (++count == pitch) {
2641 								count = 0;
2642 								dstNext += SCREEN_W;
2643 								dst = dstNext;
2644 							}
2645 						}
2646 					} else {
2647 						while (subcode--) {
2648 							if (noXor)
2649 								*dst++ = *src++;
2650 							else
2651 								*dst++ ^= *src++;
2652 
2653 							if (++count == pitch) {
2654 								count = 0;
2655 								dstNext += SCREEN_W;
2656 								dst = dstNext;
2657 							}
2658 						}
2659 					}
2660 				} else {
2661 					dst += subcode;
2662 
2663 					count += subcode;
2664 					while (count >= pitch) {
2665 						count -= pitch;
2666 						dstNext += SCREEN_W;
2667 						dst = dstNext + count;
2668 					}
2669 
2670 				}
2671 			}
2672 		} else {
2673 			while (code--) {
2674 				if (noXor)
2675 					*dst++ = *src++;
2676 				else
2677 					*dst++ ^= *src++;
2678 
2679 				if (++count == pitch) {
2680 					count = 0;
2681 					dstNext += SCREEN_W;
2682 					dst = dstNext;
2683 				}
2684 			}
2685 		}
2686 	}
2687 }
2688 
encodeShape(int x,int y,int w,int h,int flags)2689 uint8 *Screen::encodeShape(int x, int y, int w, int h, int flags) {
2690 	uint8 *srcPtr = &_pagePtrs[_curPage][y * SCREEN_W + x];
2691 	int16 shapeSize = 0;
2692 	uint8 *tmp = srcPtr;
2693 	int xpos = w;
2694 
2695 	for (int i = h; i > 0; --i) {
2696 		uint8 *start = tmp;
2697 		shapeSize += w;
2698 		xpos = w;
2699 		while (xpos) {
2700 			uint8 value = *tmp++;
2701 			--xpos;
2702 
2703 			if (!value) {
2704 				shapeSize += 2;
2705 				int16 curX = xpos;
2706 				bool skip = false;
2707 
2708 				while (xpos) {
2709 					value = *tmp++;
2710 					--xpos;
2711 
2712 					if (value) {
2713 						skip = true;
2714 						break;
2715 					}
2716 				}
2717 
2718 				if (!skip)
2719 					++curX;
2720 
2721 				curX -= xpos;
2722 				shapeSize -= curX;
2723 
2724 				while (curX > 0xFF) {
2725 					curX -= 0xFF;
2726 					shapeSize += 2;
2727 				}
2728 			}
2729 		}
2730 
2731 		tmp = start + SCREEN_W;
2732 	}
2733 
2734 	int16 shapeSize2 = shapeSize;
2735 	if (_vm->gameFlags().useAltShapeHeader)
2736 		shapeSize += 12;
2737 	else
2738 		shapeSize += 10;
2739 
2740 	if (flags & 1)
2741 		shapeSize += 16;
2742 
2743 	uint8 table[274];
2744 	int tableIndex = 0;
2745 
2746 	uint8 *newShape = 0;
2747 	newShape = new uint8[shapeSize+16];
2748 	assert(newShape);
2749 
2750 	byte *dst = newShape;
2751 
2752 	if (_vm->gameFlags().useAltShapeHeader)
2753 		dst += 2;
2754 
2755 	WRITE_LE_UINT16(dst, (flags & 3)); dst += 2;
2756 	*dst = h; dst += 1;
2757 	WRITE_LE_UINT16(dst, w); dst += 2;
2758 	*dst = h; dst += 1;
2759 	WRITE_LE_UINT16(dst, shapeSize); dst += 2;
2760 	WRITE_LE_UINT16(dst, shapeSize2); dst += 2;
2761 
2762 	byte *src = srcPtr;
2763 	if (flags & 1) {
2764 		dst += 16;
2765 		memset(table, 0, sizeof(table));
2766 		tableIndex = 1;
2767 	}
2768 
2769 	for (int ypos = h; ypos > 0; --ypos) {
2770 		uint8 *srcBackUp = src;
2771 		xpos = w;
2772 		while (xpos) {
2773 			uint8 value = *src++;
2774 			if (value) {
2775 				if (flags & 1) {
2776 					if (!table[value]) {
2777 						if (tableIndex == 16) {
2778 							value = 1;
2779 						} else {
2780 							table[0x100+tableIndex] = value;
2781 							table[value] = tableIndex;
2782 							++tableIndex;
2783 							value = table[value];
2784 						}
2785 					} else {
2786 						value = table[value];
2787 					}
2788 				}
2789 				--xpos;
2790 				*dst++ = value;
2791 			} else {
2792 				int16 temp = 1;
2793 				--xpos;
2794 
2795 				while (xpos) {
2796 					if (*src)
2797 						break;
2798 					++src;
2799 					++temp;
2800 					--xpos;
2801 				}
2802 
2803 				while (temp > 0xFF) {
2804 					*dst++ = 0;
2805 					*dst++ = 0xFF;
2806 					temp -= 0xFF;
2807 				}
2808 
2809 				if (temp & 0xFF) {
2810 					*dst++ = 0;
2811 					*dst++ = temp & 0xFF;
2812 				}
2813 			}
2814 		}
2815 		src = srcBackUp + SCREEN_W;
2816 	}
2817 
2818 	if (!(flags & 2)) {
2819 		if (shapeSize > _animBlockSize) {
2820 			dst = newShape;
2821 			if (_vm->gameFlags().useAltShapeHeader)
2822 				dst += 2;
2823 
2824 			flags = READ_LE_UINT16(dst);
2825 			flags |= 2;
2826 			WRITE_LE_UINT16(dst, flags);
2827 		} else {
2828 			src = newShape;
2829 			if (_vm->gameFlags().useAltShapeHeader)
2830 				src += 2;
2831 			if (flags & 1)
2832 				src += 16;
2833 
2834 			src += 10;
2835 			uint8 *shapePtrBackUp = src;
2836 			dst = _animBlockPtr;
2837 			memcpy(dst, src, shapeSize2);
2838 
2839 			int16 size = encodeShapeAndCalculateSize(_animBlockPtr, shapePtrBackUp, shapeSize2);
2840 			if (size > shapeSize2) {
2841 				shapeSize -= shapeSize2 - size;
2842 				uint8 *newShape2 = new uint8[shapeSize];
2843 				assert(newShape2);
2844 				memcpy(newShape2, newShape, shapeSize);
2845 				delete[] newShape;
2846 				newShape = newShape2;
2847 			} else {
2848 				dst = shapePtrBackUp;
2849 				src = _animBlockPtr;
2850 				memcpy(dst, src, shapeSize2);
2851 				dst = newShape;
2852 				if (_vm->gameFlags().useAltShapeHeader)
2853 					dst += 2;
2854 				flags = READ_LE_UINT16(dst);
2855 				flags |= 2;
2856 				WRITE_LE_UINT16(dst, flags);
2857 			}
2858 		}
2859 	}
2860 
2861 	dst = newShape;
2862 	if (_vm->gameFlags().useAltShapeHeader)
2863 		dst += 2;
2864 	WRITE_LE_UINT16((dst + 6), shapeSize);
2865 
2866 	if (flags & 1) {
2867 		dst = newShape + 10;
2868 		if (_vm->gameFlags().useAltShapeHeader)
2869 			dst += 2;
2870 		src = &table[0x100];
2871 		memcpy(dst, src, sizeof(uint8)*16);
2872 	}
2873 
2874 	return newShape;
2875 }
2876 
encodeShapeAndCalculateSize(uint8 * from,uint8 * to,int size_to)2877 int16 Screen::encodeShapeAndCalculateSize(uint8 *from, uint8 *to, int size_to) {
2878 	byte *fromPtrEnd = from + size_to;
2879 	bool skipPixel = true;
2880 	byte *tempPtr = 0;
2881 	byte *toPtr = to;
2882 	byte *fromPtr = from;
2883 	byte *toPtr2 = to;
2884 
2885 	*to++ = 0x81;
2886 	*to++ = *from++;
2887 
2888 	while (from < fromPtrEnd) {
2889 		byte *curToPtr = to;
2890 		to = fromPtr;
2891 		int size = 1;
2892 
2893 		while (true) {
2894 			byte curPixel = *from;
2895 			if (curPixel == *(from+0x40)) {
2896 				byte *toBackUp = to;
2897 				to = from;
2898 
2899 				for (int i = 0; i < (fromPtrEnd - from); ++i) {
2900 					if (*to++ != curPixel)
2901 						break;
2902 				}
2903 				--to;
2904 				uint16 diffSize = (to - from);
2905 				if (diffSize >= 0x41) {
2906 					skipPixel = false;
2907 					from = to;
2908 					to = curToPtr;
2909 					*to++ = 0xFE;
2910 					WRITE_LE_UINT16(to, diffSize); to += 2;
2911 					*to++ = curPixel;
2912 					curToPtr = to;
2913 					to = toBackUp;
2914 					continue;
2915 				} else {
2916 					to = toBackUp;
2917 				}
2918 			}
2919 
2920 			bool breakLoop = false;
2921 			while (true) {
2922 				if ((from - to) == 0) {
2923 					breakLoop = true;
2924 					break;
2925 				}
2926 				for (int i = 0; i < (from - to); ++i) {
2927 					if (*to++ == curPixel)
2928 						break;
2929 				}
2930 				if (*(to-1) == curPixel) {
2931 					if (*(from+size-1) != *(to+size-2))
2932 						continue;
2933 
2934 					byte *fromBackUp = from;
2935 					byte *toBackUp = to;
2936 					--to;
2937 					const int checkSize = fromPtrEnd - from;
2938 					for (int i = 0; i < checkSize; ++i) {
2939 						if (*from++ != *to++)
2940 							break;
2941 					}
2942 					if (*(from - 1) == *(to - 1))
2943 						++to;
2944 					from = fromBackUp;
2945 					int temp = to - toBackUp;
2946 					to = toBackUp;
2947 					if (temp >= size) {
2948 						size = temp;
2949 						tempPtr = toBackUp - 1;
2950 					}
2951 					break;
2952 				} else {
2953 					breakLoop = true;
2954 					break;
2955 				}
2956 			}
2957 
2958 			if (breakLoop)
2959 				break;
2960 		}
2961 
2962 		to = curToPtr;
2963 		if (size > 2) {
2964 			uint16 word = 0;
2965 			if (size <= 0x0A) {
2966 				uint16 diffSize = from - tempPtr;
2967 				if (diffSize <= 0x0FFF) {
2968 					byte highByte = ((diffSize & 0xFF00) >> 8) + (((size & 0xFF) - 3) << 4);
2969 					word = ((diffSize & 0xFF) << 8) | highByte;
2970 					WRITE_LE_UINT16(to, word); to += 2;
2971 					from += size;
2972 					skipPixel = false;
2973 					continue;
2974 				}
2975 			}
2976 
2977 			if (size > 0x40) {
2978 				*to++ = 0xFF;
2979 				WRITE_LE_UINT16(to, size); to += 2;
2980 			} else {
2981 				*to++ = ((size & 0xFF) - 3) | 0xC0;
2982 			}
2983 
2984 			word = tempPtr - fromPtr;
2985 			WRITE_LE_UINT16(to, word); to += 2;
2986 			from += size;
2987 			skipPixel = false;
2988 		} else {
2989 			if (!skipPixel) {
2990 				toPtr2 = to;
2991 				*to++ = 0x80;
2992 			}
2993 
2994 			if (*toPtr2 == 0xBF) {
2995 				toPtr2 = to;
2996 				*to++ = 0x80;
2997 			}
2998 
2999 			++(*toPtr2);
3000 			*to++ = *from++;
3001 			skipPixel = true;
3002 		}
3003 	}
3004 	*to++ = 0x80;
3005 
3006 	return (to - toPtr);
3007 }
3008 
shade16bitColor(uint16 col)3009 uint16 Screen::shade16bitColor(uint16 col) {
3010 	uint8 r = (col & 0x1f);
3011 	uint8 g = (col & 0x3E0) >> 5;
3012 	uint8 b = (col & 0x7C00) >> 10;
3013 
3014 	r = (r > _16bitShadingLevel) ? r - _16bitShadingLevel : 0;
3015 	g = (g > _16bitShadingLevel) ? g - _16bitShadingLevel : 0;
3016 	b = (b > _16bitShadingLevel) ? b - _16bitShadingLevel : 0;
3017 
3018 	return (b << 10) | (g << 5) | r;
3019 }
3020 
hideMouse()3021 void Screen::hideMouse() {
3022 	++_mouseLockCount;
3023 	CursorMan.showMouse(false);
3024 }
3025 
showMouse()3026 void Screen::showMouse() {
3027 	if (_mouseLockCount == 1) {
3028 		CursorMan.showMouse(true);
3029 
3030 		// We need to call OSystem::updateScreen here, else the mouse cursor
3031 		// will only be visible on mouse movment.
3032 		_system->updateScreen();
3033 	}
3034 
3035 	if (_mouseLockCount > 0)
3036 		_mouseLockCount--;
3037 }
3038 
3039 
isMouseVisible() const3040 bool Screen::isMouseVisible() const {
3041 	return _mouseLockCount == 0;
3042 }
3043 
setShapePages(int page1,int page2,int minY,int maxY)3044 void Screen::setShapePages(int page1, int page2, int minY, int maxY) {
3045 	_shapePages[0] = _pagePtrs[page1];
3046 	_shapePages[1] = _pagePtrs[page2];
3047 	_maskMinY = minY;
3048 	_maskMaxY = maxY;
3049 }
3050 
setMouseCursor(int x,int y,const byte * shape)3051 void Screen::setMouseCursor(int x, int y, const byte *shape) {
3052 	if (!shape)
3053 		return;
3054 
3055 	if (_vm->gameFlags().useAltShapeHeader)
3056 		shape += 2;
3057 
3058 	int mouseHeight = *(shape + 2);
3059 	int mouseWidth = (READ_LE_UINT16(shape + 3)) + 2;
3060 
3061 	if (_vm->gameFlags().useAltShapeHeader)
3062 		shape -= 2;
3063 
3064 	if (_vm->gameFlags().useHiRes) {
3065 		x <<= 1;
3066 		y <<= 1;
3067 		mouseWidth <<= 1;
3068 		mouseHeight <<= 1;
3069 	}
3070 
3071 	uint8 *cursor = new uint8[mouseHeight * mouseWidth];
3072 	fillRect(0, 0, mouseWidth, mouseHeight, _cursorColorKey, 8);
3073 	drawShape(8, shape, 0, 0, 0, 0);
3074 
3075 	int xOffset = 0;
3076 
3077 	if (_vm->gameFlags().useHiRes) {
3078 		xOffset = mouseWidth;
3079 		scale2x<uint8, uint16>(getPagePtr(8) + mouseWidth, SCREEN_W, getPagePtr(8), SCREEN_W, mouseWidth, mouseHeight);
3080 		postProcessCursor(getPagePtr(8) + mouseWidth, mouseWidth, mouseHeight, SCREEN_W);
3081 	} else {
3082 		postProcessCursor(getPagePtr(8), mouseWidth, mouseHeight, SCREEN_W);
3083 	}
3084 
3085 	CursorMan.showMouse(false);
3086 	copyRegionToBuffer(8, xOffset, 0, mouseWidth, mouseHeight, cursor);
3087 	CursorMan.replaceCursor(cursor, mouseWidth, mouseHeight, x, y, _cursorColorKey);
3088 	if (isMouseVisible())
3089 		CursorMan.showMouse(true);
3090 	delete[] cursor;
3091 
3092 	// makes sure that the cursor is drawn
3093 	// we do not use Screen::updateScreen here
3094 	// so we can be sure that changes to page 0
3095 	// are NOT updated on the real screen here
3096 	_system->updateScreen();
3097 }
3098 
getPalette(int num)3099 Palette &Screen::getPalette(int num) {
3100 	assert(num >= 0 && (uint)num < _palettes.size());
3101 	return *_palettes[num];
3102 }
3103 
copyPalette(const int dst,const int src)3104 void Screen::copyPalette(const int dst, const int src) {
3105 	getPalette(dst).copy(getPalette(src));
3106 }
3107 
getShapeFlag1(int x,int y)3108 byte Screen::getShapeFlag1(int x, int y) {
3109 	uint8 color = _shapePages[0][y * SCREEN_W + x];
3110 	color &= 0x80;
3111 	color ^= 0x80;
3112 
3113 	if (color & 0x80)
3114 		return 1;
3115 	return 0;
3116 }
3117 
getShapeFlag2(int x,int y)3118 byte Screen::getShapeFlag2(int x, int y) {
3119 	uint8 color = _shapePages[0][y * SCREEN_W + x];
3120 	color &= 0x7F;
3121 	color &= 0x87;
3122 	return color;
3123 }
3124 
getDrawLayer(int x,int y)3125 int Screen::getDrawLayer(int x, int y) {
3126 	int xpos = x - 8;
3127 	int ypos = y - 1;
3128 	int layer = 1;
3129 
3130 	for (int curX = xpos; curX < xpos + 16; ++curX) {
3131 		int tempLayer = getShapeFlag2(curX, ypos);
3132 
3133 		if (layer < tempLayer)
3134 			layer = tempLayer;
3135 
3136 		if (layer >= 7)
3137 			return 7;
3138 	}
3139 	return layer;
3140 }
3141 
getDrawLayer2(int x,int y,int height)3142 int Screen::getDrawLayer2(int x, int y, int height) {
3143 	int xpos = x - 8;
3144 	int ypos = y - 1;
3145 	int layer = 1;
3146 
3147 	for (int useX = xpos; useX < xpos + 16; ++useX) {
3148 		for (int useY = ypos - height; useY < ypos; ++useY) {
3149 			int tempLayer = getShapeFlag2(useX, useY);
3150 
3151 			if (tempLayer > layer)
3152 				layer = tempLayer;
3153 
3154 			if (tempLayer >= 7)
3155 				return 7;
3156 		}
3157 	}
3158 	return layer;
3159 }
3160 
3161 
setNewShapeHeight(uint8 * shape,int height)3162 int Screen::setNewShapeHeight(uint8 *shape, int height) {
3163 	if (_vm->gameFlags().useAltShapeHeader)
3164 		shape += 2;
3165 
3166 	int oldHeight = shape[2];
3167 	shape[2] = height;
3168 	return oldHeight;
3169 }
3170 
resetShapeHeight(uint8 * shape)3171 int Screen::resetShapeHeight(uint8 *shape) {
3172 	if (_vm->gameFlags().useAltShapeHeader)
3173 		shape += 2;
3174 
3175 	int oldHeight = shape[2];
3176 	shape[2] = shape[5];
3177 	return oldHeight;
3178 }
3179 
blockInRegion(int x,int y,int width,int height)3180 void Screen::blockInRegion(int x, int y, int width, int height) {
3181 	assert(_shapePages[0]);
3182 	byte *toPtr = _shapePages[0] + (y * 320 + x);
3183 	for (int i = 0; i < height; ++i) {
3184 		byte *backUpTo = toPtr;
3185 		for (int i2 = 0; i2 < width; ++i2)
3186 			*toPtr++ &= 0x7F;
3187 		toPtr = (backUpTo + 320);
3188 	}
3189 }
3190 
blockOutRegion(int x,int y,int width,int height)3191 void Screen::blockOutRegion(int x, int y, int width, int height) {
3192 	assert(_shapePages[0]);
3193 	byte *toPtr = _shapePages[0] + (y * 320 + x);
3194 	for (int i = 0; i < height; ++i) {
3195 		byte *backUpTo = toPtr;
3196 		for (int i2 = 0; i2 < width; ++i2)
3197 			*toPtr++ |= 0x80;
3198 		toPtr = (backUpTo + 320);
3199 	}
3200 }
3201 
rectClip(int & x,int & y,int w,int h)3202 void Screen::rectClip(int &x, int &y, int w, int h) {
3203 	if (x < 0)
3204 		x = 0;
3205 	else if (x + w >= 320)
3206 		x = 320 - w;
3207 
3208 	if (y < 0)
3209 		y = 0;
3210 	else if (y + h >= 200)
3211 		y = 200 - h;
3212 }
3213 
shakeScreen(int times)3214 void Screen::shakeScreen(int times) {
3215 	static const int8 _shakeParaPC[] = { 32, 0, -4, 32, 0, 0 };
3216 	static const int8 _shakeParaFMTOWNS[] = { 32, 0, -4, 48, 0, 4, 32, -4, 0, 32, 4, 0, 32, 0, 0 };
3217 
3218 	const int8 *shakeData = _shakeParaPC;
3219 	int steps = ARRAYSIZE(_shakeParaPC) / 3;
3220 
3221 	// The FM-TOWNS version has a slightly better shake animation
3222 	// TODO: check PC-98 version
3223 	if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
3224 		shakeData = _shakeParaFMTOWNS;
3225 		steps = ARRAYSIZE(_shakeParaFMTOWNS) / 3;
3226 	}
3227 
3228 	Common::Event event;
3229 
3230 	while (times--) {
3231 		const int8 *data = shakeData;
3232 		for (int i = 0; i < steps; ++i) {
3233 			// The original PC version did not need an artificial delay, but we do or the shake will be
3234 			// too fast to be actually seen.
3235 			uint32 end = _system->getMillis() + data[0];
3236 			_system->setShakePos(data[1], data[2]);
3237 
3238 			for (uint32 now = _system->getMillis(); now < end; ) {
3239 				// Update the event manager to keep smooth mouse pointer movement.
3240 				while (_vm->getEventManager()->pollEvent(event)) {
3241 					if (event.type == Common::EVENT_KEYDOWN) {
3242 						// This is really the only thing that should be handled.
3243 						if (event.kbd.keycode == Common::KEYCODE_q && event.kbd.hasFlags(Common::KBD_CTRL))
3244 							_vm->quitGame();
3245 					}
3246 				}
3247 				_system->updateScreen();
3248 				now = _system->getMillis();
3249 				_system->delayMillis(MIN<uint>(end - now, 10));
3250 			}
3251 			data += 3;
3252 		}
3253 	}
3254 }
3255 
loadBitmap(const char * filename,int tempPage,int dstPage,Palette * pal,bool skip)3256 void Screen::loadBitmap(const char *filename, int tempPage, int dstPage, Palette *pal, bool skip) {
3257 	uint32 fileSize;
3258 	uint8 *srcData = _vm->resource()->fileData(filename, &fileSize);
3259 
3260 	if (!srcData) {
3261 		warning("couldn't load bitmap: '%s'", filename);
3262 		return;
3263 	}
3264 
3265 	if (skip)
3266 		srcData += 4;
3267 
3268 	const char *ext = filename + strlen(filename) - 3;
3269 	uint8 compType = srcData[2];
3270 	uint32 imgSize = (_vm->game() == GI_KYRA2 && !scumm_stricmp(ext, "CMP")) ? READ_LE_UINT16(srcData) : READ_LE_UINT32(srcData + 4);
3271 	uint16 palSize = READ_LE_UINT16(srcData + 8);
3272 
3273 	if (pal && palSize)
3274 		loadPalette(srcData + 10, *pal, palSize);
3275 
3276 	uint8 *srcPtr = srcData + 10 + palSize;
3277 	uint8 *dstData = getPagePtr(dstPage);
3278 	memset(dstData, 0, _screenPageSize);
3279 	if (dstPage == 0 || tempPage == 0)
3280 		_forceFullUpdate = true;
3281 
3282 	switch (compType) {
3283 	case 0:
3284 		memcpy(dstData, srcPtr, imgSize);
3285 		break;
3286 	case 1:
3287 		Screen::decodeFrame1(srcPtr, dstData, imgSize);
3288 		break;
3289 	case 3:
3290 		Screen::decodeFrame3(srcPtr, dstData, imgSize, _isAmiga);
3291 		break;
3292 	case 4:
3293 		Screen::decodeFrame4(srcPtr, dstData, imgSize);
3294 		break;
3295 	default:
3296 		error("Unhandled bitmap compression %d", compType);
3297 	}
3298 
3299 	if (skip)
3300 		srcData -= 4;
3301 
3302 	delete[] srcData;
3303 }
3304 
loadPalette(const char * filename,Palette & pal)3305 bool Screen::loadPalette(const char *filename, Palette &pal) {
3306 	if (_renderMode == Common::kRenderCGA)
3307 		return true;
3308 
3309 	Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename);
3310 
3311 	if (!stream)
3312 		return false;
3313 
3314 	debugC(3, kDebugLevelScreen, "Screen::loadPalette('%s', %p)", filename, (const void *)&pal);
3315 
3316 	const int maxCols = _16bitPalette ? 256 : pal.getNumColors();
3317 	int numCols = 0;
3318 
3319 	if (_isAmiga) {
3320 		numCols = stream->size() / Palette::kAmigaBytesPerColor;
3321 		pal.loadAmigaPalette(*stream, 0, MIN(maxCols, numCols));
3322 	} else if (_vm->gameFlags().platform == Common::kPlatformPC98 && _use16ColorMode) {
3323 		numCols = stream->size() / Palette::kPC98BytesPerColor;
3324 		pal.loadPC98Palette(*stream, 0, MIN(maxCols, numCols));
3325 	} else if (_renderMode == Common::kRenderEGA) {
3326 		numCols = stream->size();
3327 		// There aren't any 16 color EGA palette files. So this shouldn't ever get triggered.
3328 		assert (numCols != 16);
3329 		numCols /= Palette::kVGABytesPerColor;
3330 		pal.loadVGAPalette(*stream, 0, numCols);
3331 	} else {
3332 		if (_bytesPerPixel == 2) {
3333 			numCols = stream->size() / 2;
3334 			pal.loadHiColorPalette(*stream, 0, numCols);
3335 		} else if (!_16bitPalette) {
3336 			numCols = stream->size() / Palette::kVGABytesPerColor;
3337 			pal.loadVGAPalette(*stream, 0, MIN(maxCols, numCols));
3338 		} else {
3339 			error("Screen::loadPalette(): Failed to load file '%s' with invalid size %d in HiColor mode", filename, (int)stream->size());
3340 		}
3341 	}
3342 
3343 	if (numCols > maxCols)
3344 		warning("Palette file '%s' includes %d colors, but the target palette only support %d colors", filename, numCols, maxCols);
3345 
3346 	delete stream;
3347 	return true;
3348 }
3349 
loadPaletteTable(const char * filename,int firstPalette)3350 bool Screen::loadPaletteTable(const char *filename, int firstPalette) {
3351 	Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename);
3352 
3353 	if (!stream)
3354 		return false;
3355 
3356 	debugC(3, kDebugLevelScreen, "Screen::loadPaletteTable('%s', %d)", filename, firstPalette);
3357 
3358 	if (_isAmiga) {
3359 		const int numColors = getPalette(firstPalette).getNumColors();
3360 		const int palSize = getPalette(firstPalette).getNumColors() * Palette::kAmigaBytesPerColor;
3361 		const int numPals = stream->size() / palSize;
3362 
3363 		for (int i = 0; i < numPals; ++i)
3364 			getPalette(i + firstPalette).loadAmigaPalette(*stream, 0, numColors);
3365 	} else {
3366 		const int numColors = getPalette(firstPalette).getNumColors();
3367 		const int palSize = getPalette(firstPalette).getNumColors() * Palette::kVGABytesPerColor;
3368 		const int numPals = stream->size() / palSize;
3369 
3370 		for (int i = 0; i < numPals; ++i)
3371 			getPalette(i + firstPalette).loadVGAPalette(*stream, 0, numColors);
3372 	}
3373 
3374 	delete stream;
3375 	return true;
3376 }
3377 
loadPalette(const byte * data,Palette & pal,int bytes)3378 void Screen::loadPalette(const byte *data, Palette &pal, int bytes) {
3379 	Common::MemoryReadStream stream(data, bytes, DisposeAfterUse::NO);
3380 
3381 	if (_isAmiga) {
3382 		// EOB II Amiga sometimes has multiple palettes here one after
3383 		// the other (64 bytes each). We only load the first one here.
3384 		pal.loadAmigaPalette(stream, 0, MIN<int>(32, stream.size() / Palette::kAmigaBytesPerColor));
3385 	} else if (_vm->gameFlags().platform == Common::kPlatformPC98 && _use16ColorMode) {
3386 		pal.loadPC98Palette(stream, 0, stream.size() / Palette::kPC98BytesPerColor);
3387 	} else if (_renderMode == Common::kRenderEGA) {
3388 		// EOB II checks the number of palette bytes to distinguish between real EGA palettes
3389 		// and normal palettes (which are used to generate a color map).
3390 		if (stream.size() == 16)
3391 			pal.loadEGAPalette(stream, 0, stream.size());
3392 		else
3393 			pal.loadVGAPalette(stream, 0, stream.size() / Palette::kVGABytesPerColor);
3394 	} else
3395 		pal.loadVGAPalette(stream, 0, stream.size() / Palette::kVGABytesPerColor);
3396 }
3397 
3398 // dirty rect handling
3399 
addDirtyRect(int x,int y,int w,int h)3400 void Screen::addDirtyRect(int x, int y, int w, int h) {
3401 	if (_dirtyRects.size() >= kMaxDirtyRects || _forceFullUpdate) {
3402 		_forceFullUpdate = true;
3403 		return;
3404 	}
3405 
3406 	Common::Rect r(x, y, x + w, y + h);
3407 
3408 	// Clip rectangle
3409 	r.clip(SCREEN_W, _screenHeight - _yTransOffs);
3410 
3411 	// If it is empty after clipping, we are done
3412 	if (r.isEmpty())
3413 		return;
3414 
3415 	// Check if the new rectangle is contained within another in the list
3416 	Common::List<Common::Rect>::iterator it;
3417 	for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ) {
3418 		// If we find a rectangle which fully contains the new one,
3419 		// we can abort the search.
3420 		if (it->contains(r))
3421 			return;
3422 
3423 		// Conversely, if we find rectangles which are contained in
3424 		// the new one, we can remove them
3425 		if (r.contains(*it))
3426 			it = _dirtyRects.erase(it);
3427 		else
3428 			++it;
3429 	}
3430 
3431 	// If we got here, we can safely add r to the list of dirty rects.
3432 	_dirtyRects.push_back(r);
3433 }
3434 
3435 // overlay functions
3436 
getOverlayPtr(int page)3437 byte *Screen::getOverlayPtr(int page) {
3438 	if (page == 0 || page == 1)
3439 		return _sjisOverlayPtrs[1];
3440 	else if (page == 2 || page == 3)
3441 		return _sjisOverlayPtrs[2];
3442 
3443 	if (_vm->game() == GI_KYRA2) {
3444 		if (page == 12 || page == 13)
3445 			return _sjisOverlayPtrs[3];
3446 	} else if (_vm->game() == GI_LOL) {
3447 		if (page == 4 || page == 5)
3448 			return _sjisOverlayPtrs[3];
3449 		if (page == 6 || page == 7)
3450 			return _sjisOverlayPtrs[4];
3451 		if (page == 12 || page == 13)
3452 			return _sjisOverlayPtrs[5];
3453 	}
3454 
3455 	return 0;
3456 }
3457 
clearOverlayPage(int page)3458 void Screen::clearOverlayPage(int page) {
3459 	byte *dst = getOverlayPtr(page);
3460 	if (!dst)
3461 		return;
3462 	memset(dst, _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE);
3463 }
3464 
clearOverlayRect(int page,int x,int y,int w,int h)3465 void Screen::clearOverlayRect(int page, int x, int y, int w, int h) {
3466 	byte *dst = getOverlayPtr(page);
3467 
3468 	if (!dst || w < 0 || h < 0)
3469 		return;
3470 
3471 	x <<= 1; y <<= 1;
3472 	w <<= 1; h <<= 1;
3473 
3474 	dst += y * 640 + x;
3475 
3476 	if (w == 640 && h == 400) {
3477 		memset(dst, _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE);
3478 	} else {
3479 		while (h--) {
3480 			memset(dst, _sjisInvisibleColor, w);
3481 			dst += 640;
3482 		}
3483 	}
3484 }
3485 
copyOverlayRegion(int x,int y,int x2,int y2,int w,int h,int srcPage,int dstPage)3486 void Screen::copyOverlayRegion(int x, int y, int x2, int y2, int w, int h, int srcPage, int dstPage) {
3487 	byte *dst = getOverlayPtr(dstPage);
3488 	const byte *src = getOverlayPtr(srcPage);
3489 
3490 	if (!dst || !src)
3491 		return;
3492 
3493 	x <<= 1; x2 <<= 1;
3494 	y <<= 1; y2 <<= 1;
3495 	w <<= 1; h <<= 1;
3496 
3497 	if (w == 640 && h == 400) {
3498 		memcpy(dst, src, SCREEN_OVL_SJIS_SIZE);
3499 	} else {
3500 		dst += y2 * 640 + x2;
3501 		src += y * 640 + x;
3502 
3503 		while (h--) {
3504 			for (x = 0; x < w; ++x)
3505 				memmove(dst, src, w);
3506 			dst += 640;
3507 			src += 640;
3508 		}
3509 	}
3510 }
3511 
crossFadeRegion(int x1,int y1,int x2,int y2,int w,int h,int srcPage,int dstPage)3512 void Screen::crossFadeRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage) {
3513 	if (srcPage > 13 || dstPage > 13)
3514 		error("Screen::crossFadeRegion(): attempting to use temp page as source or dest page.");
3515 
3516 	hideMouse();
3517 
3518 	uint16 *wB = (uint16 *)_pagePtrs[14];
3519 	uint8 *hB = _pagePtrs[14] + 640 * _bytesPerPixel;
3520 
3521 	for (int i = 0; i < w; i++)
3522 		wB[i] = i;
3523 
3524 	for (int i = 0; i < h; i++)
3525 		hB[i] = i;
3526 
3527 	for (int i = 0; i < w; i++)
3528 		SWAP(wB[_vm->_rnd.getRandomNumberRng(0, w - 1)], wB[i]);
3529 
3530 	for (int i = 0; i < h; i++)
3531 		SWAP(hB[_vm->_rnd.getRandomNumberRng(0, h - 1)], hB[i]);
3532 
3533 	uint8 *s = _pagePtrs[srcPage];
3534 	uint8 *d = _pagePtrs[dstPage];
3535 
3536 	for (int i = 0; i < h; i++) {
3537 		int iH = i;
3538 		uint32 end = _system->getMillis() + 3;
3539 		for (int ii = 0; ii < w; ii++) {
3540 			int sX = (x1 + wB[ii]);
3541 			int sY = (y1 + hB[iH]);
3542 			int dX = (x2 + wB[ii]);
3543 			int dY = (y2 + hB[iH]);
3544 
3545 			if (++iH >= h)
3546 				iH = 0;
3547 
3548 			if (_bytesPerPixel == 2)
3549 				((uint16*)d)[dY * 320 + dX] = ((uint16*)s)[sY * 320 + sX];
3550 			else
3551 				d[dY * 320 + dX] = s[sY * 320 + sX];
3552 			addDirtyRect(dX, dY, 1, 1);
3553 		}
3554 
3555 		// This tries to speed things up, to get similiar speeds as in DOSBox etc.
3556 		// We can't write single pixels directly into the video memory like the original did.
3557 		// We also (unlike the original) want to aim at similiar speeds for all platforms.
3558 		if (!(i % 10))
3559 			updateScreen();
3560 
3561 		uint32 cur = _system->getMillis();
3562 		if (end > cur)
3563 			_system->delayMillis(end - cur);
3564 	}
3565 
3566 	updateScreen();
3567 	showMouse();
3568 }
3569 
3570 #pragma mark -
3571 
DOSFont()3572 DOSFont::DOSFont() {
3573 	_data = _widthTable = _heightTable = 0;
3574 	_colorMap = 0;
3575 	_width = _height = _numGlyphs = 0;
3576 	_bitmapOffsets = 0;
3577 }
3578 
load(Common::SeekableReadStream & file)3579 bool DOSFont::load(Common::SeekableReadStream &file) {
3580 	unload();
3581 
3582 	_data = new uint8[file.size()];
3583 	assert(_data);
3584 
3585 	file.read(_data, file.size());
3586 	if (file.err())
3587 		return false;
3588 
3589 	const uint16 fontSig = READ_LE_UINT16(_data + 2);
3590 
3591 	if (fontSig != 0x0500) {
3592 		warning("DOSFont: invalid font: %.04X)", fontSig);
3593 		return false;
3594 	}
3595 
3596 	const uint16 descOffset = READ_LE_UINT16(_data + 4);
3597 
3598 	_width = _data[descOffset + 5];
3599 	_height = _data[descOffset + 4];
3600 	_numGlyphs = _data[descOffset + 3] + 1;
3601 
3602 	_bitmapOffsets = (uint16 *)(_data + READ_LE_UINT16(_data + 6));
3603 	_widthTable = _data + READ_LE_UINT16(_data + 8);
3604 	_heightTable = _data + READ_LE_UINT16(_data + 12);
3605 
3606 	for (int i = 0; i < _numGlyphs; ++i)
3607 		_bitmapOffsets[i] = READ_LE_UINT16(&_bitmapOffsets[i]);
3608 
3609 	return true;
3610 }
3611 
getCharWidth(uint16 c) const3612 int DOSFont::getCharWidth(uint16 c) const {
3613 	if (c >= _numGlyphs)
3614 		return 0;
3615 	return _widthTable[c];
3616 }
3617 
drawChar(uint16 c,byte * dst,int pitch,int) const3618 void DOSFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
3619 	if (c >= _numGlyphs)
3620 		return;
3621 
3622 	if (!_bitmapOffsets[c])
3623 		return;
3624 
3625 	const uint8 *src = _data + _bitmapOffsets[c];
3626 	const uint8 charWidth = _widthTable[c];
3627 
3628 	if (!charWidth)
3629 		return;
3630 
3631 	pitch -= charWidth;
3632 
3633 	uint8 charH1 = _heightTable[c * 2 + 0];
3634 	uint8 charH2 = _heightTable[c * 2 + 1];
3635 	uint8 charH0 = _height - (charH1 + charH2);
3636 
3637 	while (charH1--) {
3638 		uint8 col = _colorMap[0];
3639 		for (int i = 0; i < charWidth; ++i) {
3640 			if (col != 0)
3641 				*dst = col;
3642 			++dst;
3643 		}
3644 		dst += pitch;
3645 	}
3646 
3647 	while (charH2--) {
3648 		uint8 b = 0;
3649 		for (int i = 0; i < charWidth; ++i) {
3650 			uint8 col;
3651 			if (i & 1) {
3652 				col = _colorMap[b >> 4];
3653 			} else {
3654 				b = *src++;
3655 				col = _colorMap[b & 0xF];
3656 			}
3657 			if (col != 0) {
3658 				*dst = col;
3659 			}
3660 			++dst;
3661 		}
3662 		dst += pitch;
3663 	}
3664 
3665 	while (charH0--) {
3666 		uint8 col = _colorMap[0];
3667 		for (int i = 0; i < charWidth; ++i) {
3668 			if (col != 0)
3669 				*dst = col;
3670 			++dst;
3671 		}
3672 		dst += pitch;
3673 	}
3674 }
3675 
unload()3676 void DOSFont::unload() {
3677 	delete[] _data;
3678 	_data = _widthTable = _heightTable = 0;
3679 	_colorMap = 0;
3680 	_width = _height = _numGlyphs = 0;
3681 	_bitmapOffsets = 0;
3682 }
3683 
3684 
AMIGAFont()3685 AMIGAFont::AMIGAFont() {
3686 	_width = _height = 0;
3687 	memset(_chars, 0, sizeof(_chars));
3688 }
3689 
load(Common::SeekableReadStream & file)3690 bool AMIGAFont::load(Common::SeekableReadStream &file) {
3691 	const uint16 dataSize = file.readUint16BE();
3692 	if (dataSize + 2 != file.size())
3693 		return false;
3694 
3695 	_width = file.readByte();
3696 	_height = file.readByte();
3697 
3698 	// Read the character definition offset table
3699 	uint16 offsets[ARRAYSIZE(_chars)];
3700 	for (int i = 0; i < ARRAYSIZE(_chars); ++i)
3701 		offsets[i] = file.readUint16BE() + 4;
3702 
3703 	if (file.err())
3704 		return false;
3705 
3706 	for (int i = 0; i < ARRAYSIZE(_chars); ++i) {
3707 		file.seek(offsets[i], SEEK_SET);
3708 
3709 		_chars[i].yOffset = file.readByte();
3710 		_chars[i].xOffset = file.readByte();
3711 		_chars[i].width = file.readByte();
3712 		file.readByte(); // unused
3713 
3714 		// If the y offset is 255, then the character
3715 		// does not have any bitmap representation
3716 		if (_chars[i].yOffset != 255) {
3717 			Character::Graphics &g = _chars[i].graphics;
3718 
3719 			g.width = file.readUint16BE();
3720 			g.height = file.readUint16BE();
3721 
3722 			int depth = file.readByte();
3723 			int specialWidth = file.readByte();
3724 			int flags = file.readByte();
3725 			int bytesPerPlane = file.readByte();
3726 
3727 			assert(depth != 0 && specialWidth == 0 && flags == 0 && bytesPerPlane != 0);
3728 
3729 			// Allocate a temporary buffer to store the plane data
3730 			const int planesSize = bytesPerPlane * g.height * depth;
3731 			uint8 *tempData = new uint8[MAX(g.width * g.height, planesSize)];
3732 			assert(tempData);
3733 
3734 			file.read(tempData, planesSize);
3735 
3736 			// Convert the plane based graphics to our graphic format
3737 			Screen::convertAmigaGfx(tempData, g.width, g.height, depth, false, bytesPerPlane);
3738 
3739 			// Create a buffer perfectly fitting the character
3740 			g.bitmap = new uint8[g.width * g.height];
3741 			assert(g.bitmap);
3742 
3743 			memcpy(g.bitmap, tempData, g.width * g.height);
3744 			delete[] tempData;
3745 		}
3746 
3747 		if (file.err())
3748 			return false;
3749 	}
3750 
3751 	return !file.err();
3752 }
3753 
getCharWidth(uint16 c) const3754 int AMIGAFont::getCharWidth(uint16 c) const {
3755 	if (c >= 255)
3756 		return 0;
3757 	return _chars[c].width;
3758 }
3759 
drawChar(uint16 c,byte * dst,int pitch,int) const3760 void AMIGAFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
3761 	if (c >= 255)
3762 		return;
3763 
3764 	if (_chars[c].yOffset == 255)
3765 		return;
3766 
3767 	dst += _chars[c].yOffset * pitch;
3768 	dst += _chars[c].xOffset;
3769 
3770 	pitch -= _chars[c].graphics.width;
3771 
3772 	const uint8 *src = _chars[c].graphics.bitmap;
3773 	assert(src);
3774 
3775 	for (int y = 0; y < _chars[c].graphics.height; ++y) {
3776 		for (int x = 0; x < _chars[c].graphics.width; ++x) {
3777 			if (*src)
3778 				*dst = *src;
3779 			++src;
3780 			++dst;
3781 		}
3782 
3783 		dst += pitch;
3784 	}
3785 }
3786 
unload()3787 void AMIGAFont::unload() {
3788 	_width = _height = 0;
3789 	for (int i = 0; i < ARRAYSIZE(_chars); ++i)
3790 		delete[] _chars[i].graphics.bitmap;
3791 	memset(_chars, 0, sizeof(_chars));
3792 }
3793 
SJISFont(Common::SharedPtr<Graphics::FontSJIS> & font,const uint8 invisColor,bool is16Color,bool drawOutline,int extraSpacing)3794 SJISFont::SJISFont(Common::SharedPtr<Graphics::FontSJIS> &font, const uint8 invisColor, bool is16Color, bool drawOutline, int extraSpacing)
3795 	: _colorMap(0), _font(font), _invisColor(invisColor), _isTextMode(is16Color), _style(kStyleNone), _drawOutline(drawOutline), _sjisWidthOffset(extraSpacing) {
3796 	assert(_font);
3797 }
3798 
getHeight() const3799 int SJISFont::getHeight() const {
3800 	return _font->getFontHeight() >> 1;
3801 }
3802 
getWidth() const3803 int SJISFont::getWidth() const {
3804 	return (_font->getMaxFontWidth() >> 1) + _sjisWidthOffset;
3805 }
3806 
getCharWidth(uint16 c) const3807 int SJISFont::getCharWidth(uint16 c) const {
3808 	if (c <= 0x7F || (c >= 0xA1 && c <= 0xDF))
3809 		return _font->getCharWidth('a') >> 1;
3810 	else
3811 		return getWidth();
3812 }
3813 
setColorMap(const uint8 * src)3814 void SJISFont::setColorMap(const uint8 *src) {
3815 	_colorMap = src;
3816 }
3817 
drawChar(uint16 c,byte * dst,int pitch,int) const3818 void SJISFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
3819 	uint8 color1, color2;
3820 
3821 	if (_isTextMode) {
3822 		// PC98 16 color games specify a color value which is for the
3823 		// PC98 text mode palette, thus we need to remap it.
3824 		color1 = ((_colorMap[1] >> 5) & 0x7) + 16;
3825 		color2 = ((_colorMap[0] >> 5) & 0x7) + 16;
3826 	} else {
3827 		color1 = _colorMap[1];
3828 		color2 = _colorMap[0];
3829 	}
3830 
3831 	if (!_isTextMode && _colorMap[0] == _invisColor)
3832 		_font->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
3833 	else
3834 		_font->setDrawingMode(_drawOutline ? Graphics::FontSJIS::kOutlineMode : Graphics::FontSJIS::kDefaultMode);
3835 
3836 	_font->toggleFatPrint(_style == kStyleFat);
3837 	_font->drawChar(dst, c, 640, 1, color1, color2, 640, 400);
3838 }
3839 
3840 #pragma mark -
3841 
Palette(const int numColors)3842 Palette::Palette(const int numColors) : _palData(0), _numColors(numColors) {
3843 	_palData = new uint8[numColors * 3];
3844 	assert(_palData);
3845 
3846 	memset(_palData, 0, numColors * 3);
3847 }
3848 
~Palette()3849 Palette::~Palette() {
3850 	delete[] _palData;
3851 	_palData = 0;
3852 }
3853 
loadVGAPalette(Common::ReadStream & stream,int startIndex,int colors)3854 void Palette::loadVGAPalette(Common::ReadStream &stream, int startIndex, int colors) {
3855 	assert(startIndex + colors <= _numColors);
3856 
3857 	uint8 *pos = _palData + startIndex * 3;
3858 	for (int i = 0 ; i < colors * 3; i++)
3859 		*pos++ = stream.readByte() & 0x3F;
3860 }
3861 
loadHiColorPalette(Common::ReadStream & stream,int startIndex,int colors)3862 void Palette::loadHiColorPalette(Common::ReadStream &stream, int startIndex, int colors) {
3863 	uint16 *pos = (uint16*)(_palData + startIndex * 2);
3864 
3865 	Graphics::PixelFormat currentFormat = g_system->getScreenFormat();
3866 	Graphics::PixelFormat originalFormat(2, 5, 5, 5, 0, 5, 10, 0, 0);
3867 
3868 	for (int i = 0; i < colors; i++) {
3869 		uint8 r, g, b;
3870 		originalFormat.colorToRGB(stream.readUint16LE(), r, g, b);
3871 		*pos++ = currentFormat.RGBToColor(r, g, b);
3872 	}
3873 }
3874 
loadEGAPalette(Common::ReadStream & stream,int startIndex,int colors)3875 void Palette::loadEGAPalette(Common::ReadStream &stream, int startIndex, int colors) {
3876 	assert(startIndex + colors <= 16);
3877 
3878 	uint8 *dst = _palData + startIndex * 3;
3879 	for (int i = 0; i < colors; i++) {
3880 		uint8 index = stream.readByte();
3881 		assert(index < _egaNumColors);
3882 		memcpy(dst, &_egaColors[index * 3], 3);
3883 		dst += 3;
3884 	}
3885 }
3886 
setCGAPalette(int palIndex,CGAIntensity intensity)3887 void Palette::setCGAPalette(int palIndex, CGAIntensity intensity) {
3888 	assert(_numColors >= _cgaNumColors);
3889 	assert(!(palIndex & ~1));
3890 	memcpy(_palData, _cgaColors[palIndex * 2 + intensity], _cgaNumColors * 3);
3891 }
3892 
loadAmigaPalette(Common::ReadStream & stream,int startIndex,int colors)3893 void Palette::loadAmigaPalette(Common::ReadStream &stream, int startIndex, int colors) {
3894 	assert(startIndex + colors <= _numColors);
3895 
3896 	for (int i = 0; i < colors; ++i) {
3897 		uint16 col = stream.readUint16BE();
3898 		_palData[(i + startIndex) * 3 + 2] = ((col & 0xF) * 0x3F) / 0xF; col >>= 4;
3899 		_palData[(i + startIndex) * 3 + 1] = ((col & 0xF) * 0x3F) / 0xF; col >>= 4;
3900 		_palData[(i + startIndex) * 3 + 0] = ((col & 0xF) * 0x3F) / 0xF; col >>= 4;
3901 	}
3902 }
3903 
loadPC98Palette(Common::ReadStream & stream,int startIndex,int colors)3904 void Palette::loadPC98Palette(Common::ReadStream &stream, int startIndex, int colors) {
3905 	assert(startIndex + colors <= _numColors);
3906 
3907 	for (int i = 0; i < colors; ++i) {
3908 		const byte g = stream.readByte(), r = stream.readByte(), b = stream.readByte();
3909 
3910 		_palData[(i + startIndex) * 3 + 0] = ((r & 0xF) * 0x3F) / 0xF;
3911 		_palData[(i + startIndex) * 3 + 1] = ((g & 0xF) * 0x3F) / 0xF;
3912 		_palData[(i + startIndex) * 3 + 2] = ((b & 0xF) * 0x3F) / 0xF;
3913 	}
3914 }
3915 
clear()3916 void Palette::clear() {
3917 	memset(_palData, 0, _numColors * 3);
3918 }
3919 
fill(int firstCol,int numCols,uint8 value)3920 void Palette::fill(int firstCol, int numCols, uint8 value) {
3921 	assert(firstCol >= 0 && firstCol + numCols <= _numColors);
3922 
3923 	memset(_palData + firstCol * 3, CLIP<int>(value, 0, 63), numCols * 3);
3924 }
3925 
copy(const Palette & source,int firstCol,int numCols,int dstStart)3926 void Palette::copy(const Palette &source, int firstCol, int numCols, int dstStart) {
3927 	if (numCols == -1)
3928 		numCols = MIN(source.getNumColors(), _numColors) - firstCol;
3929 	if (dstStart == -1)
3930 		dstStart = firstCol;
3931 
3932 	assert(numCols >= 0 && numCols <= _numColors);
3933 	assert(firstCol >= 0 && firstCol <= source.getNumColors());
3934 	assert(dstStart >= 0 && dstStart + numCols <= _numColors);
3935 
3936 	memmove(_palData + dstStart * 3, source._palData + firstCol * 3, numCols * 3);
3937 }
3938 
copy(const uint8 * source,int firstCol,int numCols,int dstStart)3939 void Palette::copy(const uint8 *source, int firstCol, int numCols, int dstStart) {
3940 	if (dstStart == -1)
3941 		dstStart = firstCol;
3942 
3943 	assert(numCols >= 0 && numCols <= _numColors);
3944 	assert(firstCol >= 0);
3945 	assert(dstStart >= 0 && dstStart + numCols <= _numColors);
3946 
3947 	memmove(_palData + dstStart * 3, source + firstCol * 3, numCols * 3);
3948 }
3949 
fetchRealPalette() const3950 uint8 *Palette::fetchRealPalette() const {
3951 	uint8 *buffer = new uint8[_numColors * 3];
3952 	assert(buffer);
3953 
3954 	uint8 *dst = buffer;
3955 	const uint8 *palData = _palData;
3956 
3957 	for (int i = 0; i < _numColors; ++i) {
3958 		dst[0] = (palData[0] << 2) | (palData[0] & 3);
3959 		dst[1] = (palData[1] << 2) | (palData[1] & 3);
3960 		dst[2] = (palData[2] << 2) | (palData[2] & 3);
3961 
3962 		dst += 3;
3963 		palData += 3;
3964 	}
3965 
3966 	return buffer;
3967 }
3968 
3969 const uint8 Palette::_egaColors[] = {
3970 	0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
3971 	0xAA, 0x00, 0x00, 0xAA,	0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
3972 	0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF,	0x55, 0x55, 0xFF, 0xFF,
3973 	0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
3974 };
3975 
3976 const int Palette::_egaNumColors = ARRAYSIZE(_egaColors) / 3;
3977 
3978 const uint8 Palette::_cgaColors[4][12] = {
3979 	{ 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x2A, 0x15, 0x00 },
3980 	{ 0x00, 0x00, 0x00, 0x15, 0x3F, 0x15, 0x3F, 0x15, 0x15, 0x3F, 0x3F, 0x15 },
3981 	{ 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x00, 0x2A, 0x2A, 0x2A, 0x2A },
3982 	{ 0x00, 0x00, 0x00, 0x15, 0x3F, 0x3F, 0x3F, 0x15, 0x3F, 0x3F, 0x3F, 0x3F }
3983 };
3984 
3985 const int Palette::_cgaNumColors = ARRAYSIZE(_cgaColors[0]) / 3;
3986 
3987 } // End of namespace Kyra
3988