gfx.c
author tron
Thu, 27 Jan 2005 12:52:20 +0000
changeset 1192 2649bd4e556a
parent 1130 2f1b7c54a12d
child 1309 4403a69da4f8
permissions -rw-r--r--
(svn r1696) Use GetTileSlope() instead of FindLandscapeHeightByTile() where it is sufficient. FindLandscapeHeightByTile() uses GetTileSlope() internally and adds some more info, which is discarded in these cases.
While touching the code make a bit more clear how GetBridgeHeight() works.
#include "stdafx.h"
#include "ttd.h"
#include "gfx.h"
#include "table/palettes.h"
#include "hal.h"

static void GfxMainBlitter(byte *sprite, int x, int y, int mode);

static int _stringwidth_out;
static byte _cursor_backup[64*64];
static Rect _invalid_rect;
static byte *_color_remap_ptr;
static byte _string_colorremap[3];

#define DIRTY_BYTES_PER_LINE (MAX_SCREEN_WIDTH/64)
static byte _dirty_blocks[DIRTY_BYTES_PER_LINE * MAX_SCREEN_HEIGHT / 8];



void memcpy_pitch(void *d, void *s, int w, int h, int spitch, int dpitch)
{
	byte *dp = (byte*)d;
	byte *sp = (byte*)s;

	assert(h >= 0);
	for (; h != 0; --h) {
		memcpy(dp, sp, w);
		dp += dpitch;
		sp += spitch;
	}
}


void GfxScroll(int left, int top, int width, int height, int xo, int yo) {
	byte *src, *dst;
	int p;
	int ht;

	if (xo == 0 && yo == 0)
		return;

	if (_cursor.visible)
		UndrawMouseCursor();
	UndrawTextMessage();

	p = _screen.pitch;

	if (yo > 0) {
		// Calculate pointers
		dst = _screen.dst_ptr + (top + height - 1) * p + left;
		src = dst - yo * p;

		// Decrease height and increase top
		top += yo;
		height -= yo;
		assert(height > 0);

		// Adjust left & width
		if (xo >= 0) {
			dst += xo;
			left += xo;
			width -= xo;
		} else {
			src -= xo;
			width += xo;
		}

		for (ht = height; ht > 0; --ht) {
			memcpy(dst, src, width);
			src -= p;
			dst -= p;
		}
	} else {
		// Calculate pointers
		dst = _screen.dst_ptr + top * p + left;
		src = dst - yo * p;

		// Decrese height. (yo is <=0).
		height += yo;
		assert(height > 0);

		// Adjust left & width
		if (xo >= 0) {
			dst += xo;
			left += xo;
			width -= xo;
		} else {
			src -= xo;
			width += xo;
		}

		// the y-displacement may be 0 therefore we have to use memmove,
		// because source and destination may overlap
		for (ht = height; ht > 0; --ht) {
			memmove(dst, src, width);
			src += p;
			dst += p;
		}
	}
	// This part of the screen is now dirty.
	_video_driver->make_dirty(left, top, width, height);
}


void GfxFillRect(int left, int top, int right, int bottom, int color) {
	DrawPixelInfo *dpi = _cur_dpi;
	byte *dst,*ctab;
	const int otop = top;
	const int oleft = left;

	if (dpi->zoom != 0)
		return;

	if (left > right || top > bottom)
		return;

	if (right < dpi->left || left >= dpi->left + dpi->width)
		return;

	if (bottom < dpi->top || top >= dpi->top + dpi->height)
		return;

	if ( (left -= dpi->left) < 0) left = 0;
	right = right - dpi->left + 1;
	if (right > dpi->width) right=dpi->width;
	right -= left;
	assert(right > 0);

	if ( (top -= dpi->top) < 0) top = 0;
	bottom = bottom - dpi->top + 1;
	if (bottom > dpi->height) bottom=dpi->height;
	bottom -= top;
	assert(bottom > 0);

	dst = dpi->dst_ptr + top * dpi->pitch + left;

	if (!(color & 0x8000)) {
		if (!(color & 0x4000)) {
			do {
				memset(dst, color, right);
				dst += dpi->pitch;
			} while (--bottom);
		} else {
			/* use colortable mode */
			ctab = GetSpritePtr(color & 0x3FFF) + 1;
			do {
				int i;
				for(i=0; i!=right;i++)
					dst[i] = ctab[dst[i]];
				dst += dpi->pitch;
			} while (--bottom);
		}
	} else {
		byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
		do {
			int i;
			for (i = (bo ^= 1); i < right; i += 2)
				dst[i] = (byte)color;
			dst += dpi->pitch;
		} while (--bottom > 0);
	}
}

static void GfxSetPixel(int x, int y, int color)
{
	DrawPixelInfo *dpi = _cur_dpi;
	if ((x-=dpi->left) < 0 || x>=dpi->width || (y-=dpi->top)<0 || y>=dpi->height)
		return;
	dpi->dst_ptr[y*dpi->pitch+x] = color;
}

void GfxDrawLine(int x, int y, int x2, int y2, int color)
{
	int dy;
	int dx;
	int stepx, stepy;
	int frac;

	// Check clipping first
	{
		DrawPixelInfo *dpi = _cur_dpi;
		int t;

		if (x < dpi->left && x2 < dpi->left)
			return;

		if (y < dpi->top && y2 < dpi->top)
			return;

		t = dpi->left + dpi->width;
		if (x > t && x2 > t)
			return;

		t = dpi->top + dpi->height;
		if (y > t && y2 > t)
			return;
	}

	dy = (y2 - y) * 2;
	if (dy < 0) { dy = -dy;  stepy = -1; } else { stepy = 1; }

	dx = (x2 - x) * 2;
	if (dx < 0) { dx = -dx;  stepx = -1; } else { stepx = 1; }

	GfxSetPixel(x, y, color);
	if (dx > dy) {
		frac = dy - (dx >> 1);
		while (x != x2) {
			if (frac >= 0) {
				y += stepy;
				frac -= dx;
			}
			x += stepx;
			frac += dy;
			GfxSetPixel(x, y, color);
		}
	} else {
		frac = dx - (dy >> 1);
		while (y != y2) {
			if (frac >= 0) {
				x += stepx;
				frac -= dy;
			}
			y += stepy;
			frac += dx;
			GfxSetPixel(x, y, color);
		}
	}
}

