/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "common/system.h"
#include "common/macresman.h"
#include "common/util.h"
#include "graphics/cursorman.h"
#include "graphics/maccursor.h"
#ifdef ENABLE_HE
#include "graphics/wincursor.h"
#endif
#include "scumm/bomp.h"
#include "scumm/charset.h"
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/he/resource_he.h"
#include "scumm/scumm.h"
#include "scumm/scumm_v2.h"
#include "scumm/scumm_v5.h"

namespace Scumm {

/*
 * Mouse cursor cycle colors (for the default crosshair).
 */
static const byte default_v1_cursor_colors[4] = {
	1, 1, 12, 11
};

static const uint16 default_pce_cursor_colors[4] = {
	0x01FF, 0x01FF, 0x016D, 0x0092
};

static const byte default_cursor_colors[4] = {
	15, 15, 7, 8
};

// 2bpp
static const uint16 default_he_cursor[128] = {
	0x0a80, 0x0000, 0x0000, 0x0000, 0x2568, 0x0000, 0x0000, 0x0000,
	0x9556, 0x8000, 0x0000, 0x0000, 0x9955, 0x6800, 0x0000, 0x0000,
	0x9955, 0x5680, 0x0000, 0x0000, 0x2655, 0x5568, 0x0000, 0x0000,
	0x2695, 0x5556, 0x8000, 0x0000, 0x0995, 0x5555, 0x6800, 0x0000,
	0x09a5, 0x5555, 0x5600, 0x0000, 0x0269, 0x5555, 0x5580, 0x0000,
	0x0269, 0x5555, 0x5560, 0x0000, 0x009a, 0x5555, 0x5560, 0x0000,
	0x009a, 0x9555, 0x5580, 0x0000, 0x0026, 0x9555, 0x5600, 0x0000,
	0x0026, 0xa555, 0x5580, 0x0000, 0x0009, 0xa955, 0x5560, 0x0000,
	0x0009, 0xa9a9, 0x5558, 0x0000, 0x0002, 0x6aaa, 0x5558, 0x0000,
	0x0002, 0x6a9a, 0xaa58, 0x0000, 0x0000, 0x9a66, 0xaa60, 0x0000,
	0x0000, 0x2589, 0xa980, 0x0000, 0x0000, 0x0a02, 0x5600, 0x0000,
	0x0000, 0x0000, 0xa800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
};

static const byte default_v6_cursor[] = {
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
	0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x0F,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
	0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, 0x0F,0x0F,0x0F, 0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0xFF,
	0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x0F,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};

void ScummEngine_v5::animateCursor() {
	if (_cursor.animate) {
		if (!(_cursor.animateIndex & 0x1)) {
			setBuiltinCursor((_cursor.animateIndex >> 1) & 3);
		}
		_cursor.animateIndex++;
	}
}

void ScummEngine_v6::setCursorHotspot(int x, int y) {
	_cursor.hotspotX = x;
	_cursor.hotspotY = y;
}

void ScummEngine_v6::setCursorTransparency(int a) {
	int i, size;

	size = _cursor.width * _cursor.height;

	for (i = 0; i < size; i++)
		if (_grabbedCursor[i] == (byte)a)
			_grabbedCursor[i] = 0xFF;

	updateCursor();
}

void ScummEngine::updateCursor() {
	int transColor = (_game.heversion >= 80) ? 5 : 255;
#ifdef USE_RGB_COLOR
	Graphics::PixelFormat format = _system->getScreenFormat();
	CursorMan.replaceCursor(_grabbedCursor, _cursor.width, _cursor.height,
							_cursor.hotspotX, _cursor.hotspotY,
							(_game.platform == Common::kPlatformNES ? _grabbedCursor[63] : transColor),
							(_game.heversion == 70 ? true : false),
							&format);
#else
	CursorMan.replaceCursor(_grabbedCursor, _cursor.width, _cursor.height,
							_cursor.hotspotX, _cursor.hotspotY,
							(_game.platform == Common::kPlatformNES ? _grabbedCursor[63] : transColor),
							(_game.heversion == 70 ? true : false));
#endif
}

void ScummEngine_v6::grabCursor(int x, int y, int w, int h) {
	VirtScreen *vs = findVirtScreen(y);

	if (vs == nullptr) {
		debug(0, "grabCursor: invalid Y %d", y);
		return;
	}

	setCursorFromBuffer((byte *)vs->getBasePtr(x, y - vs->topline), w, h, vs->pitch);
}

void ScummEngine_v6::setDefaultCursor() {
	setCursorHotspot(7, 6);
	setCursorFromBuffer(default_v6_cursor, 16, 13, 16);
}

void ScummEngine::setCursorFromBuffer(const byte *ptr, int width, int height, int pitch) {
	uint size;
	byte *dst;

	size = width * height * _bytesPerPixel;
	if (size > sizeof(_grabbedCursor))
		error("grabCursor: grabbed cursor too big");

	_cursor.width = width;
	_cursor.height = height;
	_cursor.animate = 0;

	dst = _grabbedCursor;
	for (; height; height--) {
		memcpy(dst, ptr, width * _bytesPerPixel);
		dst += width * _bytesPerPixel;
		ptr += pitch;
	}

	updateCursor();
}

void ScummEngine_v70he::setCursorFromImg(uint img, uint room, uint imgindex) {
	_resExtractor->setCursor(img);
}

void ScummEngine_v70he::setDefaultCursor() {
	const uint16 *src;
	int i, j;
	static const byte palette[] = {0,    0,    0,
								   0xff, 0xff, 0xff,
								   0,    0,    0,    };


	memset(_grabbedCursor, 5, sizeof(_grabbedCursor));

	_cursor.hotspotX = _cursor.hotspotY = 2;
	src = default_he_cursor;

	_cursor.width = 32;
	_cursor.height = 32;

	for (i = 0; i < 32; i++) {
		uint p = *src;
		for (j = 0; j < 32; j++) {
			switch ((p & (0x3 << 14)) >> 14) {
				case 1:
					_grabbedCursor[32 * i + j] = 0xfe;
					break;
				case 2:
					_grabbedCursor[32 * i + j] = 0xfd;
					break;
				default:
					break;
			}
			p <<= 2;

			if (j == 31)
				++src;
			else if ((j + 1) % 8 == 0)
				p = *(++src);
		}
	}

	// Since white color position is not guaranteed
	// we setup our own palette if supported by backend
	CursorMan.disableCursorPalette(false);
	CursorMan.replaceCursorPalette(palette, 0xfd, 3);

	updateCursor();
}

#ifdef ENABLE_HE
void ScummEngine_v80he::setDefaultCursor() {
	// v80+ games use the default Windows cursor instead of the usual
	// default HE cursor.
	Graphics::Cursor *cursor = Graphics::makeDefaultWinCursor();

	// Clear the cursor
	if (_bytesPerPixel == 2) {
		for (int i = 0; i < 1024; i++)
			WRITE_UINT16(_grabbedCursor + i * 2, 5);
	} else {
		memset(_grabbedCursor, 5, sizeof(_grabbedCursor));
	}

	_cursor.width = cursor->getWidth();
	_cursor.height = cursor->getHeight();
	_cursor.hotspotX = cursor->getHotspotX();
	_cursor.hotspotY = cursor->getHotspotY();

	const byte *surface = cursor->getSurface();
	const byte *palette = cursor->getPalette();

	for (uint16 y = 0; y < _cursor.height; y++) {
		for (uint16 x = 0; x < _cursor.width; x++) {
			byte pixel = *surface++;

			if (pixel != cursor->getKeyColor()) {
				pixel -= cursor->getPaletteStartIndex();

				if (_bytesPerPixel == 2)
					WRITE_UINT16(_grabbedCursor + (y * _cursor.width + x) * 2, get16BitColor(palette[pixel * 3], palette[pixel * 3 + 1], palette[pixel * 3 + 2]));
				else
					_grabbedCursor[y * _cursor.width + x] = (pixel == 0) ? 0xfd : 0xfe;
			}
		}
	}

	if (_bytesPerPixel == 1) {
		// Since white color position is not guaranteed
		// we setup our own palette if supported by backend
		CursorMan.disableCursorPalette(false);
		CursorMan.replaceCursorPalette(palette, 0xfd, cursor->getPaletteCount());
	}

	delete cursor;

	updateCursor();
}
#endif

void ScummEngine_v6::setCursorFromImg(uint img, uint room, uint imgindex) {
	int w, h;
	const byte *dataptr, *bomp;
	uint32 size;
	FindObjectInRoom foir;
	const ImageHeader *imhd;

	if (room == (uint) - 1)
		room = getObjectRoom(img);

	findObjectInRoom(&foir, foCodeHeader | foImageHeader | foCheckAlreadyLoaded, img, room);
	imhd = (const ImageHeader *)findResourceData(MKTAG('I','M','H','D'), foir.obim);

	if (_game.version == 8) {
		setCursorHotspot(READ_LE_UINT32(&imhd->v8.hotspot[0].x),
		                  READ_LE_UINT32(&imhd->v8.hotspot[0].y));
		w = READ_LE_UINT32(&imhd->v8.width) / 8;
		h = READ_LE_UINT32(&imhd->v8.height) / 8;
	} else if (_game.version == 7) {
		setCursorHotspot(READ_LE_UINT16(&imhd->v7.hotspot[0].x),
		                  READ_LE_UINT16(&imhd->v7.hotspot[0].y));
		w = READ_LE_UINT16(&imhd->v7.width) / 8;
		h = READ_LE_UINT16(&imhd->v7.height) / 8;
	} else {
		if (_game.heversion == 0) {
			setCursorHotspot(READ_LE_UINT16(&imhd->old.hotspot[0].x),
			                 READ_LE_UINT16(&imhd->old.hotspot[0].y));
		}
		w = READ_LE_UINT16(&foir.cdhd->v6.w) / 8;
		h = READ_LE_UINT16(&foir.cdhd->v6.h) / 8;
	}

	dataptr = getObjectImage(foir.obim, imgindex);
	assert(dataptr);
	if (_game.version == 8) {
		bomp = dataptr;
	} else {
		size = READ_BE_UINT32(dataptr + 4);
		if (size > sizeof(_grabbedCursor))
			error("setCursorFromImg: Cursor image too large");

		bomp = findResource(MKTAG('B','O','M','P'), dataptr);
	}

	if (bomp != nullptr)
		useBompCursor(bomp, w, h);
	else
		useIm01Cursor(dataptr, w, h);
}

void ScummEngine_v6::useIm01Cursor(const byte *im, int w, int h) {
	VirtScreen *vs = &_virtscr[kMainVirtScreen];
	byte *buf, *dst;
	const byte *src;
	int i;

	w *= 8;
	h *= 8;

	// Backup the screen content
	dst = buf = (byte *)malloc(w * h);
	src = vs->getPixels(0, 0);

	for (i = 0; i < h; i++) {
		memcpy(dst, src, w);
		dst += w;
		src += vs->pitch;
	}

	// Do some drawing
	drawBox(0, 0, w - 1, h - 1, 0xFF);

	vs->hasTwoBuffers = false;
	_gdi->disableZBuffer();
	_gdi->drawBitmap(im, vs, _screenStartStrip, 0, w, h, 0, w / 8, 0);
	vs->hasTwoBuffers = true;
	_gdi->enableZBuffer();

	// Grab the data we just drew and setup the cursor with it
	setCursorFromBuffer(vs->getPixels(0, 0), w, h, vs->pitch);

	// Restore the screen content
	src = buf;
	dst = vs->getPixels(0, 0);

	for (i = 0; i < h; i++) {
		memcpy(dst, src, w);
		dst += vs->pitch;
		src += w;
	}

	free(buf);
}

void ScummEngine_v6::useBompCursor(const byte *im, int width, int height) {
	uint size;

	width *= 8;
	height *= 8;

	size = width * height;
	if (size > sizeof(_grabbedCursor))
		error("useBompCursor: cursor too big (%d)", size);

	_cursor.width = width;
	_cursor.height = height;
	_cursor.animate = 0;

	// Skip the header
	if (_game.version == 8) {
		im += 16;
	} else {
		im += 18;
	}
	decompressBomp(_grabbedCursor, im, width, height);

	updateCursor();
}

void ScummEngine_v5::redefineBuiltinCursorFromChar(int index, int chr) {
	if (!_macCursorFile.empty())
		return;

	// Cursor image in both Loom versions are based on images from charset.
	// This function is *only* supported for Loom!
	assert(_game.id == GID_LOOM);

	assert(index >= 0 && index < 4);

//	const int oldID = _charset->getCurID();

	uint16 *ptr = _cursorImages[index];
	int h;

	if (index == 1 && _game.platform == Common::kPlatformPCEngine) {
		uint16 cursorPCE[] = {
			0x8000, 0xC000, 0xE000, 0xF000, 0xF800, 0xFC00, 0xFE00, 0xFF00,
			0xF180, 0xF800, 0x8C00, 0x0C00, 0x0600, 0x0600, 0x0300, 0x0000
		};

		for (h = 0; h < ARRAYSIZE(cursorPCE); h++) {
			*ptr++ = cursorPCE[h];
		}
	} else {
		if (_game.version == 3) {
			_charset->setCurID(0);
		} else if (_game.version >= 4) {
			_charset->setCurID(1);
		}

		Graphics::Surface s;
		byte buf[16*17];
		memset(buf, 123, 16*17);
		// For correct rendering, we need to use original (non-CJK) fonts
		bool oldCJKMode = _useCJKMode;
		_useCJKMode = false;
		s.init(_charset->getCharWidth(chr), _charset->getFontHeight(),
		       _charset->getCharWidth(chr), buf,
		       Graphics::PixelFormat::createFormatCLUT8());
		// s.h = 17 for FM-TOWNS Loom Japanese. Fixes bug #1964
		assert(s.w <= 16 && s.h <= 17);

		_charset->drawChar(chr, s, 0, 0);
		_useCJKMode = oldCJKMode;

		memset(ptr, 0, 17 * sizeof(uint16));
		for (h = 0; h < s.h; h++) {
			for (int w = 0; w < s.w; w++) {
				if (buf[s.pitch * h + w] != 123)
					*ptr |= 1 << (15 - w);
			}
			ptr++;
		}
	}

//	_charset->setCurID(oldID);
}

void ScummEngine_v5::redefineBuiltinCursorHotspot(int index, int x, int y) {
	// Cursor image in both Looms are based on images from charset.
	// This function is *only* supported for Loom!
	assert(_game.id == GID_LOOM);

	assert(index >= 0 && index < 4);

	_cursorHotspots[index * 2] = x;
	_cursorHotspots[index * 2 + 1] = y;
}

void ScummEngine_v2::setBuiltinCursor(int idx) {
	int i, j;
	byte color;

	memset(_grabbedCursor, 0xFF, sizeof(_grabbedCursor));

	if (_game.version <= 1)
		color = default_v1_cursor_colors[idx];
	else
		color = default_cursor_colors[idx];

	if (_game.platform == Common::kPlatformNES) {
		_cursor.width = 8;
		_cursor.height = 8;
		_cursor.hotspotX = 0;
		_cursor.hotspotY = 0;

		byte *dst = _grabbedCursor;
		byte *src = &_NESPatTable[0][0xfa * 16];
		byte *palette = _NESPalette[1];

		for (i = 0; i < 8; i++) {
			byte c0 = src[i];
			byte c1 = src[i + 8];
			for (j = 0; j < 8; j++)
				*dst++ = palette[((c0 >> (7 - j)) & 1) | (((c1 >> (7 - j)) & 1) << 1) | ((idx == 3) ? 4 : 0)];
		}

	} else if (_game.platform == Common::kPlatformAmiga) {
		_cursor.width = 15;
		_cursor.height = 15;
		_cursor.hotspotX = 7;
		_cursor.hotspotY = 7;

		byte *hotspot = _grabbedCursor + _cursor.hotspotY * _cursor.width + _cursor.hotspotX;

		// Crosshair, symmetric
		// TODO: Instead of setting this up via code, we should simply extend
		//       default_cursor_images to contain this shape.
		for (i = 0; i < 5; i++) {
			*(hotspot - 3 - i) = color;
			*(hotspot + 3 + i) = color;
			*(hotspot - _cursor.width * (3 + i)) = color;
			*(hotspot + _cursor.width * (3 + i)) = color;
		}

		// Arrow heads, diagonal lines
		for (i = 1; i <= 2; i++) {
			*(hotspot - _cursor.width * i - (3 + i)) = color;
			*(hotspot + _cursor.width * i - (3 + i)) = color;
			*(hotspot - _cursor.width * i + (3 + i)) = color;
			*(hotspot + _cursor.width * i + (3 + i)) = color;
			*(hotspot - _cursor.width * (3 + i) - i) = color;
			*(hotspot + _cursor.width * (3 + i) - i) = color;
			*(hotspot - _cursor.width * (3 + i) + i) = color;
			*(hotspot + _cursor.width * (3 + i) + i) = color;
		}
	} else {
		_cursor.width = 23;
		_cursor.height = 21;
		_cursor.hotspotX = 11;
		_cursor.hotspotY = 10;

		byte *hotspot = _grabbedCursor + _cursor.hotspotY * _cursor.width + _cursor.hotspotX;

		// Crosshair, slightly assymetric
		// TODO: Instead of setting this up via code, we should simply extend
		//       default_cursor_images to contain this shape.

		for (i = 0; i < 7; i++) {
			*(hotspot - 5 - i) = color;
			*(hotspot + 5 + i) = color;
		}

		for (i = 0; i < 8; i++) {
			*(hotspot - _cursor.width * (3 + i)) = color;
			*(hotspot + _cursor.width * (3 + i)) = color;
		}

		// Arrow heads, diagonal lines

		for (i = 1; i <= 3; i++) {
			*(hotspot - _cursor.width * i - 5 - i) = color;
			*(hotspot + _cursor.width * i - 5 - i) = color;
			*(hotspot - _cursor.width * i + 5 + i) = color;
			*(hotspot + _cursor.width * i + 5 + i) = color;
			*(hotspot - _cursor.width * (i + 3) - i) = color;
			*(hotspot - _cursor.width * (i + 3) + i) = color;
			*(hotspot + _cursor.width * (i + 3) - i) = color;
			*(hotspot + _cursor.width * (i + 3) + i) = color;
		}

		// Final touches

		*(hotspot - _cursor.width - 7) = color;
		*(hotspot - _cursor.width + 7) = color;
		*(hotspot + _cursor.width - 7) = color;
		*(hotspot + _cursor.width + 7) = color;
		*(hotspot - (_cursor.width * 5) - 1) = color;
		*(hotspot - (_cursor.width * 5) + 1) = color;
		*(hotspot + (_cursor.width * 5) - 1) = color;
		*(hotspot + (_cursor.width * 5) + 1) = color;
	}

	updateCursor();
}

void ScummEngine_v5::resetCursors() {
	static const uint16 default_cursor_images[4][16] = {
		/* cross-hair */
		{ 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0000, 0x7e3f,
		  0x0000, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0000 },
		/* hourglass */
		{ 0x0000, 0x7ffe, 0x6006, 0x300c, 0x1818, 0x0c30, 0x0660, 0x03c0,
		  0x0660, 0x0c30, 0x1998, 0x33cc, 0x67e6, 0x7ffe, 0x0000, 0x0000 },
		/* arrow */
		{ 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x7f00,
		  0x7f80, 0x78c0, 0x7c00, 0x4600, 0x0600, 0x0300, 0x0300, 0x0180 },
		/* hand */
		{ 0x1e00, 0x1200, 0x1200, 0x1200, 0x1200, 0x13ff, 0x1249, 0x1249,
		  0xf249, 0x9001, 0x9001, 0x9001, 0x8001, 0x8001, 0x8001, 0xffff },
	};

	static const byte default_cursor_hotspots[10] = {
		8, 7,   8, 7,   1, 1,   5, 0,
		8, 7, //zak256
	};


	for (int i = 0; i < 4; i++) {
		memcpy(_cursorImages[i], default_cursor_images[i], 32);
	}
	memcpy(_cursorHotspots, default_cursor_hotspots, 8);

}

void ScummEngine_v5::setBuiltinCursor(int idx) {
	if (!_macCursorFile.empty()) {
		Common::MacResManager resource;
		if (resource.open(_macCursorFile)) {
			Common::MacResIDArray resArray = resource.getResIDArray(MKTAG('C', 'U', 'R', 'S'));
			Common::SeekableReadStream *curs = resource.getResource(MKTAG('C', 'U', 'R', 'S'), resArray[0]);
			Graphics::MacCursor macCursor;
			if (macCursor.readFromStream(*curs)) {
				_cursor.animate = 0;
				CursorMan.replaceCursor(&macCursor);
				delete curs;
				return;
			}
			delete curs;
		}
	}

	if (_game.id == GID_INDY3 && _macScreen) {
		const byte buf[15 * 15] = {
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0xFF, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
		};

		_cursor.animate = 0;
		CursorMan.replaceCursor(buf, 15, 15, 7, 7, 0xFF);
		return;
	}

	int i, j;
	uint16 color;
	const uint16 *src = _cursorImages[_currentCursor];

	if (_outputPixelFormat.bytesPerPixel == 2) {
		if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
			byte r, g, b;
			colorPCEToRGB(default_pce_cursor_colors[idx], &r, &g, &b);
			color = get16BitColor(r, g, b);
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
		} else if (_game.platform == Common::kPlatformFMTowns) {
			byte *palEntry = &_textPalette[default_cursor_colors[idx] * 3];
			color = get16BitColor(palEntry[0], palEntry[1], palEntry[2]);
#endif
		} else {
			color = _16BitPalette[default_cursor_colors[idx]];
		}

		for (i = 0; i < 1024; i++)
			WRITE_UINT16(_grabbedCursor + i * 2, 0xFF);
	} else {
		// Indy4 Amiga uses its own color set for the cursor image.
		// This is patchwork code to make the cursor flash in correct colors.
		if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) {
			static const uint8 indy4AmigaColors[4] = {
				252, 252, 253, 254
			};
			color = indy4AmigaColors[idx];
		} else {
			color = default_cursor_colors[idx];
		}
		memset(_grabbedCursor, 0xFF, sizeof(_grabbedCursor));
	}

	_cursor.hotspotX = _cursorHotspots[2 * _currentCursor] * _textSurfaceMultiplier;
	_cursor.hotspotY = _cursorHotspots[2 * _currentCursor + 1] * _textSurfaceMultiplier;
	_cursor.width = 16 * _textSurfaceMultiplier;
	_cursor.height = 16 * _textSurfaceMultiplier;

	int scl = _outputPixelFormat.bytesPerPixel * _textSurfaceMultiplier;

	for (i = 0; i < 16; i++) {
		for (j = 0; j < 16; j++) {
			if (src[i] & (1 << j)) {
				byte *dst1 = _grabbedCursor + 16 * scl * i * _textSurfaceMultiplier + (15 - j) * scl;
				byte *dst2 = (_textSurfaceMultiplier == 2) ? dst1 + 16 * scl : dst1;
				if (_outputPixelFormat.bytesPerPixel == 2) {
					for (int b = 0; b < scl; b += 2) {
						*((uint16 *)dst1) = *((uint16 *)dst2) = color;
						dst1 += 2;
						dst2 += 2;
					}
				} else {
					for (int b = 0; b < scl; b++)
						*dst1++ = *dst2++ = color;
				}
			}
		}
	}

	updateCursor();
}

} // End of namespace Scumm
