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 "common/config-manager.h"
24 #include "common/file.h"
25 #include "common/textconsole.h"
26 #include "engines/util.h"
27 
28 #include "graphics/cursorman.h"
29 #include "graphics/palette.h"
30 
31 #include "agi/agi.h"
32 #include "agi/graphics.h"
33 #include "agi/mouse_cursor.h"
34 #include "agi/palette.h"
35 #include "agi/picture.h"
36 #include "agi/text.h"
37 
38 namespace Agi {
39 
40 #include "agi/font.h"
41 
GfxMgr(AgiBase * vm,GfxFont * font)42 GfxMgr::GfxMgr(AgiBase *vm, GfxFont *font) : _vm(vm), _font(font) {
43 	_agipalFileNum = 0;
44 
45 	memset(&_paletteGfxMode, 0, sizeof(_paletteGfxMode));
46 	memset(&_paletteTextMode, 0, sizeof(_paletteTextMode));
47 
48 	memset(&_mouseCursor, 0, sizeof(_mouseCursor));
49 	memset(&_mouseCursorBusy, 0, sizeof(_mouseCursorBusy));
50 
51 	initPriorityTable();
52 
53 	_renderStartVisualOffsetY = 0;
54 	_renderStartDisplayOffsetY = 0;
55 
56 	_upscaledHires = DISPLAY_UPSCALED_DISABLED;
57 	_displayScreenWidth = DISPLAY_DEFAULT_WIDTH;
58 	_displayScreenHeight = DISPLAY_DEFAULT_HEIGHT;
59 	_displayFontWidth = 8;
60 	_displayFontHeight = 8;
61 
62 	_displayWidthMulAdjust = 0; // visualPos * (2+0) = displayPos
63 	_displayHeightMulAdjust = 0; // visualPos * (1+0) = displayPos
64 
65 	_pixels = 0;
66 	_displayPixels = 0;
67 
68 	_activeScreen = NULL;
69 	_gameScreen = NULL;
70 	_priorityScreen = NULL;
71 	_displayScreen = NULL;
72 }
73 
74 /**
75  * Initialize graphics device.
76  *
77  * @see deinit_video()
78  */
initVideo()79 int GfxMgr::initVideo() {
80 	bool forceHires = false;
81 
82 	// Set up palettes
83 	initPalette(_paletteTextMode, PALETTE_EGA);
84 
85 	switch (_vm->_renderMode) {
86 	case Common::kRenderEGA:
87 		initPalette(_paletteGfxMode, PALETTE_EGA);
88 		break;
89 	case Common::kRenderCGA:
90 		initPalette(_paletteGfxMode, PALETTE_CGA, 4, 8);
91 		break;
92 	case Common::kRenderVGA:
93 		initPalette(_paletteGfxMode, PALETTE_VGA, 256, 8);
94 		break;
95 	case Common::kRenderHercG:
96 		initPalette(_paletteGfxMode, PALETTE_HERCULES_GREEN, 2, 8);
97 		forceHires = true;
98 		break;
99 	case Common::kRenderHercA:
100 		initPalette(_paletteGfxMode, PALETTE_HERCULES_AMBER, 2, 8);
101 		forceHires = true;
102 		break;
103 	case Common::kRenderAmiga:
104 		if (!ConfMan.getBool("altamigapalette")) {
105 			// Set the correct Amiga palette depending on AGI interpreter version
106 			if (_vm->getVersion() < 0x2936)
107 				initPalette(_paletteGfxMode, PALETTE_AMIGA_V1, 16, 4);
108 			else if (_vm->getVersion() == 0x2936)
109 				initPalette(_paletteGfxMode, PALETTE_AMIGA_V2, 16, 4);
110 			else if (_vm->getVersion() > 0x2936)
111 				initPalette(_paletteGfxMode, PALETTE_AMIGA_V3, 16, 4);
112 		} else {
113 			// Set the old common alternative Amiga palette
114 			initPalette(_paletteGfxMode, PALETTE_AMIGA_ALT);
115 		}
116 		break;
117 	case Common::kRenderApple2GS:
118 		switch (_vm->getGameID()) {
119 		case GID_SQ1:
120 			// Special one, only used for Space Quest 1 on Apple IIgs. Is the same as Amiga v1 palette
121 			initPalette(_paletteGfxMode, PALETTE_APPLE_II_GS_SQ1, 16, 4);
122 			break;
123 		default:
124 			// Regular "standard" Apple IIgs palette, used by everything else
125 			initPalette(_paletteGfxMode, PALETTE_APPLE_II_GS, 16, 4);
126 			break;
127 		}
128 		break;
129 	case Common::kRenderAtariST:
130 		initPalette(_paletteGfxMode, PALETTE_ATARI_ST, 16, 3);
131 		break;
132 	case Common::kRenderMacintosh:
133 		switch (_vm->getGameID()) {
134 		case GID_KQ3:
135 		case GID_PQ1:
136 			initPaletteCLUT(_paletteGfxMode, PALETTE_MACINTOSH_CLUT, 16);
137 			break;
138 		case GID_GOLDRUSH:
139 			// We use the common KQ3/PQ1 palette at the moment.
140 			// It seems the Gold Rush palette, that came with the game is quite ugly.
141 			initPaletteCLUT(_paletteGfxMode, PALETTE_MACINTOSH_CLUT, 16);
142 			break;
143 		case GID_SQ2:
144 			initPaletteCLUT(_paletteGfxMode, PALETTE_MACINTOSH_CLUT3, 16);
145 			break;
146 		default:
147 			initPaletteCLUT(_paletteGfxMode, PALETTE_MACINTOSH_CLUT3, 16);
148 			break;
149 		}
150 		break;
151 	default:
152 		error("initVideo: unsupported render mode");
153 		break;
154 	}
155 
156 	//bool forcedUpscale = true;
157 
158 	if (_font->isFontHires() || forceHires) {
159 		// Upscaling enable
160 		_upscaledHires = DISPLAY_UPSCALED_640x400;
161 		_displayScreenWidth = 640;
162 		_displayScreenHeight = 400;
163 		_displayFontWidth = 16;
164 		_displayFontHeight = 16;
165 
166 		_displayWidthMulAdjust = 2;
167 		_displayHeightMulAdjust = 1;
168 	}
169 
170 	// set up mouse cursors
171 	switch (_vm->_renderMode) {
172 	case Common::kRenderEGA:
173 	case Common::kRenderCGA:
174 	case Common::kRenderVGA:
175 	case Common::kRenderHercG:
176 	case Common::kRenderHercA:
177 		initMouseCursor(&_mouseCursor, MOUSECURSOR_SCI, 11, 16, 0, 0);
178 		initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_SCI_BUSY, 15, 16, 7, 8);
179 		break;
180 	case Common::kRenderAmiga:
181 		initMouseCursor(&_mouseCursor, MOUSECURSOR_AMIGA, 8, 11, 0, 0);
182 		initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_AMIGA_BUSY, 13, 16, 7, 8);
183 		break;
184 	case Common::kRenderApple2GS:
185 		// had no special busy mouse cursor
186 		initMouseCursor(&_mouseCursor, MOUSECURSOR_APPLE_II_GS, 9, 11, 0, 0);
187 		initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_SCI_BUSY, 15, 16, 7, 8);
188 		break;
189 	case Common::kRenderAtariST:
190 		initMouseCursor(&_mouseCursor, MOUSECURSOR_ATARI_ST, 11, 16, 0, 0);
191 		initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_SCI_BUSY, 15, 16, 7, 8);
192 		break;
193 	case Common::kRenderMacintosh:
194 		// It looks like Atari ST + Macintosh used the same standard mouse cursor
195 		// TODO: Verify by checking actual hardware
196 		initMouseCursor(&_mouseCursor, MOUSECURSOR_ATARI_ST, 11, 16, 0, 0);
197 		initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_MACINTOSH_BUSY, 10, 14, 7, 8);
198 		break;
199 	default:
200 		error("initVideo: unsupported render mode");
201 		break;
202 	}
203 
204 	_pixels = SCRIPT_WIDTH * SCRIPT_HEIGHT;
205 	_gameScreen = (byte *)calloc(_pixels, 1);
206 	_priorityScreen = (byte *)calloc(_pixels, 1);
207 	_activeScreen = _gameScreen;
208 	//_activeScreen = _priorityScreen;
209 
210 	_displayPixels = _displayScreenWidth * _displayScreenHeight;
211 	_displayScreen = (byte *)calloc(_displayPixels, 1);
212 
213 	initGraphics(_displayScreenWidth, _displayScreenHeight);
214 
215 	setPalette(true); // set gfx-mode palette
216 
217 	// set up mouse cursor palette
218 	CursorMan.replaceCursorPalette(MOUSECURSOR_PALETTE, 1, ARRAYSIZE(MOUSECURSOR_PALETTE) / 3);
219 	setMouseCursor();
220 
221 	return errOK;
222 }
223 
224 /**
225  * Deinitialize graphics device.
226  *
227  * @see init_video()
228  */
deinitVideo()229 int GfxMgr::deinitVideo() {
230 	// Free mouse cursors in case they were allocated
231 	if (_mouseCursor.bitmapDataAllocated)
232 		free(_mouseCursor.bitmapDataAllocated);
233 	if (_mouseCursorBusy.bitmapDataAllocated)
234 		free(_mouseCursorBusy.bitmapDataAllocated);
235 
236 	free(_displayScreen);
237 	free(_gameScreen);
238 	free(_priorityScreen);
239 
240 	return errOK;
241 }
242 
setRenderStartOffset(uint16 offsetY)243 void GfxMgr::setRenderStartOffset(uint16 offsetY) {
244 	if (offsetY >= (VISUAL_HEIGHT - SCRIPT_HEIGHT))
245 		error("invalid render start offset");
246 
247 	_renderStartVisualOffsetY = offsetY;
248 	_renderStartDisplayOffsetY = offsetY * (1 + _displayHeightMulAdjust);
249 }
getRenderStartDisplayOffsetY()250 uint16 GfxMgr::getRenderStartDisplayOffsetY() {
251 	return _renderStartDisplayOffsetY;
252 }
253 
254 // Translates a game screen coordinate to a display screen coordinate
255 // Game screen to 320x200 -> x * 2, y + renderStart
256 // Game screen to 640x400 -> x * 4, (y * 2) + renderStart
translateGamePosToDisplayScreen(int16 & x,int16 & y)257 void GfxMgr::translateGamePosToDisplayScreen(int16 &x, int16 &y) {
258 	x = x * (2 + _displayWidthMulAdjust);
259 	y = y * (1 + _displayHeightMulAdjust) + _renderStartDisplayOffsetY;
260 }
261 
262 // Translates a visual coordinate to a display screen coordinate
263 // Visual to 320x200 -> x * 2, y
264 // Visual to 640x400 -> x * 4, y * 2
translateVisualPosToDisplayScreen(int16 & x,int16 & y)265 void GfxMgr::translateVisualPosToDisplayScreen(int16 &x, int16 &y) {
266 	x = x * (2 + _displayWidthMulAdjust);
267 	y = y * (1 + _displayHeightMulAdjust);
268 }
269 
270 // Translates a display screen coordinate to a game screen coordinate
271 // Display screen to 320x200 -> x / 2, y - renderStart
272 // Display screen to 640x400 -> x / 4, (y / 2) - renderStart
translateDisplayPosToGameScreen(int16 & x,int16 & y)273 void GfxMgr::translateDisplayPosToGameScreen(int16 &x, int16 &y) {
274 	y -= _renderStartDisplayOffsetY; // remove status bar line
275 	x = x / (2 + _displayWidthMulAdjust);
276 	y = y / (1 + _displayHeightMulAdjust);
277 	if (y < 0)
278 		y = 0;
279 	if (y >= SCRIPT_HEIGHT)
280 		y = SCRIPT_HEIGHT + 1; // 1 beyond
281 }
282 
283 // Translates dimension from visual screen to display screen
translateVisualDimensionToDisplayScreen(int16 & width,int16 & height)284 void GfxMgr::translateVisualDimensionToDisplayScreen(int16 &width, int16 &height) {
285 	width = width * (2 + _displayWidthMulAdjust);
286 	height = height * (1 + _displayHeightMulAdjust);
287 }
288 
289 // Translates dimension from display screen to visual screen
translateDisplayDimensionToVisualScreen(int16 & width,int16 & height)290 void GfxMgr::translateDisplayDimensionToVisualScreen(int16 &width, int16 &height) {
291 	width = width / (2 + _displayWidthMulAdjust);
292 	height = height / (1 + _displayHeightMulAdjust);
293 }
294 
295 // Translates a rect from game screen to display screen
translateGameRectToDisplayScreen(int16 & x,int16 & y,int16 & width,int16 & height)296 void GfxMgr::translateGameRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height) {
297 	translateGamePosToDisplayScreen(x, y);
298 	translateVisualDimensionToDisplayScreen(width, height);
299 }
300 
301 // Translates a rect from visual screen to display screen
translateVisualRectToDisplayScreen(int16 & x,int16 & y,int16 & width,int16 & height)302 void GfxMgr::translateVisualRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height) {
303 	translateVisualPosToDisplayScreen(x, y);
304 	translateVisualDimensionToDisplayScreen(width, height);
305 }
306 
getDisplayOffsetToGameScreenPos(int16 x,int16 y)307 uint32 GfxMgr::getDisplayOffsetToGameScreenPos(int16 x, int16 y) {
308 	translateGamePosToDisplayScreen(x, y);
309 	return (y * _displayScreenWidth) + x;
310 }
311 
getDisplayOffsetToVisualScreenPos(int16 x,int16 y)312 uint32 GfxMgr::getDisplayOffsetToVisualScreenPos(int16 x, int16 y) {
313 	translateVisualPosToDisplayScreen(x, y);
314 	return (y * _displayScreenWidth) + x;
315 }
316 
317 // Attention: uses display screen coordinates!
copyDisplayRectToScreen(int16 x,int16 y,int16 width,int16 height)318 void GfxMgr::copyDisplayRectToScreen(int16 x, int16 y, int16 width, int16 height) {
319 	// Clamp to sane values to prevent off screen blits causing exceptions in backend
320 	// FIXME: Add warnings / debug of clamping?
321 	width = CLIP<int16>(width, 0, _displayScreenWidth);
322 	height = CLIP<int16>(height, 0, _displayScreenHeight);
323 	x = CLIP<int16>(x, 0, _displayScreenWidth-width);
324 	y = CLIP<int16>(y, 0, _displayScreenHeight-height);
325 
326 	g_system->copyRectToScreen(_displayScreen + y * _displayScreenWidth + x, _displayScreenWidth, x, y, width, height);
327 }
copyDisplayRectToScreen(int16 x,int16 adjX,int16 y,int16 adjY,int16 width,int16 adjWidth,int16 height,int16 adjHeight)328 void GfxMgr::copyDisplayRectToScreen(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight) {
329 	switch (_upscaledHires) {
330 	case DISPLAY_UPSCALED_DISABLED:
331 		break;
332 	case DISPLAY_UPSCALED_640x400:
333 		adjX *= 2; adjY *= 2;
334 		adjWidth *= 2; adjHeight *= 2;
335 		break;
336 	default:
337 		assert(0);
338 		break;
339 	}
340 	x += adjX; y += adjY;
341 	width += adjWidth; height += adjHeight;
342 	g_system->copyRectToScreen(_displayScreen + y * _displayScreenWidth + x, _displayScreenWidth, x, y, width, height);
343 }
copyDisplayRectToScreenUsingGamePos(int16 x,int16 y,int16 width,int16 height)344 void GfxMgr::copyDisplayRectToScreenUsingGamePos(int16 x, int16 y, int16 width, int16 height) {
345 	translateGameRectToDisplayScreen(x, y, width, height);
346 	g_system->copyRectToScreen(_displayScreen + (y * _displayScreenWidth) + x, _displayScreenWidth, x, y, width, height);
347 }
copyDisplayRectToScreenUsingVisualPos(int16 x,int16 y,int16 width,int16 height)348 void GfxMgr::copyDisplayRectToScreenUsingVisualPos(int16 x, int16 y, int16 width, int16 height) {
349 	translateVisualRectToDisplayScreen(x, y, width, height);
350 	g_system->copyRectToScreen(_displayScreen + (y * _displayScreenWidth) + x, _displayScreenWidth, x, y, width, height);
351 }
copyDisplayToScreen()352 void GfxMgr::copyDisplayToScreen() {
353 	g_system->copyRectToScreen(_displayScreen, _displayScreenWidth, 0, 0, _displayScreenWidth, _displayScreenHeight);
354 }
355 
translateFontPosToDisplayScreen(int16 & x,int16 & y)356 void GfxMgr::translateFontPosToDisplayScreen(int16 &x, int16 &y) {
357 	x *= _displayFontWidth;
358 	y *= _displayFontHeight;
359 }
translateDisplayPosToFontScreen(int16 & x,int16 & y)360 void GfxMgr::translateDisplayPosToFontScreen(int16 &x, int16 &y) {
361 	x /= _displayFontWidth;
362 	y /= _displayFontHeight;
363 }
translateFontDimensionToDisplayScreen(int16 & width,int16 & height)364 void GfxMgr::translateFontDimensionToDisplayScreen(int16 &width, int16 &height) {
365 	width *= _displayFontWidth;
366 	height *= _displayFontHeight;
367 }
translateFontRectToDisplayScreen(int16 & x,int16 & y,int16 & width,int16 & height)368 void GfxMgr::translateFontRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height) {
369 	translateFontPosToDisplayScreen(x, y);
370 	translateFontDimensionToDisplayScreen(width, height);
371 }
getFontRectForDisplayScreen(int16 column,int16 row,int16 width,int16 height)372 Common::Rect GfxMgr::getFontRectForDisplayScreen(int16 column, int16 row, int16 width, int16 height) {
373 	Common::Rect displayRect(width * _displayFontWidth, height * _displayFontHeight);
374 	displayRect.moveTo(column * _displayFontWidth, row * _displayFontHeight);
375 	return displayRect;
376 }
377 
debugShowMap(int mapNr)378 void GfxMgr::debugShowMap(int mapNr) {
379 	switch (mapNr) {
380 	case 0:
381 		_activeScreen = _gameScreen;
382 		break;
383 	case 1:
384 		_activeScreen = _priorityScreen;
385 		break;
386 	default:
387 		break;
388 	}
389 
390 	render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT);
391 }
392 
clear(byte color,byte priority)393 void GfxMgr::clear(byte color, byte priority) {
394 	memset(_gameScreen, color, _pixels);
395 	memset(_priorityScreen, priority, _pixels);
396 }
397 
clearDisplay(byte color,bool copyToScreen)398 void GfxMgr::clearDisplay(byte color, bool copyToScreen) {
399 	memset(_displayScreen, color, _displayPixels);
400 
401 	if (copyToScreen) {
402 		copyDisplayToScreen();
403 	}
404 }
405 
putPixel(int16 x,int16 y,byte drawMask,byte color,byte priority)406 void GfxMgr::putPixel(int16 x, int16 y, byte drawMask, byte color, byte priority) {
407 	int offset = y * SCRIPT_WIDTH + x;
408 
409 	if (drawMask & GFX_SCREEN_MASK_VISUAL) {
410 		_gameScreen[offset] = color;
411 	}
412 	if (drawMask & GFX_SCREEN_MASK_PRIORITY) {
413 		_priorityScreen[offset] = priority;
414 	}
415 }
416 
putPixelOnDisplay(int16 x,int16 y,byte color)417 void GfxMgr::putPixelOnDisplay(int16 x, int16 y, byte color) {
418 	uint32 offset = 0;
419 
420 	switch (_upscaledHires) {
421 	case DISPLAY_UPSCALED_DISABLED:
422 		offset = y * _displayScreenWidth + x;
423 
424 		_displayScreen[offset] = color;
425 		break;
426 	case DISPLAY_UPSCALED_640x400:
427 		offset = (y * _displayScreenWidth) + x;
428 
429 		_displayScreen[offset + 0] = color;
430 		_displayScreen[offset + 1] = color;
431 		_displayScreen[offset + _displayScreenWidth + 0] = color;
432 		_displayScreen[offset + _displayScreenWidth + 1] = color;
433 		break;
434 	default:
435 		break;
436 	}
437 }
438 
putPixelOnDisplay(int16 x,int16 adjX,int16 y,int16 adjY,byte color)439 void GfxMgr::putPixelOnDisplay(int16 x, int16 adjX, int16 y, int16 adjY, byte color) {
440 	switch (_upscaledHires) {
441 	case DISPLAY_UPSCALED_DISABLED:
442 		break;
443 	case DISPLAY_UPSCALED_640x400:
444 		adjX *= 2; adjY *= 2;
445 		break;
446 	default:
447 		assert(0);
448 		break;
449 	}
450 	x += adjX;
451 	y += adjY;
452 	putPixelOnDisplay(x, y, color);
453 }
454 
putFontPixelOnDisplay(int16 baseX,int16 baseY,int16 addX,int16 addY,byte color,bool isHires)455 void GfxMgr::putFontPixelOnDisplay(int16 baseX, int16 baseY, int16 addX, int16 addY, byte color, bool isHires) {
456 	uint32 offset = 0;
457 
458 	switch (_upscaledHires) {
459 	case DISPLAY_UPSCALED_DISABLED:
460 		offset = ((baseY + addY) * _displayScreenWidth) + (baseX + addX);
461 		_displayScreen[offset] = color;
462 		break;
463 	case DISPLAY_UPSCALED_640x400:
464 		if (isHires) {
465 			offset = ((baseY + addY) * _displayScreenWidth) + (baseX + addX);
466 			_displayScreen[offset] = color;
467 		} else {
468 			offset = ((baseY + addY * 2) * _displayScreenWidth) + (baseX + addX * 2);
469 			_displayScreen[offset + 0] = color;
470 			_displayScreen[offset + 1] = color;
471 			_displayScreen[offset + _displayScreenWidth + 0] = color;
472 			_displayScreen[offset + _displayScreenWidth + 1] = color;
473 		}
474 		break;
475 	default:
476 		break;
477 	}
478 }
479 
getColor(int16 x,int16 y)480 byte GfxMgr::getColor(int16 x, int16 y) {
481 	int offset = y * SCRIPT_WIDTH + x;
482 
483 	return _gameScreen[offset];
484 }
485 
getPriority(int16 x,int16 y)486 byte GfxMgr::getPriority(int16 x, int16 y) {
487 	int offset = y * SCRIPT_WIDTH + x;
488 
489 	return _priorityScreen[offset];
490 }
491 
492 // used, when a control pixel is found
493 // will search downwards and compare priority in case any is found
checkControlPixel(int16 x,int16 y,byte viewPriority)494 bool GfxMgr::checkControlPixel(int16 x, int16 y, byte viewPriority) {
495 	int offset = y * SCRIPT_WIDTH + x;
496 	byte curPriority;
497 
498 	while (1) {
499 		y++;
500 		offset += SCRIPT_WIDTH;
501 		if (y >= SCRIPT_HEIGHT) {
502 			// end of screen, nothing but control pixels found
503 			return true; // draw view pixel
504 		}
505 		curPriority = _priorityScreen[offset];
506 		if (curPriority > 2) // valid priority found?
507 			break;
508 	}
509 	if (curPriority <= viewPriority)
510 		return true; // view priority is higher, draw
511 	return false; // view priority is lower, don't draw
512 }
513 
514 static byte CGA_MixtureColorTable[] = {
515 	0x00, 0x08, 0x04, 0x0C, 0x01, 0x09, 0x02, 0x05,
516 	0x0A, 0x0D, 0x06, 0x0E, 0x0B, 0x03, 0x07, 0x0F
517 };
518 
getCGAMixtureColor(byte color)519 byte GfxMgr::getCGAMixtureColor(byte color) {
520 	return CGA_MixtureColorTable[color & 0x0F];
521 }
522 
523 // Attention: in our implementation, y-coordinate is upper left.
524 // Sierra passed the lower left instead. We changed it to make upscaling easier.
render_Block(int16 x,int16 y,int16 width,int16 height,bool copyToScreen)525 void GfxMgr::render_Block(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) {
526 	if (!render_Clip(x, y, width, height))
527 		return;
528 
529 	switch (_vm->_renderMode) {
530 	case Common::kRenderHercG:
531 	case Common::kRenderHercA:
532 		render_BlockHercules(x, y, width, height, copyToScreen);
533 		break;
534 	case Common::kRenderCGA:
535 		render_BlockCGA(x, y, width, height, copyToScreen);
536 		break;
537 	case Common::kRenderEGA:
538 	default:
539 		render_BlockEGA(x, y, width, height, copyToScreen);
540 		break;
541 	}
542 
543 	if (copyToScreen) {
544 		copyDisplayRectToScreenUsingGamePos(x, y, width, height);
545 	}
546 }
547 
render_Clip(int16 & x,int16 & y,int16 & width,int16 & height,int16 clipAgainstWidth,int16 clipAgainstHeight)548 bool GfxMgr::render_Clip(int16 &x, int16 &y, int16 &width, int16 &height, int16 clipAgainstWidth, int16 clipAgainstHeight) {
549 	if ((x >= clipAgainstWidth) || ((x + width - 1) < 0) ||
550 	        (y < 0) || ((y + (height - 1)) >= clipAgainstHeight)) {
551 		return false;
552 	}
553 
554 	if (y < 0) {
555 		height += y;
556 		y = 0;
557 	}
558 
559 	if ((y + height - 1) >= clipAgainstHeight) {
560 		height = clipAgainstHeight - y;
561 	}
562 
563 #if 0
564 	if ((y - height + 1) < 0)
565 		height = y + 1;
566 
567 	if (y >= clipAgainstHeight) {
568 		height -= y - (clipAgainstHeight - 1);
569 		y = clipAgainstHeight - 1;
570 	}
571 #endif
572 
573 	if (x < 0) {
574 		width += x;
575 		x = 0;
576 	}
577 
578 	if ((x + width - 1) >= clipAgainstWidth) {
579 		width = clipAgainstWidth - x;
580 	}
581 	return true;
582 }
583 
render_BlockEGA(int16 x,int16 y,int16 width,int16 height,bool copyToScreen)584 void GfxMgr::render_BlockEGA(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) {
585 	uint32 offsetVisual = SCRIPT_WIDTH * y + x;
586 	uint32 offsetDisplay = getDisplayOffsetToGameScreenPos(x, y);
587 	int16 remainingWidth = width;
588 	int16 remainingHeight = height;
589 	byte curColor = 0;
590 	int16 displayWidth = width * (2 + _displayWidthMulAdjust);
591 
592 	while (remainingHeight) {
593 		remainingWidth = width;
594 
595 		switch (_upscaledHires) {
596 		case DISPLAY_UPSCALED_DISABLED:
597 			while (remainingWidth) {
598 				curColor = _activeScreen[offsetVisual++];
599 				_displayScreen[offsetDisplay++] = curColor;
600 				_displayScreen[offsetDisplay++] = curColor;
601 				remainingWidth--;
602 			}
603 			break;
604 		case DISPLAY_UPSCALED_640x400:
605 			while (remainingWidth) {
606 				curColor = _activeScreen[offsetVisual++];
607 				memset(&_displayScreen[offsetDisplay], curColor, 4);
608 				memset(&_displayScreen[offsetDisplay + _displayScreenWidth], curColor, 4);
609 				offsetDisplay += 4;
610 				remainingWidth--;
611 			}
612 			break;
613 		default:
614 			assert(0);
615 			break;
616 		}
617 
618 		offsetVisual += SCRIPT_WIDTH - width;
619 		offsetDisplay += _displayScreenWidth - displayWidth;
620 
621 		switch (_upscaledHires) {
622 		case DISPLAY_UPSCALED_640x400:
623 			offsetDisplay += _displayScreenWidth;
624 			break;
625 		default:
626 			break;
627 		}
628 
629 		remainingHeight--;
630 	}
631 }
632 
render_BlockCGA(int16 x,int16 y,int16 width,int16 height,bool copyToScreen)633 void GfxMgr::render_BlockCGA(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) {
634 	uint32 offsetVisual = SCRIPT_WIDTH * y + x;
635 	uint32 offsetDisplay = getDisplayOffsetToGameScreenPos(x, y);
636 	int16 remainingWidth = width;
637 	int16 remainingHeight = height;
638 	byte curColor = 0;
639 	int16 displayWidth = width * (2 + _displayWidthMulAdjust);
640 
641 	while (remainingHeight) {
642 		remainingWidth = width;
643 
644 		switch (_upscaledHires) {
645 		case DISPLAY_UPSCALED_DISABLED:
646 			while (remainingWidth) {
647 				curColor = _activeScreen[offsetVisual++];
648 				_displayScreen[offsetDisplay++] = curColor & 0x03; // we process CGA mixture
649 				_displayScreen[offsetDisplay++] = curColor >> 2;
650 				remainingWidth--;
651 			}
652 			break;
653 		case DISPLAY_UPSCALED_640x400:
654 			while (remainingWidth) {
655 				curColor = _activeScreen[offsetVisual++];
656 				_displayScreen[offsetDisplay + 0] = curColor & 0x03; // we process CGA mixture
657 				_displayScreen[offsetDisplay + 1] = curColor >> 2;
658 				_displayScreen[offsetDisplay + 2] = curColor & 0x03;
659 				_displayScreen[offsetDisplay + 3] = curColor >> 2;
660 				_displayScreen[offsetDisplay + _displayScreenWidth + 0] = curColor & 0x03;
661 				_displayScreen[offsetDisplay + _displayScreenWidth + 1] = curColor >> 2;
662 				_displayScreen[offsetDisplay + _displayScreenWidth + 2] = curColor & 0x03;
663 				_displayScreen[offsetDisplay + _displayScreenWidth + 3] = curColor >> 2;
664 				offsetDisplay += 4;
665 				remainingWidth--;
666 			}
667 			break;
668 		default:
669 			assert(0);
670 			break;
671 		}
672 
673 		offsetVisual += SCRIPT_WIDTH - width;
674 		offsetDisplay += _displayScreenWidth - displayWidth;
675 
676 		switch (_upscaledHires) {
677 		case DISPLAY_UPSCALED_640x400:
678 			offsetDisplay += _displayScreenWidth;
679 			break;
680 		default:
681 			break;
682 		}
683 
684 		remainingHeight--;
685 	}
686 }
687 
688 static const uint8 herculesColorMapping[] = {
689 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
690 	0x88, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
691 	0x80, 0x10, 0x02, 0x20, 0x01, 0x08, 0x40, 0x04,
692 	0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00,
693 	0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,
694 	0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00,
695 	0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88,
696 	0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
697 	0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88, 0x00,
698 	0xD7, 0xFF, 0x7D, 0xFF, 0xD7, 0xFF, 0x7D, 0xFF,
699 	0xDD, 0x55, 0x77, 0xAA, 0xDD, 0x55, 0x77, 0xAA,
700 	0x7F, 0xEF, 0xFD, 0xDF, 0xFE, 0xF7, 0xBF, 0xFB,
701 	0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF,
702 	0x77, 0xBB, 0xDD, 0xEE, 0x77, 0xBB, 0xDD, 0xEE,
703 	0x77, 0xFF, 0xFF, 0xFF, 0xDD, 0xFF, 0xFF, 0xFF,
704 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
705 };
706 
707 // Sierra actually seems to have rendered the whole screen all the time
render_BlockHercules(int16 x,int16 y,int16 width,int16 height,bool copyToScreen)708 void GfxMgr::render_BlockHercules(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) {
709 	uint32 offsetVisual = SCRIPT_WIDTH * y + x;
710 	uint32 offsetDisplay = getDisplayOffsetToGameScreenPos(x, y);
711 	int16 remainingWidth = width;
712 	int16 remainingHeight = height;
713 	byte curColor = 0;
714 	int16 displayWidth = width * (2 + _displayWidthMulAdjust);
715 
716 	assert(_upscaledHires == DISPLAY_UPSCALED_640x400);
717 
718 	uint16 lookupOffset1 = (y * 2 & 0x07);
719 	uint16 lookupOffset2 = 0;
720 	bool   getUpperNibble = false;
721 	byte   herculesColors1 = 0;
722 	byte   herculesColors2 = 0;
723 
724 	while (remainingHeight) {
725 		remainingWidth = width;
726 
727 		lookupOffset1 = (lookupOffset1 + 0) & 0x07;
728 		lookupOffset2 = (lookupOffset1 + 1) & 0x07;
729 
730 		getUpperNibble = (x & 1) ? false : true;
731 		while (remainingWidth) {
732 			curColor = _activeScreen[offsetVisual++] & 0x0F;
733 
734 			if (getUpperNibble) {
735 				herculesColors1 = herculesColorMapping[curColor * 8 + lookupOffset1] & 0x0F;
736 				herculesColors2 = herculesColorMapping[curColor * 8 + lookupOffset2] & 0x0F;
737 			} else {
738 				herculesColors1 = herculesColorMapping[curColor * 8 + lookupOffset1] >> 4;
739 				herculesColors2 = herculesColorMapping[curColor * 8 + lookupOffset2] >> 4;
740 			}
741 			getUpperNibble ^= true;
742 
743 			_displayScreen[offsetDisplay + 0] = (herculesColors1 & 0x08) ? 1 : 0;
744 			_displayScreen[offsetDisplay + 1] = (herculesColors1 & 0x04) ? 1 : 0;
745 			_displayScreen[offsetDisplay + 2] = (herculesColors1 & 0x02) ? 1 : 0;
746 			_displayScreen[offsetDisplay + 3] = (herculesColors1 & 0x01) ? 1 : 0;
747 
748 			_displayScreen[offsetDisplay + _displayScreenWidth + 0] = (herculesColors2 & 0x08) ? 1 : 0;
749 			_displayScreen[offsetDisplay + _displayScreenWidth + 1] = (herculesColors2 & 0x04) ? 1 : 0;
750 			_displayScreen[offsetDisplay + _displayScreenWidth + 2] = (herculesColors2 & 0x02) ? 1 : 0;
751 			_displayScreen[offsetDisplay + _displayScreenWidth + 3] = (herculesColors2 & 0x01) ? 1 : 0;
752 
753 			offsetDisplay += 4;
754 			remainingWidth--;
755 		}
756 
757 		lookupOffset1 += 2;
758 
759 		offsetVisual += SCRIPT_WIDTH - width;
760 		offsetDisplay += _displayScreenWidth - displayWidth;
761 		offsetDisplay += _displayScreenWidth;
762 
763 		remainingHeight--;
764 	}
765 }
766 
767 // Table used for at least Manhunter 2, it renders 2 lines -> 3 lines instead of 4
768 // Manhunter 1 is shipped with a broken Hercules font
769 // King's Quest 4 aborts right at the start, when Hercules rendering is active
770 #if 0
771 static const uint8 herculesCoordinateOffset[] = {
772 	0x00, 0x01, 0x03, 0x04, 0x06, 0x07, 0x01, 0x02,
773 	0x04, 0x05, 0x07, 0x00, 0x02, 0x03, 0x05, 0x06
774 };
775 
776 static const uint8 herculesColorMapping[] = {
777 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	0x40, 0x00, 0x02, 0x00, 0x40, 0x00, 0x08, 0x00,
778 	0x80, 0x10, 0x02, 0x20, 0x01, 0x08, 0x40, 0x04,	0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00,
779 	0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,	0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00,
780 	0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88,	0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
781 	0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88, 0x00,	0xD7, 0xFF, 0x7D, 0xFF, 0xD7, 0xFF, 0x7D, 0xFF,
782 	0xDD, 0x55, 0x77, 0xAA, 0xDD, 0x55, 0x77, 0xAA,	0x7F, 0xEF, 0xFD, 0xDF, 0xFE, 0xF7, 0xBF, 0xFB,
783 	0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF,	0x77, 0xBB, 0xDD, 0xEE, 0x77, 0xBB, 0xDD, 0xEE,
784 	0x7F, 0xEF, 0xFB, 0xBF, 0xEF, 0xFE, 0xBF, 0xFD,	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
785 };
786 #endif
787 
transition_Amiga()788 void GfxMgr::transition_Amiga() {
789 	uint16 screenPos = 1;
790 	uint32 screenStepPos = 1;
791 	int16  posY = 0, posX = 0;
792 	int16  stepCount = 0;
793 
794 	// disable mouse while transition is taking place
795 	if ((_vm->_game.mouseEnabled) && (!_vm->_game.mouseHidden)) {
796 		CursorMan.showMouse(false);
797 	}
798 
799 	do {
800 		if (screenPos & 1) {
801 			screenPos = screenPos >> 1;
802 			screenPos = screenPos ^ 0x3500; // 13568d
803 		} else {
804 			screenPos = screenPos >> 1;
805 		}
806 
807 		if ((screenPos < 13440) && (screenPos & 1)) {
808 			screenStepPos = screenPos >> 1;
809 			posY = screenStepPos / SCRIPT_WIDTH;
810 			posX = screenStepPos - (posY * SCRIPT_WIDTH);
811 
812 			// Adjust to only update the game screen, not the status bar
813 			translateGamePosToDisplayScreen(posX, posY);
814 
815 			switch (_upscaledHires) {
816 			case DISPLAY_UPSCALED_DISABLED:
817 				for (int16 multiPixel = 0; multiPixel < 4; multiPixel++) {
818 					screenStepPos = (posY * _displayScreenWidth) + posX;
819 					g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 2, 1);
820 					posY += 42;
821 				}
822 				break;
823 			case DISPLAY_UPSCALED_640x400:
824 				for (int16 multiPixel = 0; multiPixel < 4; multiPixel++) {
825 					screenStepPos = (posY * _displayScreenWidth) + posX;
826 					g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 4, 2);
827 					posY += 42 * 2;
828 				}
829 				break;
830 			default:
831 				assert(0);
832 				break;
833 			}
834 
835 			stepCount++;
836 			if (stepCount == 220) {
837 				// 30 times for the whole transition, so should take around 0.5 seconds
838 				g_system->updateScreen();
839 				g_system->delayMillis(16);
840 				stepCount = 0;
841 			}
842 		}
843 	} while (screenPos != 1);
844 
845 	// Enable mouse again
846 	if ((_vm->_game.mouseEnabled) && (!_vm->_game.mouseHidden)) {
847 		CursorMan.showMouse(true);
848 	}
849 
850 	g_system->updateScreen();
851 }
852 
853 // This transition code was not reverse engineered, but created based on the Amiga transition code
854 // Atari ST definitely had a hi-res transition using the full resolution unlike the Amiga transition.
transition_AtariSt()855 void GfxMgr::transition_AtariSt() {
856 	uint16 screenPos = 1;
857 	uint32 screenStepPos = 1;
858 	int16  posY = 0, posX = 0;
859 	int16  stepCount = 0;
860 
861 	// disable mouse while transition is taking place
862 	if ((_vm->_game.mouseEnabled) && (!_vm->_game.mouseHidden)) {
863 		CursorMan.showMouse(false);
864 	}
865 
866 	do {
867 		if (screenPos & 1) {
868 			screenPos = screenPos >> 1;
869 			screenPos = screenPos ^ 0x3500; // 13568d
870 		} else {
871 			screenPos = screenPos >> 1;
872 		}
873 
874 		if ((screenPos < 13440) && (screenPos & 1)) {
875 			screenStepPos = screenPos >> 1;
876 			posY = screenStepPos / DISPLAY_DEFAULT_WIDTH;
877 			posX = screenStepPos - (posY * DISPLAY_DEFAULT_WIDTH);
878 
879 			switch (_upscaledHires) {
880 			case DISPLAY_UPSCALED_DISABLED:
881 				posY += _renderStartDisplayOffsetY; // adjust to only update the main area, not the status bar
882 				for (int16 multiPixel = 0; multiPixel < 8; multiPixel++) {
883 					screenStepPos = (posY * _displayScreenWidth) + posX;
884 					g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 1, 1);
885 					posY += 21;
886 				}
887 				break;
888 			case DISPLAY_UPSCALED_640x400:
889 				posX *= 2; posY *= 2;
890 				posY += _renderStartDisplayOffsetY; // adjust to only update the main area, not the status bar
891 				for (int16 multiPixel = 0; multiPixel < 8; multiPixel++) {
892 					screenStepPos = (posY * _displayScreenWidth) + posX;
893 					g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 2, 2);
894 					posY += 21 * 2;
895 				}
896 				break;
897 			default:
898 				break;
899 			}
900 
901 			stepCount++;
902 			if (stepCount == 168) {
903 				// 40 times for the whole transition, so should take around 0.7 seconds
904 				// When using an Atari ST emulator, the transition seems to be even slower than this
905 				// TODO: should get checked on real hardware
906 				g_system->updateScreen();
907 				g_system->delayMillis(16);
908 				stepCount = 0;
909 			}
910 		}
911 	} while (screenPos != 1);
912 
913 	// Enable mouse again
914 	if ((_vm->_game.mouseEnabled) && (!_vm->_game.mouseHidden)) {
915 		CursorMan.showMouse(true);
916 	}
917 
918 	g_system->updateScreen();
919 }
920 
921 // Attention: y coordinate is here supposed to be the upper one!
block_save(int16 x,int16 y,int16 width,int16 height,byte * bufferPtr)922 void GfxMgr::block_save(int16 x, int16 y, int16 width, int16 height, byte *bufferPtr) {
923 	int16 startOffset = y * SCRIPT_WIDTH + x;
924 	int16 offset = startOffset;
925 	int16 remainingHeight = height;
926 	byte *curBufferPtr = bufferPtr;
927 
928 	//warning("block_save: %d, %d -> %d, %d", x, y, width, height);
929 
930 	while (remainingHeight) {
931 		memcpy(curBufferPtr, _gameScreen + offset, width);
932 		offset += SCRIPT_WIDTH;
933 		curBufferPtr += width;
934 		remainingHeight--;
935 	}
936 
937 	remainingHeight = height;
938 	offset = startOffset;
939 	while (remainingHeight) {
940 		memcpy(curBufferPtr, _priorityScreen + offset, width);
941 		offset += SCRIPT_WIDTH;
942 		curBufferPtr += width;
943 		remainingHeight--;
944 	}
945 }
946 
947 // Attention: y coordinate is here supposed to be the upper one!
block_restore(int16 x,int16 y,int16 width,int16 height,byte * bufferPtr)948 void GfxMgr::block_restore(int16 x, int16 y, int16 width, int16 height, byte *bufferPtr) {
949 	int16 startOffset = y * SCRIPT_WIDTH + x;
950 	int16 offset = startOffset;
951 	int16 remainingHeight = height;
952 	byte *curBufferPtr = bufferPtr;
953 
954 	//warning("block_restore: %d, %d -> %d, %d", x, y, width, height);
955 
956 	while (remainingHeight) {
957 		memcpy(_gameScreen + offset, curBufferPtr, width);
958 		offset += SCRIPT_WIDTH;
959 		curBufferPtr += width;
960 		remainingHeight--;
961 	}
962 
963 	remainingHeight = height;
964 	offset = startOffset;
965 	while (remainingHeight) {
966 		memcpy(_priorityScreen + offset, curBufferPtr, width);
967 		offset += SCRIPT_WIDTH;
968 		curBufferPtr += width;
969 		remainingHeight--;
970 	}
971 }
972 
973 // coordinates are for visual screen, but are supposed to point somewhere inside the playscreen
974 // x, y is the upper left. Sierra passed them as lower left. We change that to make upscaling easier.
975 // attention: Clipping is done here against 160x200 instead of 160x168
976 //            Original interpreter didn't do any clipping, we do it for security.
977 //            Clipping against the regular script width/height must not be done,
978 //            because at least during the intro one message box goes beyond playscreen
979 //            Going beyond 160x168 will result in messageboxes not getting fully removed
980 //            In KQ4's case, the scripts clear the screen that's why it works.
drawBox(int16 x,int16 y,int16 width,int16 height,byte backgroundColor,byte lineColor)981 void GfxMgr::drawBox(int16 x, int16 y, int16 width, int16 height, byte backgroundColor, byte lineColor) {
982 	if (!render_Clip(x, y, width, height, VISUAL_WIDTH, VISUAL_HEIGHT - _renderStartVisualOffsetY))
983 		return;
984 
985 	// coordinate translation: visual-screen -> display-screen
986 	translateVisualRectToDisplayScreen(x, y, width, height);
987 
988 	y = y + _renderStartDisplayOffsetY;	// drawDisplayRect paints anywhere on the whole screen, our coordinate is within playscreen
989 
990 	// draw box background
991 	drawDisplayRect(x, y, width, height, backgroundColor);
992 
993 	// draw lines
994 	switch (_vm->_renderMode) {
995 	case Common::kRenderApple2GS:
996 	case Common::kRenderAmiga:
997 		// Slightly different window frame, and actually using 1-pixel width, which is "hi-res"
998 		drawDisplayRect(x, +2, y, +2, width, -4, 0, 1, lineColor);
999 		drawDisplayRect(x + width, -3, y, +2, 0, 1, height, -4, lineColor);
1000 		drawDisplayRect(x, +2, y + height, -3, width, -4, 0, 1, lineColor);
1001 		drawDisplayRect(x, +2, y, +2, 0, 1, height, -4, lineColor);
1002 		break;
1003 	case Common::kRenderMacintosh:
1004 		// 1 pixel between box and frame lines. Frame lines were black
1005 		drawDisplayRect(x, +1, y, +1, width, -2, 0, 1, 0);
1006 		drawDisplayRect(x + width, -2, y, +1, 0, 1, height, -2, 0);
1007 		drawDisplayRect(x, +1, y + height, -2, width, -2, 0, 1, 0);
1008 		drawDisplayRect(x, +1, y, +1, 0, 1, height, -2, 0);
1009 		break;
1010 	case Common::kRenderHercA:
1011 	case Common::kRenderHercG:
1012 		lineColor = 0; // change linecolor to black
1013 		// fall through
1014 	case Common::kRenderCGA:
1015 	case Common::kRenderEGA:
1016 	case Common::kRenderVGA:
1017 	case Common::kRenderAtariST:
1018 	default:
1019 		drawDisplayRect(x, +2, y, +1, width, -4, 0, 1, lineColor);
1020 		drawDisplayRect(x + width, -4, y, +2, 0, 2, height, -4, lineColor);
1021 		drawDisplayRect(x, +2, y + height, -2, width, -4, 0, 1, lineColor);
1022 		drawDisplayRect(x, +2, y, +2, 0, 2, height, -4, lineColor);
1023 		break;
1024 	}
1025 }
1026 
1027 // coordinates are directly for display screen
drawDisplayRect(int16 x,int16 y,int16 width,int16 height,byte color,bool copyToScreen)1028 void GfxMgr::drawDisplayRect(int16 x, int16 y, int16 width, int16 height, byte color, bool copyToScreen) {
1029 	switch (_vm->_renderMode) {
1030 	case Common::kRenderCGA:
1031 		drawDisplayRectCGA(x, y, width, height, color);
1032 		break;
1033 	case Common::kRenderHercG:
1034 	case Common::kRenderHercA:
1035 		if (color)
1036 			color = 1; // change any color except black to green/amber
1037 		// fall through
1038 	case Common::kRenderEGA:
1039 	default:
1040 		drawDisplayRectEGA(x, y, width, height, color);
1041 		break;
1042 	}
1043 	if (copyToScreen) {
1044 		copyDisplayRectToScreen(x, y, width, height);
1045 	}
1046 }
1047 
drawDisplayRect(int16 x,int16 adjX,int16 y,int16 adjY,int16 width,int16 adjWidth,int16 height,int16 adjHeight,byte color,bool copyToScreen)1048 void GfxMgr::drawDisplayRect(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight, byte color, bool copyToScreen) {
1049 	switch (_upscaledHires) {
1050 	case DISPLAY_UPSCALED_DISABLED:
1051 		x += adjX; y += adjY;
1052 		width += adjWidth; height += adjHeight;
1053 		break;
1054 	case DISPLAY_UPSCALED_640x400:
1055 		x += adjX * 2; y += adjY * 2;
1056 		width += adjWidth * 2; height += adjHeight * 2;
1057 		break;
1058 	default:
1059 		assert(0);
1060 		break;
1061 	}
1062 	drawDisplayRect(x, y, width, height, color, copyToScreen);
1063 }
1064 
drawDisplayRectEGA(int16 x,int16 y,int16 width,int16 height,byte color)1065 void GfxMgr::drawDisplayRectEGA(int16 x, int16 y, int16 width, int16 height, byte color) {
1066 	uint32 offsetDisplay = (y * _displayScreenWidth) + x;
1067 	int16 remainingHeight = height;
1068 
1069 	while (remainingHeight) {
1070 		memset(_displayScreen + offsetDisplay, color, width);
1071 
1072 		offsetDisplay += _displayScreenWidth;
1073 		remainingHeight--;
1074 	}
1075 }
1076 
drawDisplayRectCGA(int16 x,int16 y,int16 width,int16 height,byte color)1077 void GfxMgr::drawDisplayRectCGA(int16 x, int16 y, int16 width, int16 height, byte color) {
1078 	uint32 offsetDisplay = (y * _displayScreenWidth) + x;
1079 	int16 remainingHeight = height;
1080 	int16 remainingWidth = width;
1081 	byte CGAMixtureColor = getCGAMixtureColor(color);
1082 	byte *displayScreen = nullptr;
1083 
1084 	// we should never get an uneven width
1085 	assert((width & 1) == 0);
1086 
1087 	while (remainingHeight) {
1088 		remainingWidth = width;
1089 
1090 		// set up pointer
1091 		displayScreen = _displayScreen + offsetDisplay;
1092 
1093 		while (remainingWidth) {
1094 			*displayScreen++ = CGAMixtureColor & 0x03;
1095 			*displayScreen++ = CGAMixtureColor >> 2;
1096 			remainingWidth -= 2;
1097 		}
1098 
1099 		offsetDisplay += _displayScreenWidth;
1100 		remainingHeight--;
1101 	}
1102 }
1103 
1104 // row + column are text-coordinates
drawCharacter(int16 row,int16 column,byte character,byte foreground,byte background,bool disabledLook)1105 void GfxMgr::drawCharacter(int16 row, int16 column, byte character, byte foreground, byte background, bool disabledLook) {
1106 	int16 x = column;
1107 	int16 y = row;
1108 	byte  transformXOR = 0;
1109 	byte  transformOR = 0;
1110 
1111 	translateFontPosToDisplayScreen(x, y);
1112 
1113 	// Now figure out, if special handling needs to be done
1114 	if (_vm->_game.gfxMode) {
1115 		if (background & 0x08) {
1116 			// invert enabled
1117 			background &= 0x07; // remove invert bit
1118 			transformXOR = 0xFF;
1119 		}
1120 		if (disabledLook) {
1121 			transformOR = 0x55;
1122 		}
1123 	}
1124 
1125 	drawCharacterOnDisplay(x, y, character, foreground, background, transformXOR, transformOR);
1126 }
1127 
1128 // only meant for internal use (SystemUI)
drawStringOnDisplay(int16 x,int16 y,const char * text,byte foregroundColor,byte backgroundColor)1129 void GfxMgr::drawStringOnDisplay(int16 x, int16 y, const char *text, byte foregroundColor, byte backgroundColor) {
1130 	while (*text) {
1131 		drawCharacterOnDisplay(x, y, *text, foregroundColor, backgroundColor);
1132 		text++;
1133 		x += _displayFontWidth;
1134 	}
1135 }
1136 
drawStringOnDisplay(int16 x,int16 adjX,int16 y,int16 adjY,const char * text,byte foregroundColor,byte backgroundColor)1137 void GfxMgr::drawStringOnDisplay(int16 x, int16 adjX, int16 y, int16 adjY, const char *text, byte foregroundColor, byte backgroundColor) {
1138 	switch (_upscaledHires) {
1139 	case DISPLAY_UPSCALED_DISABLED:
1140 		x += adjX;
1141 		y += adjY;
1142 		break;
1143 	case DISPLAY_UPSCALED_640x400:
1144 		x += adjX * 2;
1145 		y += adjY * 2;
1146 		break;
1147 	default:
1148 		assert(0);
1149 		break;
1150 	}
1151 	drawStringOnDisplay(x, y, text, foregroundColor, backgroundColor);
1152 }
1153 
drawCharacterOnDisplay(int16 x,int16 y,const byte character,byte foreground,byte background,byte transformXOR,byte transformOR)1154 void GfxMgr::drawCharacterOnDisplay(int16 x, int16 y, const byte character, byte foreground, byte background, byte transformXOR, byte transformOR) {
1155 	int16       curX, curY;
1156 	const byte *fontData;
1157 	bool        fontIsHires = _font->isFontHires();
1158 	int16       fontHeight = fontIsHires ? 16 : FONT_DISPLAY_HEIGHT;
1159 	int16       fontWidth = fontIsHires ? 16 : FONT_DISPLAY_WIDTH;
1160 	int16       fontBytesPerCharacter = fontIsHires ? 32 : FONT_BYTES_PER_CHARACTER;
1161 	byte        curByte = 0;
1162 	uint16      curBit;
1163 
1164 	// get font data of specified character
1165 	fontData = _font->getFontData() + character * fontBytesPerCharacter;
1166 
1167 	curBit = 0;
1168 	for (curY = 0; curY < fontHeight; curY++) {
1169 		for (curX = 0; curX < fontWidth; curX++) {
1170 			if (!curBit) {
1171 				curByte = *fontData;
1172 				// do transformations in case they are needed (invert/disabled look)
1173 				curByte ^= transformXOR;
1174 				curByte |= transformOR;
1175 				fontData++;
1176 				curBit  = 0x80;
1177 			}
1178 			if (curByte & curBit) {
1179 				putFontPixelOnDisplay(x, y, curX, curY, foreground, fontIsHires);
1180 			} else {
1181 				putFontPixelOnDisplay(x, y, curX, curY, background, fontIsHires);
1182 			}
1183 			curBit = curBit >> 1;
1184 		}
1185 		if (transformOR)
1186 			transformOR ^= 0xFF;
1187 	}
1188 
1189 	copyDisplayRectToScreen(x, y, _displayFontWidth, _displayFontHeight);
1190 }
1191 
1192 #define SHAKE_VERTICAL_PIXELS 4
1193 #define SHAKE_HORIZONTAL_PIXELS 4
1194 
1195 // Sierra used some EGA port trickery to do it, we have to do it by copying pixels around
1196 //
1197 // Shaking locations:
1198 // - Fanmade "Enclosure" right during the intro
1199 // - Space Quest 2 almost right at the start when getting captured (after walking into the space ship)
shakeScreen(int16 repeatCount)1200 void GfxMgr::shakeScreen(int16 repeatCount) {
1201 	int shakeNr, shakeCount;
1202 	uint8 *blackSpace;
1203 	int16 shakeHorizontalPixels = SHAKE_HORIZONTAL_PIXELS * (2 + _displayWidthMulAdjust);
1204 	int16 shakeVerticalPixels = SHAKE_VERTICAL_PIXELS * (1 + _displayHeightMulAdjust);
1205 
1206 	if ((blackSpace = (uint8 *)calloc(shakeHorizontalPixels * _displayScreenWidth, 1)) == NULL)
1207 		return;
1208 
1209 	shakeCount = repeatCount * 8; // effectively 4 shakes per repeat
1210 
1211 	// it's 4 pixels down and 8 pixels to the right
1212 	// and it's also filling the remaining space with black
1213 	for (shakeNr = 0; shakeNr < shakeCount; shakeNr++) {
1214 		if (shakeNr & 1) {
1215 			// move back
1216 			copyDisplayToScreen();
1217 		} else {
1218 			g_system->copyRectToScreen(_displayScreen, _displayScreenWidth, shakeHorizontalPixels, shakeVerticalPixels, _displayScreenWidth - shakeHorizontalPixels, _displayScreenHeight - shakeVerticalPixels);
1219 			// additionally fill the remaining space with black
1220 			g_system->copyRectToScreen(blackSpace, _displayScreenWidth, 0, 0, _displayScreenWidth, shakeVerticalPixels);
1221 			g_system->copyRectToScreen(blackSpace, shakeHorizontalPixels, 0, 0, shakeHorizontalPixels, _displayScreenHeight);
1222 		}
1223 		g_system->updateScreen();
1224 		g_system->delayMillis(66); // Sierra waited for 4 V'Syncs, which is around 66 milliseconds
1225 	}
1226 
1227 	free(blackSpace);
1228 }
1229 
updateScreen()1230 void GfxMgr::updateScreen() {
1231 	g_system->updateScreen();
1232 }
1233 
initPriorityTable()1234 void GfxMgr::initPriorityTable() {
1235 	_priorityTableSet = false;
1236 
1237 	createDefaultPriorityTable(_priorityTable);
1238 }
1239 
createDefaultPriorityTable(uint8 * priorityTable)1240 void GfxMgr::createDefaultPriorityTable(uint8 *priorityTable) {
1241 	int16 priority, step;
1242 	int16 yPos = 0;
1243 
1244 	for (priority = 1; priority < 15; priority++) {
1245 		for (step = 0; step < 12; step++) {
1246 			priorityTable[yPos++] = priority < 4 ? 4 : priority;
1247 		}
1248 	}
1249 }
1250 
setPriorityTable(int16 priorityBase)1251 void GfxMgr::setPriorityTable(int16 priorityBase) {
1252 	int16 x, priorityY, priority;
1253 
1254 	_priorityTableSet = true;
1255 	x = (SCRIPT_HEIGHT - priorityBase) * SCRIPT_HEIGHT / 10;
1256 
1257 	for (priorityY = 0; priorityY < SCRIPT_HEIGHT; priorityY++) {
1258 		priority = (priorityY - priorityBase) < 0 ? 4 : (priorityY - priorityBase) * SCRIPT_HEIGHT / x + 5;
1259 		if (priority > 15)
1260 			priority = 15;
1261 		_priorityTable[priorityY] = priority;
1262 	}
1263 }
1264 
1265 // used for saving
saveLoadGetPriority(int16 yPos)1266 int16 GfxMgr::saveLoadGetPriority(int16 yPos) {
1267 	assert(yPos < SCRIPT_HEIGHT);
1268 	return _priorityTable[yPos];
1269 }
saveLoadWasPriorityTableModified()1270 bool GfxMgr::saveLoadWasPriorityTableModified() {
1271 	return _priorityTableSet;
1272 }
1273 
1274 // used for restoring
saveLoadSetPriority(int16 yPos,int16 priority)1275 void GfxMgr::saveLoadSetPriority(int16 yPos, int16 priority) {
1276 	assert(yPos < SCRIPT_HEIGHT);
1277 	_priorityTable[yPos] = priority;
1278 }
saveLoadSetPriorityTableModifiedBool(bool wasModified)1279 void GfxMgr::saveLoadSetPriorityTableModifiedBool(bool wasModified) {
1280 	_priorityTableSet = wasModified;
1281 }
saveLoadFigureOutPriorityTableModifiedBool()1282 void GfxMgr::saveLoadFigureOutPriorityTableModifiedBool() {
1283 	uint8 defaultPriorityTable[SCRIPT_HEIGHT]; /**< priority table */
1284 
1285 	createDefaultPriorityTable(defaultPriorityTable);
1286 
1287 	if (memcmp(defaultPriorityTable, _priorityTable, sizeof(_priorityTable)) == 0) {
1288 		// Match, it is the default table, so reset the flag
1289 		_priorityTableSet = false;
1290 	} else {
1291 		_priorityTableSet = true;
1292 	}
1293 }
1294 
1295 /**
1296  * Convert sprite priority to y value.
1297  */
priorityToY(int16 priority)1298 int16 GfxMgr::priorityToY(int16 priority) {
1299 	int16 currentY;
1300 
1301 	if (!_priorityTableSet) {
1302 		// priority table wasn't set by scripts? calculate directly
1303 		return (priority - 5) * 12 + 48;
1304 	}
1305 
1306 	// Dynamic priority bands were introduced in 2.425, but removed again until 2.936 (effectively last version of AGI2)
1307 	// They are available from 2.936 onwards.
1308 	// It seems there was a glitch, that caused priority bands to not get calculated properly.
1309 	// It was caused by this function starting with Y = 168 instead of 167, which meant it always
1310 	// returned with 168 as result.
1311 	// This glitch is required in King's Quest 4 2.0, otherwise in room 54 ego will get drawn over
1312 	//  the last dwarf, that enters the house.
1313 	//  Dwarf is screen object 13 (view 152), gets fixed priority of 8, which would normally
1314 	//  result in a Y of 101. Ego is priority (non-fixed) 8, which would mean that dwarf is
1315 	//  drawn first, followed by ego, which would then draw ego over the dwarf.
1316 	//  For more information see bug #3182 (dwarf sprite priority)
1317 	//
1318 	// This glitch is definitely present in 2.425, 2.936 and 3.002.086.
1319 	//
1320 	// Priority bands were working properly in: 3.001.098 (Black Cauldron)
1321 	uint16 agiVersion = _vm->getVersion();
1322 
1323 	if (agiVersion <= 0x3086) {
1324 		return 168; // Buggy behavior, see above
1325 	}
1326 
1327 	currentY = 167;
1328 	while (_priorityTable[currentY] >= priority) {
1329 		currentY--;
1330 		if (currentY < 0) // Original AGI didn't do this, we abort in that case and return -1
1331 			break;
1332 	}
1333 	return currentY;
1334 }
1335 
priorityFromY(int16 yPos)1336 int16 GfxMgr::priorityFromY(int16 yPos) {
1337 	assert(yPos < SCRIPT_HEIGHT);
1338 	return _priorityTable[yPos];
1339 }
1340 
1341 
1342 /**
1343  * Initialize the color palette
1344  * This function initializes the color palette using the specified
1345  * RGB palette.
1346  * @param p           A pointer to the source RGB palette.
1347  * @param colorCount  Count of colors in the source palette.
1348  * @param fromBits    Bits per source color component.
1349  * @param toBits      Bits per destination color component.
1350  */
initPalette(uint8 * destPalette,const uint8 * paletteData,uint colorCount,uint fromBits,uint toBits)1351 void GfxMgr::initPalette(uint8 *destPalette, const uint8 *paletteData, uint colorCount, uint fromBits, uint toBits) {
1352 	const uint srcMax  = (1 << fromBits) - 1;
1353 	const uint destMax = (1 << toBits) - 1;
1354 	for (uint colorNr = 0; colorNr < colorCount; colorNr++) {
1355 		for (uint componentNr = 0; componentNr < 3; componentNr++) { // Convert RGB components
1356 			destPalette[colorNr * 3 + componentNr] = (paletteData[colorNr * 3 + componentNr] * destMax) / srcMax;
1357 		}
1358 	}
1359 }
1360 
1361 // Converts CLUT data to a palette, that we can use
initPaletteCLUT(uint8 * destPalette,const uint16 * paletteCLUTData,uint colorCount)1362 void GfxMgr::initPaletteCLUT(uint8 *destPalette, const uint16 *paletteCLUTData, uint colorCount) {
1363 	for (uint colorNr = 0; colorNr < colorCount; colorNr++) {
1364 		for (uint componentNr = 0; componentNr < 3; componentNr++) { // RGB component
1365 			byte component = (paletteCLUTData[colorNr * 3 + componentNr] >> 8);
1366 			// Adjust gamma (1.8 to 2.2)
1367 			component = (byte)(255 * pow(component / 255.0f, 0.8181f));
1368 			destPalette[colorNr * 3 + componentNr] = component;
1369 		}
1370 	}
1371 }
1372 
setPalette(bool gfxModePalette)1373 void GfxMgr::setPalette(bool gfxModePalette) {
1374 	if (gfxModePalette) {
1375 		g_system->getPaletteManager()->setPalette(_paletteGfxMode, 0, 256);
1376 	} else {
1377 		g_system->getPaletteManager()->setPalette(_paletteTextMode, 0, 256);
1378 	}
1379 }
1380 
1381 //Gets AGIPAL Data
setAGIPal(int p0)1382 void GfxMgr::setAGIPal(int p0) {
1383 	//If 0 from savefile, do not use
1384 	if (p0 == 0)
1385 		return;
1386 
1387 	char filename[15];
1388 	sprintf(filename, "pal.%d", p0);
1389 
1390 	Common::File agipal;
1391 	if (!agipal.open(filename)) {
1392 		warning("Couldn't open AGIPAL palette file '%s'. Not changing palette", filename);
1393 		return; // Needed at least by Naturette 3 which uses AGIPAL but provides no palette files
1394 	}
1395 
1396 	//Chunk0 holds colors 0-7
1397 	agipal.read(&_agipalPalette[0], 24);
1398 
1399 	//Chunk1 is the same as the chunk0
1400 
1401 	//Chunk2 chunk holds colors 8-15
1402 	agipal.seek(24, SEEK_CUR);
1403 	agipal.read(&_agipalPalette[24], 24);
1404 
1405 	//Chunk3 is the same as the chunk2
1406 
1407 	//Chunks4-7 are duplicates of chunks0-3
1408 
1409 	if (agipal.eos() || agipal.err()) {
1410 		warning("Couldn't read AGIPAL palette from '%s'. Not changing palette", filename);
1411 		return;
1412 	}
1413 
1414 	// Use only the lowest 6 bits of each color component (Red, Green and Blue)
1415 	// because VGA used only 6 bits per color component (i.e. VGA had 18-bit colors).
1416 	// This should now be identical to the original AGIPAL-hack's behavior.
1417 	bool validVgaPalette = true;
1418 	for (int i = 0; i < 16 * 3; i++) {
1419 		if (_agipalPalette[i] >= (1 << 6)) {
1420 			_agipalPalette[i] &= 0x3F; // Leave only the lowest 6 bits of each color component
1421 			validVgaPalette = false;
1422 		}
1423 	}
1424 
1425 	if (!validVgaPalette)
1426 		warning("Invalid AGIPAL palette (Over 6 bits per color component) in '%s'. Using only the lowest 6 bits per color component", filename);
1427 
1428 	_agipalFileNum = p0;
1429 
1430 	initPalette(_paletteGfxMode, _agipalPalette);
1431 	setPalette(true); // set gfx-mode palette
1432 
1433 	debug(1, "Using AGIPAL palette from '%s'", filename);
1434 }
1435 
getAGIPalFileNum()1436 int GfxMgr::getAGIPalFileNum() {
1437 	return _agipalFileNum;
1438 }
1439 
initMouseCursor(MouseCursorData * mouseCursor,const byte * bitmapData,uint16 width,uint16 height,int hotspotX,int hotspotY)1440 void GfxMgr::initMouseCursor(MouseCursorData *mouseCursor, const byte *bitmapData, uint16 width, uint16 height, int hotspotX, int hotspotY) {
1441 	switch (_upscaledHires) {
1442 	case DISPLAY_UPSCALED_DISABLED:
1443 		mouseCursor->bitmapData = bitmapData;
1444 		break;
1445 	case DISPLAY_UPSCALED_640x400: {
1446 		mouseCursor->bitmapDataAllocated = (byte *)malloc(width * height * 4);
1447 		mouseCursor->bitmapData = mouseCursor->bitmapDataAllocated;
1448 
1449 		// Upscale mouse cursor
1450 		byte *upscaledData = mouseCursor->bitmapDataAllocated;
1451 
1452 		for (uint16 y = 0; y < height; y++) {
1453 			for (uint16 x = 0; x < width; x++) {
1454 				byte curColor = *bitmapData++;
1455 				upscaledData[x * 2 + 0] = curColor;
1456 				upscaledData[x * 2 + 1] = curColor;
1457 				upscaledData[x * 2 + (width * 2) + 0] = curColor;
1458 				upscaledData[x * 2 + (width * 2) + 1] = curColor;
1459 			}
1460 			upscaledData += width * 2 * 2;
1461 		}
1462 
1463 		width *= 2;
1464 		height *= 2;
1465 		hotspotX *= 2;
1466 		hotspotY *= 2;
1467 		break;
1468 	}
1469 	default:
1470 		assert(0);
1471 		break;
1472 	}
1473 	mouseCursor->width = width;
1474 	mouseCursor->height = height;
1475 	mouseCursor->hotspotX = hotspotX;
1476 	mouseCursor->hotspotY = hotspotY;
1477 }
1478 
setMouseCursor(bool busy)1479 void GfxMgr::setMouseCursor(bool busy) {
1480 	MouseCursorData *mouseCursor = nullptr;
1481 
1482 	if (!busy) {
1483 		mouseCursor = &_mouseCursor;
1484 	} else {
1485 		mouseCursor = &_mouseCursorBusy;
1486 	}
1487 
1488 	if (mouseCursor) {
1489 		CursorMan.replaceCursor(mouseCursor->bitmapData, mouseCursor->width, mouseCursor->height, mouseCursor->hotspotX, mouseCursor->hotspotY, 0);
1490 	}
1491 }
1492 
1493 #if 0
1494 void GfxMgr::setCursor(bool amigaStyleCursor, bool busy) {
1495 	if (busy) {
1496 		CursorMan.replaceCursorPalette(MOUSECURSOR_AMIGA_PALETTE, 1, ARRAYSIZE(MOUSECURSOR_AMIGA_PALETTE) / 3);
1497 		CursorMan.replaceCursor(MOUSECURSOR_AMIGA_BUSY, 13, 16, 7, 8, 0);
1498 		return;
1499 	}
1500 
1501 	if (!amigaStyleCursor) {
1502 		CursorMan.replaceCursorPalette(sciMouseCursorPalette, 1, ARRAYSIZE(sciMouseCursorPalette) / 3);
1503 		CursorMan.replaceCursor(sciMouseCursor, 11, 16, 1, 1, 0);
1504 	} else { // amigaStyleCursor
1505 		CursorMan.replaceCursorPalette(amigaMouseCursorPalette, 1, ARRAYSIZE(amigaMouseCursorPalette) / 3);
1506 		CursorMan.replaceCursor(amigaMouseCursor, 8, 11, 1, 1, 0);
1507 	}
1508 }
1509 
1510 void GfxMgr::setCursorPalette(bool amigaStyleCursor) {
1511 	if (!amigaStyleCursor) {
1512 		if (_currentCursorPalette != 1) {
1513 			CursorMan.replaceCursorPalette(sciMouseCursorPalette, 1, ARRAYSIZE(sciMouseCursorPalette) / 3);
1514 			_currentCursorPalette = 1;
1515 		}
1516 	} else { // amigaStyleCursor
1517 		if (_currentCursorPalette != 2) {
1518 			CursorMan.replaceCursorPalette(amigaMouseCursorPalette, 1, ARRAYSIZE(amigaMouseCursorPalette) / 3);
1519 			_currentCursorPalette = 2;
1520 		}
1521 	}
1522 }
1523 #endif
1524 
1525 } // End of namespace Agi
1526