// ASSIGNMENT OF ASCII LETTERS < 32
// 0 - end of string
// 1 - SETX <BYTE>
// 2 - SETXY <BYTE> <BYTE>
// 3-7 -
// 8 - TINYFONT
// 9 - BIGFONT
// 10 - newline
// 11-14 -
// 15-31 - 17 colors


enum {
	ASCII_LETTERSTART = 32,

	ASCII_SETX = 1,
	ASCII_SETXY = 2,

	ASCII_TINYFONT = 8,
	ASCII_BIGFONT = 9,
	ASCII_NL = 10,

	ASCII_COLORSTART = 15,
};


/* returns right coordinate */
int DrawString(int x, int y, uint16 str, uint16 color)
{
	GetString(str_buffr, str);
	assert(strlen(str_buffr) < sizeof(str_buffr) - 1);
	return DoDrawString(str_buffr, x, y, color);
}


void DrawStringRightAligned(int x, int y, uint16 str, uint16 color)
{
	GetString(str_buffr, str);
	assert(strlen(str_buffr) < sizeof(str_buffr) - 1);
	DoDrawString(str_buffr, x - GetStringWidth(str_buffr), y, color);
}


int DrawStringCentered(int x, int y, uint16 str, uint16 color)
{
	int w;

	GetString(str_buffr, str);
	assert(strlen(str_buffr) < sizeof(str_buffr) - 1);

	w = GetStringWidth(str_buffr);
	DoDrawString(str_buffr, x - (w>>1), y, color);

	return w;
}

void DrawStringCenterUnderline(int x, int y, uint16 str, uint16 color)
{
	int w = DrawStringCentered(x, y, str, color);
	GfxFillRect(x-(w>>1), y+10, x-(w>>1)+w, y+10, _string_colorremap[1]);
}

static uint32 FormatStringLinebreaks(byte *str, int maxw)
{
	int num = 0;
	int base = _stringwidth_base;
	int w;
	byte *last_space;
	byte c;

	for(;;) {
		w = 0;
		last_space = NULL;

		for(;;) {
			c = *str++;
			if (c == ' ') last_space = str;

			if (c >= ASCII_LETTERSTART) {
				w += _stringwidth_table[base + ((byte)c) - 0x20];
				if (w > maxw) {
					str = last_space;
					if (str == NULL)
						return num + (base << 16);
					break;
				}
			} else {
				if (c == 0) return num + (base << 16);
				if (c == ASCII_NL) break;

				if (c == ASCII_SETX) str++;
				else if (c == ASCII_SETXY) str += 2;
				else if (c == ASCII_TINYFONT) base = 224;
				else if (c == ASCII_BIGFONT) base = 448;
			}
		}

		num++;
		str[-1] = 0;
	}
}


void DrawStringMultiCenter(int x, int y, uint16 str, int maxw)
{
	uint32 tmp;
	int num, w, mt, t;
	byte *src;
	byte c;

	GetString(str_buffr, str);
	assert(strlen(str_buffr) < sizeof(str_buffr) - 1);

	tmp = FormatStringLinebreaks(str_buffr, maxw);
	num = (uint16)tmp;
	t = tmp >> 16;

	mt = 10;
	if (t != 0) {
		mt = 6;
		if (t != 244) mt = 18;
	}

	y -= (mt >> 1) * num;

	src = str_buffr;

	for(;;) {
		w = GetStringWidth(src);
		DoDrawString(src, x - (w>>1), y, 0xFE);
		_stringwidth_base = _stringwidth_out;

		for(;;) {
			c = *src++;
			if (c == 0) {
				y += mt;
				if (--num < 0) {
					_stringwidth_base = 0;
					return;
				}
				break;
			} else if (c == ASCII_SETX) {
				src++;
			} else if (c == ASCII_SETXY) {
				src+=2;
			}
		}
	}
}

void DrawStringMultiLine(int x, int y, uint16 str, int maxw) {
	uint32 tmp;
	int num, w, mt, t;
	byte *src;
	byte c;

	GetString(str_buffr, str);
	assert(strlen(str_buffr) < sizeof(str_buffr) - 1);

	tmp = FormatStringLinebreaks(str_buffr, maxw);
	num = (uint16)tmp;
	t = tmp >> 16;
	mt = 10;
	if (t != 0) {
		mt = 6;
		if (t != 244) mt = 18;
	}

	src = str_buffr;

	for(;;) {
		w = GetStringWidth(src);
		DoDrawString(src, x, y, 0xFE);
		_stringwidth_base = _stringwidth_out;

		for(;;) {
			c = *src++;
			if (c == 0) {
				y += mt;
				if (--num < 0) {
					_stringwidth_base = 0;
					return;
				}
				break;
			} else if (c == ASCII_SETX) {
				src++;
			} else if (c == ASCII_SETXY) {
				src+=2;
			}
		}
	}
}

int GetStringWidth(const byte *str) {
	int w = -1;
	byte c;
	int base = _stringwidth_base;

	for(;;) {
		c = *str++;
		if (c == 0)
			return w;
		if (c >= ASCII_LETTERSTART) {
			w += _stringwidth_table[base + c - ASCII_LETTERSTART];
		} else {
			if (c == ASCII_SETX) str++;
			else if (c == ASCII_SETXY) str += 2;
			else if (c == ASCII_TINYFONT) base = 224;
			else if (c == ASCII_BIGFONT) base = 448;
		}
	}
}

void DrawFrameRect(int left, int top, int right, int bottom, int ctab, int flags) {
	byte color_2 = _color_list[ctab].window_color_1a;
	byte color_interior = _color_list[ctab].window_color_bga;
	byte color_3 = _color_list[ctab].window_color_bgb;
	byte color = _color_list[ctab].window_color_2;

	if (!(flags & 0x8))	{
		if (!(flags & 0x20)) {
			GfxFillRect(left, top, left, bottom-1, color);
			GfxFillRect(left+1, top, right-1, top, color);
			GfxFillRect(right, top, right, bottom-1, color_2);
			GfxFillRect(left, bottom, right, bottom, color_2);
			if (!(flags & 0x10)) {
				GfxFillRect(left+1, top+1, right-1, bottom-1, color_interior);
			}
		} else {
			GfxFillRect(left, top, left, bottom, color_2);
			GfxFillRect(left+1, top, right, top, color_2);
			GfxFillRect(right, top+1, right, bottom-1, color);
			GfxFillRect(left+1, bottom, right, bottom, color);
			if (!(flags & 0x10)) {
				GfxFillRect(left+1, top+1, right-1, bottom-1,
					flags&0x40 ? color_interior : color_3);
			}
		}
	} else if (flags & 0x1) {
		// transparency
		GfxFillRect(left, top, right, bottom, 0x4322);
	} else {
		GfxFillRect(left, top, right, bottom, color_interior);
	}
}

int DoDrawString(const byte *string, int x, int y, uint16 real_color) {
	DrawPixelInfo *dpi = _cur_dpi;
	int base = _stringwidth_base;
	byte c;
	byte color;
	int xo = x, yo = y;

	color = real_color & 0xFF;

	if (color != 0xFE) {
		if (x >= dpi->left + dpi->width ||
				x + _screen.width*2 <= dpi->left ||
				y >= dpi->top + dpi->height ||
				y + _screen.height <= dpi->top)
					return x;

		if (color != 0xFF) {
switch_color:;
			if (real_color & IS_PALETTE_COLOR) {
				_string_colorremap[1] = color;
				_string_colorremap[2] = 215;
			} else {
				_string_colorremap[1] = _string_colormap[color].text;
				_string_colorremap[2] = _string_colormap[color].shadow;
			}
			_color_remap_ptr = _string_colorremap;
		}
	}

check_bounds:
	if (y + 19 <= dpi->top || dpi->top + dpi->height <= y) {
skip_char:;
		for(;;) {
			c = *string++;
			if (c < ASCII_LETTERSTART) goto skip_cont;
		}
	}

	for(;;) {
		c = *string++;
skip_cont:;
		if (c == 0) {
			_stringwidth_out = base;
			return x;
		}
		if (c >= ASCII_LETTERSTART) {
			if (x >= dpi->left + dpi->width) goto skip_char;
			if (x + 26 >= dpi->left) {
				GfxMainBlitter(GetSpritePtr(base + 2 + c - ASCII_LETTERSTART), x, y, 1);
			}
			x += _stringwidth_table[base + c - ' '];
		} else if (c == ASCII_NL) { // newline = {}
			x = xo;
			y += 10;
			if (base != 0) {
				y -= 4;
				if (base != 0xE0)
					y += 12;
			}
			goto check_bounds;
		} else if (c >= ASCII_COLORSTART) { // change color?
			color = (byte)(c - ASCII_COLORSTART);
			goto switch_color;
		} else if (c == ASCII_SETX) { // {SETX}
			x = xo + *string++;
		} else if (c == ASCII_SETXY) {// {SETXY}
			x = xo + *string++;
			y = yo + *string++;
		} else if (c == ASCII_TINYFONT) { // {TINYFONT}
			base = 0xE0;
		} else if (c == ASCII_BIGFONT) { // {BIGFONT}
			base = 0x1C0;
		} else {
			printf("Unknown string command character %d\n", c);
		}
	}
}

void DrawSprite(uint32 img, int x, int y) {
	if (img & 0x8000) {
		_color_remap_ptr = GetSpritePtr(img >> 16) + 1;
		GfxMainBlitter(GetSpritePtr(img & 0x3FFF), x, y, 1);
	} else if (img & 0x4000) {
		_color_remap_ptr = GetSpritePtr(img >> 16) + 1;
		GfxMainBlitter(GetSpritePtr(img & 0x3FFF), x, y, 2);
	} else {
		GfxMainBlitter(GetSpritePtr(img & 0x3FFF), x, y, 0);
	}
}

typedef struct BlitterParams {
	int start_x, start_y;
	byte *sprite, *sprite_org;
	byte *dst;
	int mode;
	int width, height;
	int width_org, height_org;
	int pitch;
	byte info;
} BlitterParams;

static void GfxBlitTileZoomIn(BlitterParams *bp)
{
	byte *src_o = bp->sprite, *src;
	int num, skip;
	byte done;
	byte *dst, *ctab;

	if (bp->mode & 1) {
		src_o += READ_LE_UINT16(src_o + bp->start_y * 2);

		do {
			do {
				done = src_o[0];
				num = done & 0x7F;
				skip = src_o[1];
				src = src_o + 2;
				src_o += num + 2;

				dst = bp->dst;

				if ( (skip -= bp->start_x) > 0) {
					dst += skip;
				} else {
					src -= skip;
					num += skip;
					if (num <= 0)
						continue;
					skip = 0;
				}

				skip = skip + num - bp->width;
				if (skip > 0) {
					num -= skip;
					if (num <= 0)
						continue;
				}

				ctab = _color_remap_ptr;

				while (num >= 4) {
					dst[3] = ctab[src[3]];
					dst[2] = ctab[src[2]];
					dst[1] = ctab[src[1]];
					dst[0] = ctab[src[0]];
					dst += 4;
					src += 4;
					num -= 4;
				}
				while (num) {
					*dst++ = ctab[*src++];
					num--;
				}
			} while (!(done & 0x80));

			bp->dst += bp->pitch;
		} while (--bp->height);
	} else if (bp->mode & 2) {
		src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
		do {
			do {
				done = src_o[0];
				num = done & 0x7F;
				skip = src_o[1];
				src_o += num + 2;

				dst = bp->dst;

				if ( (skip -= bp->start_x) > 0) {
					dst += skip;
				} else {
					num += skip;
					if (num <= 0)
						continue;
					skip = 0;
				}

				skip = skip + num - bp->width;
				if (skip > 0) {
					num -= skip;
					if (num <= 0)
						continue;
				}

				ctab = _color_remap_ptr;
				while (num) {
					*dst = ctab[*dst];
					dst++;
					num--;
				}
			} while (!(done & 0x80));

			bp->dst += bp->pitch;
		} while (--bp->height);

	} else {
		src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
		do {
			do {
				done = src_o[0];
				num = done & 0x7F;
				skip = src_o[1];
				src = src_o + 2;
				src_o += num + 2;

				dst = bp->dst;

				if ( (skip -= bp->start_x) > 0) {
					dst += skip;
				} else {
					src -= skip;
					num += skip;
					if (num <= 0)
						continue;
					skip = 0;
				}

				skip = skip + num - bp->width;
				if (skip > 0) {
					num -= skip;
					if (num <= 0)
						continue;
				}
#if defined(_WIN32)
				if (num & 1) *dst++ = *src++;
				if (num & 2) { *(uint16*)dst = *(uint16*)src; dst += 2; src += 2; }
				if (num >>= 2) {
					do {
						*(uint32*)dst = *(uint32*)src;
						dst += 4;
						src += 4;
					} while (--num);
				}
#else
				memcpy(dst, src, num);
#endif
			} while (!(done & 0x80));

			bp->dst += bp->pitch;
		} while (--bp->height);
	}
}

static void GfxBlitZoomInUncomp(BlitterParams *bp)
{
	byte *src = bp->sprite;
	byte *dst = bp->dst;
	int height = bp->height;
	int width = bp->width;
	int i;

	assert(height > 0);
	assert(width > 0);

	if (bp->mode & 1) {
		if (bp->info & 1) {
			byte *ctab = _color_remap_ptr;
			byte b;

			do {
				for(i=0; i!=width; i++) {
					if ((b=ctab[src[i]]) != 0)
						dst[i] = b;
				}
				src += bp->width_org;
				dst += bp->pitch;
			} while (--height);
		}
	} else if (bp->mode & 2) {
		if (bp->info & 1) {
			byte *ctab = _color_remap_ptr;
			do {
				for(i=0; i!=width; i++)
					if (src[i])
						dst[i] = ctab[dst[i]];
				src += bp->width_org;
				dst += bp->pitch;
			} while (--height);
		}
	} else {
		if (!(bp->info & 1)) {
			do {
				memcpy(dst, src, width);
				src += bp->width_org;
				dst += bp->pitch;
			} while (--height);
		} else {
			do {
				int n = width;
				while (n >= 4) {
					if (src[0]) dst[0] = src[0];
					if (src[1]) dst[1] = src[1];
					if (src[2]) dst[2] = src[2];
					if (src[3]) dst[3] = src[3];

					dst += 4;
					src += 4;
					n -= 4;
				}

				while (n) {
					if (src[0]) dst[0] = src[0];
					src++;
					dst++;
					n--;
				}

				src += bp->width_org - width;
				dst += bp->pitch - width;

			} while (--height);
		}
	}
}

static void GfxBlitTileZoomMedium(BlitterParams *bp)
{
	byte *src_o = bp->sprite, *src;
	int num, skip;
	byte done;
	byte *dst, *ctab;

	if (bp->mode & 1) {
		src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
		do {
			do {
				done = src_o[0];
				num = done & 0x7F;
				skip = src_o[1];
				src = src_o + 2;
				src_o += num + 2;

				dst = bp->dst;

				if (skip & 1) {
					skip++;
					src++;
					if (!--num)
						continue;
				}

				if ( (skip -= bp->start_x) > 0) {
					dst += skip >> 1;
				} else {
					src -= skip;
					num += skip;
					if (num <= 0)
						continue;
					skip = 0;
				}

				skip = skip + num - bp->width;
				if (skip > 0) {
					num -= skip;
					if (num <= 0)
						continue;
				}

				ctab = _color_remap_ptr;
				num = (num + 1) >> 1;
				if (num) {
					do {
						*dst = ctab[*src];
						dst++;
						src+=2;
					} while (--num);
				}
			} while (!(done & 0x80));
			bp->dst += bp->pitch;
			if (!--bp->height)
				return;

			do {
				done = src_o[0];
				src_o += (done & 0x7F) + 2;
			} while (!(done & 0x80));
		} while (--bp->height);
	} else if (bp->mode & 2) {
		src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
		do {
			do {
				done = src_o[0];
				num = done & 0x7F;
				skip = src_o[1];
				src_o += num + 2;

				dst = bp->dst;

				if (skip & 1) {
					skip++;
					if (!--num)
						continue;
				}

				if ( (skip -= bp->start_x) > 0) {
					dst += skip >> 1;
				} else {
					num += skip;
					if (num <= 0)
						continue;
					skip = 0;
				}

				skip = skip + num - bp->width;
				if (skip > 0) {
					num -= skip;
					if (num <= 0)
						continue;
				}

				ctab = _color_remap_ptr;
				num = (num + 1) >> 1;
				if (num) {
					do {
						*dst = ctab[*dst];
						dst++;
					} while (--num);
				}

			} while (!(done & 0x80));
			bp->dst += bp->pitch;
			if (!--bp->height)
				return;

			do {
				done = src_o[0];
				src_o += (done & 0x7F) + 2;
			} while (!(done & 0x80));
		} while (--bp->height);

	} else {
		src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
		do {
			do {
				done = src_o[0];
				num = done & 0x7F;
				skip = src_o[1];
				src = src_o + 2;
				src_o += num + 2;

				dst = bp->dst;

				if (skip & 1) {
					skip++;
					src++;
					if (!--num)
						continue;
				}

				if ( (skip -= bp->start_x) > 0) {
					dst += skip >> 1;
				} else {
					src -= skip;
					num += skip;
					if (num <= 0)
						continue;
					skip = 0;
				}

				skip = skip + num - bp->width;
				if (skip > 0) {
					num -= skip;
					if (num <= 0)
						continue;
				}

				num = (num + 1) >> 1;

				if (num) {
					do {
						*dst = *src;
						dst++;
						src+=2;
					} while (--num);
				}

			} while (!(done & 0x80));

			bp->dst += bp->pitch;
			if (!--bp->height)
				return;

			do {
				done = src_o[0];
				src_o += (done & 0x7F) + 2;
			} while (!(done & 0x80));

		} while (--bp->height);
	}
}

static void GfxBlitZoomMediumUncomp(BlitterParams *bp)
{
	byte *src = bp->sprite;
	byte *dst = bp->dst;
	int height = bp->height;
	int width = bp->width;
	int i;

	assert(height > 0);
	assert(width > 0);

	if (bp->mode & 1) {
		if (bp->info & 1) {
			byte *ctab = _color_remap_ptr,b;
			height >>= 1;
			if (height)	do {
				for(i=0; i!=width>>1; i++)
					if ((b=ctab[src[i*2]]) != 0)
						dst[i] = b;
				src += bp->width_org * 2;
				dst += bp->pitch;
			} while (--height);
		}
	} else if (bp->mode & 2) {
		if (bp->info & 1) {
			byte *ctab = _color_remap_ptr;
			height >>= 1;
			if (height)	do {
				for(i=0; i!=width>>1; i++)
					if (src[i*2])
						dst[i] = ctab[dst[i]];
				src += bp->width_org * 2;
				dst += bp->pitch;
			} while (--height);
		}
	} else {
		if (bp->info & 1) {
			height >>= 1;
			if (height)	do {
				for(i=0; i!=width>>1; i++)
					if (src[i*2])
						dst[i] = src[i*2];
				src += bp->width_org * 2;
				dst += bp->pitch;
			} while (--height);
		}
	}
}

static void GfxBlitTileZoomOut(BlitterParams *bp)
{
	byte *src_o = bp->sprite, *src;
	int num, skip;
	byte done;
	byte *dst, *ctab;

	if (bp->mode & 1) {
		src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
		for(;;) {
			do {
				done = src_o[0];
				num = done & 0x7F;
				skip = src_o[1];
				src = src_o + 2;
				src_o += num + 2;

				dst = bp->dst;

				if (skip & 1) {
					skip++;
					src++;
					if (!--num)
						continue;
				}

				if (skip & 2) {
					skip+=2;
					src+=2;
					if ((num-=2) <= 0)
						continue;
				}

				if ( (skip -= bp->start_x) > 0) {
					dst += skip >> 2;
				} else {
					src -= skip;
					num += skip;
					if (num <= 0)
						continue;
					skip = 0;
				}

				skip = skip + num - bp->width;
				if (skip > 0) {
					num -= skip;
					if (num <= 0)
						continue;
				}

				ctab = _color_remap_ptr;
				num = (num + 3) >> 2;
				if (num) {
					do {
						*dst = ctab[*src];
						dst++;
						src+=4;
					} while (--num);
				}
			} while (!(done & 0x80));
			bp->dst += bp->pitch;
			if (!--bp->height)
				return;

			do {
				done = src_o[0];
				src_o += (done & 0x7F) + 2;
			} while (!(done & 0x80));
			if (!--bp->height)
				return;

			do {
				done = src_o[0];
				src_o += (done & 0x7F) + 2;
			} while (!(done & 0x80));
			if (!--bp->height)
				return;

			do {
				done = src_o[0];
				src_o += (done & 0x7F) + 2;
			} while (!(done & 0x80));
			if (!--bp->height)
				return;
		}
	} else if (bp->mode & 2) {
		src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
		for(;;) {
			do {
				done = src_o[0];
				num = done & 0x7F;
				skip = src_o[1];
				src_o += num + 2;

				dst = bp->dst;

				if (skip & 1) {
					skip++;
					if (!--num)
						continue;
				}

				if (skip & 2) {
					skip+=2;
					if ((num-=2) <= 0)
						continue;
				}

				if ( (skip -= bp->start_x) > 0) {
					dst += skip >> 2;
				} else {
					num += skip;
					if (num <= 0)
						continue;
					skip = 0;
				}

				skip = skip + num - bp->width;
				if (skip > 0) {
					num -= skip;
					if (num <= 0)
						continue;
				}

				ctab = _color_remap_ptr;
				num = (num + 3) >> 2;
				if (num) {
					do {
						*dst = ctab[*dst];
						dst++;
					} while (--num);
				}

			} while (!(done & 0x80));
			bp->dst += bp->pitch;
			if (!--bp->height)
				return;

			do {
				done = src_o[0];
				src_o += (done & 0x7F) + 2;
			} while (!(done & 0x80));
			if (!--bp->height)
				return;

			do {
				done = src_o[0];
				src_o += (done & 0x7F) + 2;
			} while (!(done & 0x80));
			if (!--bp->height)
				return;

			do {
				done = src_o[0];
				src_o += (done & 0x7F) + 2;
			} while (!(done & 0x80));
			if (!--bp->height)
				return;
		}
	} else {
		src_o += READ_LE_UINT16(src_o + bp->start_y * 2);
		for(;;) {
			do {
				done = src_o[0];
				num = done & 0x7F;
				skip = src_o[1];
				src = src_o + 2;
				src_o += num + 2;

				dst = bp->dst;

				if (skip & 1) {
					skip++;
					src++;
					if (!--num)
						continue;
				}

				if (skip & 2) {
					skip+=2;
					src+=2;
					if ((num-=2) <= 0)
						continue;
				}

				if ( (skip -= bp->start_x) > 0) {
					dst += skip >> 2;
				} else {
					src -= skip;
					num += skip;
					if (num <= 0)
						continue;
					skip = 0;
				}

				skip = skip + num - bp->width;
				if (skip > 0) {
					num -= skip;
					if (num <= 0)
						continue;
				}

				num = (num + 3) >> 2;

				if (num) {
					do {
						*dst = *src;
						dst++;
						src+=4;
					} while (--num);
				}

			} while (!(done & 0x80));

			bp->dst += bp->pitch;
			if (!--bp->height)
				return;

			do {
				done = src_o[0];
				src_o += (done & 0x7F) + 2;
			} while (!(done & 0x80));
			if (!--bp->height)
				return;

			do {
				done = src_o[0];
				src_o += (done & 0x7F) + 2;
			} while (!(done & 0x80));
			if (!--bp->height)
				return;

			do {
				done = src_o[0];
				src_o += (done & 0x7F) + 2;
			} while (!(done & 0x80));
			if (!--bp->height)
				return;
		}
	}
}

static void GfxBlitZoomOutUncomp(BlitterParams *bp)
{
	byte *src = bp->sprite;
	byte *dst = bp->dst;
	int height = bp->height;
	int width = bp->width;
	int i;

	assert(height > 0);
	assert(width > 0);

	if (bp->mode & 1) {
		if (bp->info & 1) {
			byte *ctab = _color_remap_ptr,b;
			height >>= 2;
			if (height)	do {
				for(i=0; i!=width>>2; i++)
					if ((b=ctab[src[i*4]]) != 0)
						dst[i] = b;
				src += bp->width_org * 4;
				dst += bp->pitch;
			} while (--height);
		}
	} else if (bp->mode & 2) {
		if (bp->info & 1) {
			byte *ctab = _color_remap_ptr;
			height >>= 2;
			if (height)	do {
				for(i=0; i!=width>>2; i++)
					if (src[i*4])
						dst[i] = ctab[dst[i]];
				src += bp->width_org * 4;
				dst += bp->pitch;
			} while (--height);
		}
	} else {
		if (bp->info & 1) {
			height >>= 2;
			if (height)	do {
				for(i=0; i!=width>>2; i++)
					if (src[i*4])
						dst[i] = src[i*4];
				src += bp->width_org * 4;
				dst += bp->pitch;
			} while (--height);
		}
	}
}

typedef void (*BlitZoomFunc)(BlitterParams *bp);

static void GfxMainBlitter(byte *sprite, int x, int y, int mode)
{
	DrawPixelInfo *dpi = _cur_dpi;
	int start_x, start_y;
	byte info;
	BlitterParams bp;
	int zoom_mask = ~((1 << (dpi->zoom))-1);

	static const BlitZoomFunc zf_tile[3] =
	{
		GfxBlitTileZoomIn,
		GfxBlitTileZoomMedium,
		GfxBlitTileZoomOut
	};
	static const BlitZoomFunc zf_uncomp[3] =
	{
		GfxBlitZoomInUncomp,
		GfxBlitZoomMediumUncomp,
		GfxBlitZoomOutUncomp
	};

	/* decode sprite header */
	x += (int16)READ_LE_UINT16(&((SpriteHdr*)sprite)->x_offs);
	y += (int16)READ_LE_UINT16(&((SpriteHdr*)sprite)->y_offs);
	bp.width_org = bp.width = READ_LE_UINT16(&((SpriteHdr*)sprite)->width);
	bp.height_org = bp.height = ((SpriteHdr*)sprite)->height;
	info = ((SpriteHdr*)sprite)->info;
	bp.info = info;
	bp.sprite_org = bp.sprite = sprite + sizeof(SpriteHdr);
	bp.dst = dpi->dst_ptr;
	bp.mode = mode;
	bp.pitch = dpi->pitch;

	assert(bp.height > 0);
	assert(bp.width > 0);

	if (info & 8) {
		/* tile blit */
		start_y = 0;

		if (dpi->zoom > 0) {
			start_y += bp.height &~ zoom_mask;
			bp.height &= zoom_mask;
			if (bp.height == 0) return;
			y&=zoom_mask;
		}

		if ( (y -= dpi->top) < 0) {
			if ((bp.height += y) <= 0)
				return;
			start_y -= y;
			y = 0;
		} else {
			bp.dst += bp.pitch * (y>>(dpi->zoom));
		}
		bp.start_y = start_y;

		if ( (y = y + bp.height - dpi->height) > 0) {
			if ( (bp.height -= y) <= 0)
				return;
		}

		start_x = 0;
		x &= zoom_mask;
		if ( (x -= dpi->left) < 0) {
			if ((bp.width += x) <= 0)
				return;
			start_x -= x;
			x = 0;
		}
		bp.start_x = start_x;
		bp.dst += x>>(dpi->zoom);

		if ( (x = x + bp.width - dpi->width) > 0) {
			if ( (bp.width -= x) <= 0)
				return;
		}

		zf_tile[dpi->zoom](&bp);
	} else {

		bp.sprite += bp.width * (bp.height & ~zoom_mask);
		bp.height &= zoom_mask;
		if (bp.height == 0)
			return;

		y &= zoom_mask;

		if ( (y -= dpi->top) < 0) {
			if ((bp.height += y) <= 0)
				return;
			bp.sprite -= bp.width * y;
			y = 0;
		} else {
			bp.dst += bp.pitch * (y>>(dpi->zoom));
		}

		if ( (y = y + bp.height - dpi->height) > 0) {
			if ( (bp.height -= y) <= 0)
				return;
		}

		start_x = 0;

		x &= zoom_mask;

		if ( (x -= dpi->left) < 0) {
			if ((bp.width += x) <= 0)
				return;
			start_x -= x;
			bp.sprite -= x;
			x = 0;
		}
		bp.dst += x>>(dpi->zoom);

		if ( (x = x + bp.width - dpi->width) > 0) {
			if ( (bp.width -= x) <= 0)
				return;
			start_x += x;
		}
		bp.start_x = start_x;

		if (info&2) {
			int totpix = bp.height_org * bp.width_org;
			byte *dst = (byte*)alloca(totpix);
			byte *src = bp.sprite_org;
			signed char b;

			bp.sprite = dst + (bp.sprite - bp.sprite_org);

			while (totpix != 0) {
				assert(totpix > 0);
				b = *src++;
				if (b >= 0) {
					int i, count=b;
					for(i=0; i!=count; i++)
						dst[i] = src[i];
					dst += count;
					src += count;
					totpix -= count;
				} else {
					byte *tmp = dst- (((b&7)<<8)|(*src++));
					int i;
					int count = -(b >> 3);

					for(i=0; i!=count; i++)
						dst[i] = tmp[i];
					dst += count;
					totpix -= count;
				}
			}
		}
		zf_uncomp[dpi->zoom](&bp);
	}
}

#if 0
static void GfxScalePalette(int pal, byte scaling)
{
	byte *dst, *src;
	size_t count;

	GfxInitPalettes();

	dst = _cur_palette;
	src = GET_PALETTE(pal);
	count = 256;
	do {
		dst[0] = (byte)(src[0] * scaling >> 8);
		dst[1] = (byte)(src[1] * scaling >> 8);
		dst[2] = (byte)(src[2] * scaling >> 8);
		dst += 3;
		src += 3;
	} while (--count);
}
#endif

void DoPaletteAnimations(void);

void GfxInitPalettes(void)
{
	int pal = _use_dos_palette?1:0;
	memcpy(_cur_palette, _palettes[pal], 256*3);

	_pal_first_dirty = 0;
	_pal_last_dirty = 255;
	DoPaletteAnimations();
}

#define EXTR(p,q) (((uint16)(_timer_counter * (p)) * (q)) >> 16)
#define EXTR2(p,q) (((uint16)(~_timer_counter * (p)) * (q)) >> 16)
#define COPY_TRIPLET do {d[0]=s[0+j]; d[1]=s[1+j]; d[2]=s[2+j];d+=3;}while(0)

void DoPaletteAnimations(void)
{
	const byte *s;
	byte *d;
	/* Amount of colors to be rotated.
	 * A few more for the DOS palette, because the water colors are
	 * 245-254 for DOS and 217-226 for Windows.  */
	int c = _use_dos_palette?38:28;
	int j;
	int i;
	const ExtraPaletteValues *ev = &_extra_palette_values;
	byte old_val[114]; // max(c*(38:28)) = 114

	d = _cur_palette + 217*3;
	memcpy(old_val, d, c*3);

	// Dark blue water
	s = ev->a;
	if (_opt.landscape == LT_CANDY) s = ev->ac;
	j = EXTR(320,5) * 3;
	for(i=0; i!=5; i++) {
		COPY_TRIPLET;
		j+=3;
		if (j == 15) j = 0;
	}

	// Glittery water
	s = ev->b;
	if (_opt.landscape == LT_CANDY) s = ev->bc;
	j = EXTR(128, 15) * 3;
	for(i=0; i!=5; i++) {
		COPY_TRIPLET;
		j += 9;
		if (j >= 45) j -= 45;
	}

	s = ev->e;
	j = EXTR2(512, 5) * 3;
	for(i=0; i!=5; i++) {
		COPY_TRIPLET;
		j += 3;
		if (j == 3*5) j = 0;
	}

	// Oil refinery fire animation
	s = ev->oil_ref;
	j = EXTR2(512, 7) * 3;
	for(i=0; i!=7; i++) {
		COPY_TRIPLET;
		j += 3;
		if (j == 3*7) j = 0;
	}

	// Radio tower blinking
	{
		byte i,v;
		i = (_timer_counter >> 1) & 0x7F;
		(v = 255, i < 0x3f) ||
		(v = 128, i < 0x4A || i >= 0x75) ||
		(v = 20);
		d[0] = v;
		d[1] = d[2] = 0;
		d += 3;

		i ^= 0x40;
		(v = 255, i < 0x3f) ||
		(v = 128, i < 0x4A || i >= 0x75) ||
		(v = 20);
		d[0] = v;

		d[1] = d[2] = 0;
		d += 3;
	}

	// Handle lighthouse and stadium animation
	s = ev->lighthouse;
	j = EXTR(256, 4) * 3;
	for(i=0; i!=4; i++) {
		COPY_TRIPLET;
		j += 3;
		if (j == 3*4) j = 0;
	}

	// Animate water for old DOS graphics
	if(_use_dos_palette) {
		// Dark blue water DOS
		s = ev->a;
		if (_opt.landscape == LT_CANDY) s = ev->ac;
		j = EXTR(320,5) * 3;
		for(i=0; i!=5; i++) {
			COPY_TRIPLET;
			j+=3;
			if (j == 15) j = 0;
		}

		// Glittery water DOS
		s = ev->b;
		if (_opt.landscape == LT_CANDY) s = ev->bc;
		j = EXTR(128, 15) * 3;
		for(i=0; i!=5; i++) {
			COPY_TRIPLET;
			j += 9;
			if (j >= 45) j -= 45;
		}
	}

	if (memcmp(old_val, _cur_palette + 217*3, c*3)) {
		if (_pal_first_dirty > 217) _pal_first_dirty = 217;
		if (_pal_last_dirty < 217+c) _pal_last_dirty = 217+c;
	}
}


void LoadStringWidthTable(void)
{
	int i;
	byte *b;

	b = _stringwidth_table;

	// 2 equals space.
	for(i=2; i != 0xE2; i++) {
		*b++ = (byte)((i < 93 || i >= 129 || i == 98) ? GetSpritePtr(i)[2] : 0);
	}

	for(i=0xE2; i != 0x1C2; i++) {
		*b++ = (byte)((i < 317 || i >= 353) ? GetSpritePtr(i)[2]+1 : 0);
	}

	for(i=0x1C2; i != 0x2A2; i++) {
		*b++ = (byte)((i < 541 || i >= 577) ? GetSpritePtr(i)[2]+1 : 0);
	}
}

void ScreenSizeChanged(void)
{
	// check the dirty rect
	if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
	if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;

	// screen size changed and the old bitmap is invalid now, so we don't want to undraw it
	_cursor.visible = false;
}

void UndrawMouseCursor(void)
{
	if (_cursor.visible) {
		_cursor.visible = false;
		memcpy_pitch(
			_screen.dst_ptr + _cursor.draw_pos.x + _cursor.draw_pos.y * _screen.pitch,
			_cursor_backup,
			_cursor.draw_size.x, _cursor.draw_size.y, _cursor.draw_size.x, _screen.pitch);

		_video_driver->make_dirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
	}
}

void DrawMouseCursor(void)
{
	int x,y,w,h;

	// Don't draw the mouse cursor if it's already drawn
	if (_cursor.visible) {
		if (!_cursor.dirty)
			return;
		UndrawMouseCursor();
	}

	w = _cursor.size.x;
	x = _cursor.pos.x + _cursor.offs.x;
	if (x < 0) { w += x; x=0; }
	if (w>_screen.width-x) { w = _screen.width-x; }
	if (w <= 0) return;
	_cursor.draw_pos.x = x;
	_cursor.draw_size.x = w;

	h = _cursor.size.y;
	y = _cursor.pos.y + _cursor.offs.y;
	if (y < 0) { h += y; y=0; }
	if (h>_screen.height-y) { h = _screen.height-y; }
	if (h <= 0) return;
	_cursor.draw_pos.y = y;
	_cursor.draw_size.y = h;

	assert(w*h < (int) sizeof(_cursor_backup));

	// Make backup of stuff below cursor
	memcpy_pitch(
		_cursor_backup,
		_screen.dst_ptr + _cursor.draw_pos.x + _cursor.draw_pos.y * _screen.pitch,
		_cursor.draw_size.x, _cursor.draw_size.y, _screen.pitch, _cursor.draw_size.x);

	// Draw cursor on screen
	_cur_dpi = &_screen;
	DrawSprite(_cursor.sprite, _cursor.pos.x, _cursor.pos.y);

	_video_driver->make_dirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);

	_cursor.visible = true;
	_cursor.dirty = false;
}

#if defined(_DEBUG)
static void DbgScreenRect(int left, int top, int right, int bottom)
{
	DrawPixelInfo dp,*old;

	old = _cur_dpi;
	_cur_dpi = &dp;
	dp = _screen;
	GfxFillRect(left, top, right-1, bottom-1, rand() & 255);
	_cur_dpi = old;
}
#endif

extern bool _dbg_screen_rect;

void RedrawScreenRect(int left, int top, int right, int bottom)
{
	assert(right <= _screen.width && bottom <= _screen.height);
	if (_cursor.visible) {
		if (right > _cursor.draw_pos.x &&
				left < _cursor.draw_pos.x + _cursor.draw_size.x &&
				bottom > _cursor.draw_pos.y &&
				top < _cursor.draw_pos.y + _cursor.draw_size.y) {
			UndrawMouseCursor();
		}
	}
	UndrawTextMessage();

#if defined(_DEBUG)
	if (_dbg_screen_rect)
		DbgScreenRect(left, top, right, bottom);
	else
#endif
		DrawOverlappedWindowForAll(left, top, right, bottom);

	_video_driver->make_dirty(left, top, right-left, bottom-top);
}

void DrawDirtyBlocks(void)
{
	byte *b = _dirty_blocks;
	int x=0,y=0;
	const int w = (_screen.width + 63) & ~63;
	const int h = (_screen.height + 7) & ~7;

	do {
		if (*b != 0) {
			int left,top;
			int right = x + 64;
			int bottom = y;
			byte *p = b;
			int h2;
			// First try coalescing downwards
			do {
				*p = 0;
				p += DIRTY_BYTES_PER_LINE;
				bottom += 8;
			} while (bottom != h && *p);

			// Try coalescing to the right too.
			h2 = (bottom - y) >> 3;
			assert(h2>0);
			p = b;

			while (right != w) {
				byte *p2 = ++p;
				int h = h2;
				// Check if a full line of dirty flags is set.
				do {
					if (!*p2) goto no_more_coalesc;
					p2 += DIRTY_BYTES_PER_LINE;
				} while (--h);

				// Wohoo, can combine it one step to the right!
				// Do that, and clear the bits.
				right += 64;

				h = h2;
				p2 = p;
				do {
					*p2 = 0;
					p2 += DIRTY_BYTES_PER_LINE;
				} while (--h);
			}
			no_more_coalesc:;

			left = x;
			top = y;

			if (left < _invalid_rect.left)left = _invalid_rect.left;
			if (top < _invalid_rect.top)	top = _invalid_rect.top;
			if (right > _invalid_rect.right)right = _invalid_rect.right;
			if (bottom > _invalid_rect.bottom)bottom = _invalid_rect.bottom;

			if (left < right && top < bottom) {
				RedrawScreenRect(left, top, right, bottom);
			}

		}
	} while (b++, (x+=64) != w || (x=0,b+=-(w>>6)+DIRTY_BYTES_PER_LINE,(y+=8) != h));

	_invalid_rect.left = w;
	_invalid_rect.top = h;
	_invalid_rect.right = 0;
	_invalid_rect.bottom = 0;
}


void SetDirtyBlocks(int left, int top, int right, int bottom)
{
	byte *b;
	int width,height,i;

	if (left < 0) left = 0;
	if (top < 0) top = 0;
	if (right > _screen.width) right = _screen.width;
	if (bottom > _screen.height) bottom = _screen.height;

	if (left >= right || top >= bottom)
		return;

	if (left < _invalid_rect.left)	_invalid_rect.left = left;
	if (top < _invalid_rect.top)  	_invalid_rect.top = top;
	if (right > _invalid_rect.right)_invalid_rect.right = right;
	if (bottom > _invalid_rect.bottom)_invalid_rect.bottom = bottom;

	left >>= 6;
	top >>= 3;

	b = _dirty_blocks + top * DIRTY_BYTES_PER_LINE + left;

	width = ((right-1) >> 6) - left + 1;
	height = ((bottom-1) >> 3) - top + 1;

	assert(width > 0 && height > 0);

	do {
		i=width;
		do b[--i] = 0xFF; while (i);

		b += DIRTY_BYTES_PER_LINE;
	} while (--height);
}

void MarkWholeScreenDirty(void)
{
	SetDirtyBlocks(0, 0, _screen.width, _screen.height);
}

bool FillDrawPixelInfo(DrawPixelInfo *n, DrawPixelInfo *o, int left, int top, int width, int height)
{
	int t;

	if (o == NULL)
		o = _cur_dpi;

	n->zoom = 0;

	assert(width > 0);
	assert(height > 0);

	n->left = 0;
	if ((left -= o->left) < 0) {
		if ((width += left) < 0)
			return false;
		n->left = -left;
		left = 0;
	}

	if ((t=width + left - o->width) > 0) {
		if ((width -= t) < 0)
			return false;
	}
	n->width = width;

	n->top = 0;
	if ((top -= o->top) < 0) {
		if ((height += top) < 0)
			return false;
		n->top = -top;
		top = 0;
	}

	n->dst_ptr = o->dst_ptr + left + top * (n->pitch = o->pitch);

	if ((t=height + top - o->height) > 0) {
		if ((height-=t) < 0)
			return false;
	}
	n->height = height;


	return true;
}

static void SetCursorSprite(uint cursor)
{
	CursorVars *cv = &_cursor;
	byte *p;

	if (cv->sprite == cursor)
		return;

	p =	GetSpritePtr(cursor & 0x3FFF);
	cv->sprite = cursor;
	cv->size.y = *(byte*)(p+1);
	cv->size.x = READ_LE_UINT16(p+2);
	cv->offs.x = (int16)READ_LE_UINT16(p+4);
	cv->offs.y = (int16)READ_LE_UINT16(p+6);

	cv->dirty = true;
}

static void SwitchAnimatedCursor(void)
{
	CursorVars *cv = &_cursor;
	const uint16 *cur;
	uint sprite;

	cur = cv->animate_cur;
	if (cur == NULL || *cur == 0xFFFF)
		cur = cv->animate_list;
	sprite = cur[0];
	cv->animate_timeout = cur[1];
	cv->animate_cur = cur + 2;

	SetCursorSprite(sprite);
}

void CursorTick(void)
{
	CursorVars *cv = &_cursor;
	if (cv->animate_timeout && !--cv->animate_timeout)
		SwitchAnimatedCursor();
}

void SetMouseCursor(uint cursor)
{
	// Turn off animation
	_cursor.animate_timeout = 0;
	// Set cursor
	SetCursorSprite(cursor);
}

void SetAnimatedMouseCursor(const uint16 *table)
{
	_cursor.animate_list = table;
	_cursor.animate_cur = NULL;
	SwitchAnimatedCursor();
}

bool ChangeResInGame(int w, int h)
{
	if ((_screen.width != w || _screen.height != h) && !_video_driver->change_resolution(w, h))
		return false;

	_cur_resolution[0] = w;
	_cur_resolution[1] = h;
	return true;
}

void ToggleFullScreen(const bool full_screen)
{
	_fullscreen = full_screen;
	/* use preset resolutions, not _screen.height and _screen.width. On windows for example
	   if Desktop-size is 1280x1024, and gamesize is also 1280x1024, _screen.height will be
		 only 1000 because of possible start-bar. For this reason you cannot switch to
		 fullscreen mode from this resolution. Use of preset resolution will fix this */
	if (!_video_driver->change_resolution(_cur_resolution[0], _cur_resolution[1]))
		_fullscreen ^= true; // switching resolution failed, put back full_screen to original status
}

uint16 GetDrawStringPlayerColor(byte player)
{
	// Get the color for DrawString-subroutines which matches the color
	//  of the player
	if (player == OWNER_SPECTATOR || player == OWNER_SPECTATOR - 1)
			return 1;
	return (_color_list[_player_colors[player]].window_color_1b) | IS_PALETTE_COLOR;
}