/* ScummVM - Scumm Interpreter
 * Copyright (C) 2004 Ivan Dubrov
 * Copyright (C) 2004-2005 The ScummVM project
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $Header: /cvsroot/scummvm/scummvm/gob/game.cpp,v 1.27.2.1 2005/10/18 02:11:18 sev Exp $
 *
 */
#include "gob/gob.h"
#include "gob/global.h"
#include "gob/game.h"
#include "gob/video.h"
#include "gob/dataio.h"
#include "gob/pack.h"
#include "gob/scenery.h"
#include "gob/inter.h"
#include "gob/parse.h"
#include "gob/draw.h"
#include "gob/mult.h"
#include "gob/util.h"
#include "gob/goblin.h"
#include "gob/cdrom.h"

namespace Gob {

Game_ExtTable *game_extTable = 0;

char *game_totFileData = 0;
Game_TotTextTable *game_totTextData;
Game_TotResTable *game_totResourceTable = 0;
char *game_imFileData = 0;
int16 game_extHandle = 0;
char game_curExtFile[14];
char game_curTotFile[14];
char game_curImaFile[18];

Game_Collision *game_collisionAreas = 0;
char game_shouldPushColls = 0;
char game_collStr[256];

int16 game_lastCollKey;
int16 game_lastCollAreaIndex;
int16 game_lastCollId;

char game_handleMouse;
char game_forceHandleMouse;

char game_tempStr[256];

int16 game_activeCollResId;
int16 game_activeCollIndex;

// Collisions stack
int16 game_collStackSize = 0;
Game_Collision *game_collStack[3];
int16 game_collStackElemSizes[3];

int16 game_mouseButtons = 0;

// Capture
static Common::Rect game_captureStack[20];
static int16 game_captureCount = 0;

Snd_SoundDesc *game_soundSamples[20];

char game_soundFromExt[20];
char game_totToLoad[20];

int32 game_startTimeKey;

char *game_loadExtData(int16 itemId, int16 *pResWidth, int16 *pResHeight) {
	int16 commonHandle;
	int16 itemsCount;
	int32 offset;
	uint32 size;
	Game_ExtItem *item;
	char isPacked;
	int16 handle;
	int32 tableSize;
	char path[20];
	char *dataBuf;
	char *packedBuf;
	char *dataPtr;

	itemId -= 30000;
	if (game_extTable == 0)
		return 0;

	commonHandle = -1;
	itemsCount = game_extTable->itemsCount;
	item = &game_extTable->items[itemId];
	tableSize = szGame_ExtTable + szGame_ExtItem * itemsCount;

	offset = item->offset;
	size = item->size;
	if (item->width & 0x8000)
		isPacked = 1;
	else
		isPacked = 0;

	if (pResWidth != 0) {
		*pResWidth = item->width & 0x7fff;
		*pResHeight = item->height;
		debug(7, "game_loadExtData(%d, %d, %d)", itemId, *pResWidth, *pResHeight);
	}

	debug(7, "game_loadExtData(%d, 0, 0)", itemId);

	if (item->height == 0)
		size += (item->width & 0x7fff) << 16;

	debug(7, "size: %d off: %d", size, offset);
	if (offset >= 0) {
		handle = game_extHandle;
	} else {
		offset = -(offset + 1);
		tableSize = 0;
		data_closeData(game_extHandle);
		strcpy(path, "commun.ex1");
		path[strlen(path) - 1] = *(game_totFileData + 0x3c) + '0';
		commonHandle = data_openData(path);
		handle = commonHandle;
	}

	debug(7, "off: %ld size: %ld", offset, tableSize);
	data_seekData(handle, offset + tableSize, SEEK_SET);
	if (isPacked)
		dataBuf = (char *)malloc(size);
	else
		dataBuf = (char *)malloc(size);

	dataPtr = dataBuf;
	while (size > 32000) {
		// BUG: huge->far conversion. Need normalization?
		data_readData(handle, (char *)dataPtr, 32000);
		size -= 32000;
		dataPtr += 32000;
	}
	data_readData(handle, (char *)dataPtr, size);
	if (commonHandle != -1) {
		data_closeData(commonHandle);
		game_extHandle = data_openData(game_curExtFile);
	}

	if (isPacked != 0) {
		packedBuf = dataBuf;
		dataBuf = (char *)malloc(READ_LE_UINT32(packedBuf));
		unpackData(packedBuf, dataBuf);
		free(packedBuf);
	}

	return dataBuf;

}

void game_clearCollisions() {
	int16 i;
	for (i = 0; i < 250; i++) {
		game_collisionAreas[i].id = 0;
		game_collisionAreas[i].left = -1;
	}
}

void game_addNewCollision(int16 id, int16 left, int16 top, int16 right, int16 bottom,
	    int16 flags, int16 key, int16 funcEnter, int16 funcLeave) {
	int16 i;
	Game_Collision *ptr;

	debug(5, "game_addNewCollision");
	debug(5, "id = %x", id);
	debug(5, "left = %d, top = %d, right = %d, bottom = %d", left, top, right, bottom);
	debug(5, "flags = %x, key = %x", flags, key);
	debug(5, "funcEnter = %d, funcLeave = %d", funcEnter, funcLeave);

	for (i = 0; i < 250; i++) {
		if (game_collisionAreas[i].left != -1)
			continue;

		ptr = &game_collisionAreas[i];
		ptr->id = id;
		ptr->left = left;
		ptr->top = top;
		ptr->right = right;
		ptr->bottom = bottom;
		ptr->flags = flags;
		ptr->key = key;
		ptr->funcEnter = funcEnter;
		ptr->funcLeave = funcLeave;
		return;
	}
	error("game_addNewCollision: Collision array full!\n");
}

void game_freeCollision(int16 id) {
	int16 i;

	for (i = 0; i < 250; i++) {
		if (game_collisionAreas[i].id == id)
			game_collisionAreas[i].left = -1;
	}
}

void game_pushCollisions(char all) {
	Game_Collision *srcPtr;
	Game_Collision *destPtr;
	int16 size;

	debug(4, "game_pushCollisions");
	for (size = 0, srcPtr = game_collisionAreas; srcPtr->left != -1;
	    srcPtr++) {
		if (all || (srcPtr->id & 0x8000))
			size++;
	}

	destPtr = (Game_Collision *)malloc(size * sizeof(Game_Collision));
	game_collStack[game_collStackSize] = destPtr;
	game_collStackElemSizes[game_collStackSize] = size;
	game_collStackSize++;

	for (srcPtr = game_collisionAreas; srcPtr->left != -1; srcPtr++) {
		if (all || (srcPtr->id & 0x8000)) {
			memcpy(destPtr, srcPtr, sizeof(Game_Collision));
			srcPtr->left = -1;
			destPtr++;
		}
	}
}

void game_popCollisions(void) {
	Game_Collision *destPtr;
	Game_Collision *srcPtr;

	debug(4, "game_popCollision");

	game_collStackSize--;
	for (destPtr = game_collisionAreas; destPtr->left != -1; destPtr++);

	srcPtr = game_collStack[game_collStackSize];
	memcpy(destPtr, srcPtr,
	    game_collStackElemSizes[game_collStackSize] *
	    sizeof(Game_Collision));

	free(game_collStack[game_collStackSize]);
}

int16 game_checkMousePoint(int16 all, int16 *resId, int16 *resIndex) {
	Game_Collision *ptr;
	int16 i;

	if (resId != 0)
		*resId = 0;

	*resIndex = 0;

	ptr = game_collisionAreas;
	for (i = 0; ptr->left != -1; ptr++, i++) {
		if (all) {
			if ((ptr->flags & 0xf) > 1)
				continue;

			if ((ptr->flags & 0xff00) != 0)
				continue;

			if (inter_mouseX < ptr->left
			    || inter_mouseX > ptr->right
			    || inter_mouseY < ptr->top
			    || inter_mouseY > ptr->bottom)
				continue;

			if (resId != 0)
				*resId = ptr->id;

			*resIndex = i;
			return ptr->key;
		} else {
			if ((ptr->flags & 0xff00) != 0)
				continue;

			if ((ptr->flags & 0xf) != 1 && (ptr->flags & 0xf) != 2)
				continue;

			if ((ptr->flags & 0xf0) >> 4 != game_mouseButtons - 1
			    && (ptr->flags & 0xf0) >> 4 != 2)
				continue;

			if (inter_mouseX < ptr->left
			    || inter_mouseX > ptr->right
			    || inter_mouseY < ptr->top
			    || inter_mouseY > ptr->bottom)
				continue;

			if (resId != 0)
				*resId = ptr->id;
			*resIndex = i;
			return ptr->key;
		}
	}

	if (game_mouseButtons != 1 && all == 0)
		return 0x11b;

	return 0;
}

void game_capturePush(int16 left, int16 top, int16 width, int16 height) {
	int16 right;

	if (game_captureCount == 20)
		error("game_capturePush: Capture stack overflow!");

	game_captureStack[game_captureCount].left = left;
	game_captureStack[game_captureCount].top = top;
	game_captureStack[game_captureCount].right = left + width;
	game_captureStack[game_captureCount].bottom = top + height;

	draw_spriteTop = top;
	draw_spriteBottom = height;

	right = left + width - 1;
	left &= 0xfff0;
	right |= 0xf;

	draw_spritesArray[30 + game_captureCount] =
	    vid_initSurfDesc(videoMode, right - left + 1, height, 0);

	draw_sourceSurface = 21;
	draw_destSurface = 30 + game_captureCount;

	draw_spriteLeft = left;
	draw_spriteRight = right - left + 1;
	draw_destSpriteX = 0;
	draw_destSpriteY = 0;
	draw_transparency = 0;
	draw_spriteOperation(0);
	game_captureCount++;
}

void game_capturePop(char doDraw) {
	if (game_captureCount <= 0)
		return;

	game_captureCount--;
	if (doDraw) {
		draw_destSpriteX = game_captureStack[game_captureCount].left;
		draw_destSpriteY = game_captureStack[game_captureCount].top;
		draw_spriteRight =
		    game_captureStack[game_captureCount].width();
		draw_spriteBottom =
		    game_captureStack[game_captureCount].height();

		draw_transparency = 0;
		draw_sourceSurface = 30 + game_captureCount;
		draw_destSurface = 21;
		draw_spriteLeft = draw_destSpriteX & 0xf;
		draw_spriteTop = 0;
		draw_spriteOperation(0);
	}
	vid_freeSurfDesc(draw_spritesArray[30 + game_captureCount]);
}

char *game_loadTotResource(int16 id) {
	Game_TotResItem *itemPtr;
	int32 offset;

	itemPtr = &game_totResourceTable->items[id];
	offset = itemPtr->offset;
	if (offset >= 0) {
		return ((char *)game_totResourceTable) + szGame_TotResTable +
		    szGame_TotResItem * game_totResourceTable->itemsCount + offset;
	} else {
		return (char *)(game_imFileData + (int32)READ_LE_UINT32(&((int32 *)game_imFileData)[-offset - 1]));
	}
}

void game_loadSound(int16 slot, char *dataPtr) {
	Snd_SoundDesc *soundDesc;

	soundDesc = (Snd_SoundDesc *)malloc(sizeof(Snd_SoundDesc));

	game_soundSamples[slot] = soundDesc;

	soundDesc->frequency = (dataPtr[4] << 8) + dataPtr[5];
	soundDesc->size = (dataPtr[1] << 16) + (dataPtr[2] << 8) + dataPtr[3];
	soundDesc->data = dataPtr + 6;
	soundDesc->timerTicks = (int32)1193180 / (int32)soundDesc->frequency;

	soundDesc->inClocks = (soundDesc->frequency * 10) / 182;
	soundDesc->flag = 0;
}

void game_interLoadSound(int16 slot) {
	char *dataPtr;
	int16 id;

	if (slot == -1)
		slot = parse_parseValExpr();

	id = inter_load16();
	if (id == -1) {
		inter_execPtr += 9;
		return;
	}

	if (id >= 30000) {
		dataPtr = game_loadExtData(id, 0, 0);
		game_soundFromExt[slot] = 1;
	} else {
		dataPtr = game_loadTotResource(id);
		game_soundFromExt[slot] = 0;
	}

	game_loadSound(slot, dataPtr);
}

void game_freeSoundSlot(int16 slot) {
	if (slot == -1)
		slot = parse_parseValExpr();

	if (game_soundSamples[slot] == 0)
		return;

	if (game_soundFromExt[slot] == 1) {
		free(game_soundSamples[slot]->data - 6);
		game_soundFromExt[slot] = 0;
	}

	free(game_soundSamples[slot]);
	game_soundSamples[slot] = 0;
}

int16 game_checkKeys(int16 *pMouseX, int16 *pMouseY, int16 *pButtons, char handleMouse) {
	util_processInput();

	if (VAR(58) != 0) {
		if (mult_frameStart != (int)VAR(58) - 1)
			mult_frameStart++;
		else
			mult_frameStart = 0;

		mult_playMult(mult_frameStart + VAR(57), mult_frameStart + VAR(57), 1,
		    handleMouse);
	}

	if (inter_soundEndTimeKey != 0
	    && util_getTimeKey() >= inter_soundEndTimeKey) {
		snd_stopSound(inter_soundStopVal);
		inter_soundEndTimeKey = 0;
	}

	if (useMouse == 0)
		error("game_checkKeys: Can't work without mouse!");

	util_getMouseState(pMouseX, pMouseY, pButtons);

	if (*pButtons == 3)
		*pButtons = 0;

	return util_checkKey();
}

int16 game_checkCollisions(char handleMouse, int16 deltaTime, int16 *pResId,
	    int16 *pResIndex) {
	char *savedIP;
	int16 resIndex;
	int16 key;
	int16 oldIndex;
	int16 oldId;
	uint32 timeKey;

	if (deltaTime >= -1) {
		game_lastCollKey = 0;
		game_lastCollAreaIndex = 0;
		game_lastCollId = 0;
	}

	if (pResId != 0)
		*pResId = 0;

	resIndex = 0;

	if (draw_cursorIndex == -1 && handleMouse != 0
	    && game_lastCollKey == 0) {
		game_lastCollKey =
		    game_checkMousePoint(1, &game_lastCollId,
		    &game_lastCollAreaIndex);

		if (game_lastCollKey != 0 && (game_lastCollId & 0x8000) != 0) {
			savedIP = inter_execPtr;
			inter_execPtr = (char *)game_totFileData +
			    game_collisionAreas[game_lastCollAreaIndex].funcEnter;

			inter_funcBlock(0);
			inter_execPtr = savedIP;
		}
	}

	if (handleMouse != 0)
		draw_animateCursor(-1);

	timeKey = util_getTimeKey();
	while (1) {
		if (inter_terminate != 0) {
			if (handleMouse)
				draw_blitCursor();
			return 0;
		}

		if (draw_noInvalidated == 0) {
			if (handleMouse)
				draw_animateCursor(-1);
			else
				draw_blitInvalidated();
		}

		// NOTE: the original asm does the below game_checkKeys call
		// _before_ this check. However, that can cause keypresses to get lost
		// since there's a return statement in this check.
		// Additionally, I added a 'deltaTime == -1' check there, since
		// when this function is called with deltaTime == -1 in game_inputArea,
		// and the return value is then discarded.
		if (deltaTime < 0) {
			uint32 curtime = util_getTimeKey();
			if (deltaTime == -1 || curtime + deltaTime > timeKey) {
				if (pResId != 0)
					*pResId = 0;

				if (pResIndex != 0)
					*pResIndex = 0;

				return 0;
			}
		}

		key = game_checkKeys(&inter_mouseX, &inter_mouseY,
							 &game_mouseButtons, handleMouse);

		if (handleMouse == 0 && game_mouseButtons != 0) {
			util_waitMouseRelease(0);
			key = 3;
		}

		if (key != 0) {

			if (handleMouse == 1)
				draw_blitCursor();

			if (pResId != 0)
				*pResId = 0;

			if (pResIndex != 0)
				*pResIndex = 0;

			if (game_lastCollKey != 0 &&
			    game_collisionAreas[game_lastCollAreaIndex].funcLeave != 0) {
				savedIP = inter_execPtr;
				inter_execPtr = (char *)game_totFileData +
				    game_collisionAreas[game_lastCollAreaIndex].funcLeave;

				inter_funcBlock(0);
				inter_execPtr = savedIP;
			}

			game_lastCollKey = 0;
			if (key != 0)
				return key;
		}

		if (handleMouse != 0) {
			if (game_mouseButtons != 0) {
				oldIndex = 0;

				draw_animateCursor(2);
				if (deltaTime <= 0) {
					if (handleMouse == 1)
						util_waitMouseRelease(1);
				} else if (deltaTime > 0) {
					util_delay(deltaTime);
				}

				draw_animateCursor(-1);
				if (pResId != 0)
					*pResId = 0;

				key = game_checkMousePoint(0, pResId, &resIndex);

				if (pResIndex != 0)
					*pResIndex = resIndex;

				if (key != 0 || (pResId != 0 && *pResId != 0)) {
					if (handleMouse == 1 && (deltaTime <= 0
						|| game_mouseButtons == 0))
						draw_blitCursor();

					if (game_lastCollKey != 0 &&
						game_collisionAreas[game_lastCollAreaIndex].funcLeave != 0) {
						savedIP = inter_execPtr;
						inter_execPtr =
						    (char *)game_totFileData +
						    game_collisionAreas[game_lastCollAreaIndex].funcLeave;

						inter_funcBlock(0);
						inter_execPtr = savedIP;
					}
					game_lastCollKey = 0;
					return key;
				}

				if (game_lastCollKey != 0 &&
				    game_collisionAreas[game_lastCollAreaIndex].funcLeave != 0) {
					savedIP = inter_execPtr;
					inter_execPtr =
					    (char *)game_totFileData +
					    game_collisionAreas[game_lastCollAreaIndex].funcLeave;

					inter_funcBlock(0);
					inter_execPtr = savedIP;
				}

				game_lastCollKey =
				    game_checkMousePoint(1, &game_lastCollId,
				    &game_lastCollAreaIndex);

				if (game_lastCollKey != 0
				    && (game_lastCollId & 0x8000) != 0) {
					savedIP = inter_execPtr;
					inter_execPtr =
					    (char *)game_totFileData +
					    game_collisionAreas[game_lastCollAreaIndex].funcEnter;

					inter_funcBlock(0);
					inter_execPtr = savedIP;
				}
			} else {

				if (handleMouse != 0 &&
				    (inter_mouseX != draw_cursorX
					|| inter_mouseY != draw_cursorY)) {
					oldIndex = game_lastCollAreaIndex;
					oldId = game_lastCollId;

					key =
					    game_checkMousePoint(1,
					    &game_lastCollId,
					    &game_lastCollAreaIndex);

					if (key != game_lastCollKey) {
						if (game_lastCollKey != 0
						    && (oldId & 0x8000) != 0) {
							savedIP = inter_execPtr;
							inter_execPtr = (char *)game_totFileData +
							    game_collisionAreas[oldIndex].funcLeave;

							inter_funcBlock(0);
							inter_execPtr = savedIP;
						}

						game_lastCollKey = key;
						if (game_lastCollKey != 0 && (game_lastCollId & 0x8000) != 0) {
							savedIP = inter_execPtr;
							inter_execPtr = (char *)game_totFileData +
							    game_collisionAreas[game_lastCollAreaIndex].funcEnter;

							inter_funcBlock(0);
							inter_execPtr = savedIP;
						}
					}
				}
			}
		}

		if (handleMouse != 0)
			draw_animateCursor(-1);

		util_delay(10);

		snd_loopSounds();
	}
}

int16 game_inputArea(int16 xPos, int16 yPos, int16 width, int16 height, int16 backColor,
	    int16 frontColor, char *str, int16 fontIndex, char inpType, int16 *pTotTime) {
	int16 handleMouse;
	uint32 editSize;
	FontDesc *pFont;
	char curSym;
	int16 key;
	const char *str1;
	const char *str2;
	int16 i;
	uint32 pos;
	int16 flag;
	int16 savedKey;

	if (game_handleMouse != 0 &&
	    (useMouse != 0 || game_forceHandleMouse != 0))
		handleMouse = 1;
	else
		handleMouse = 0;

	pos = strlen(str);
	pFont = draw_fonts[fontIndex];
	editSize = width / pFont->itemWidth;

	while (1) {
		strcpy(game_tempStr, str);
		strcat(game_tempStr, " ");
		if (strlen(game_tempStr) > editSize)
			strcpy(game_tempStr, str);

		draw_destSpriteX = xPos;
		draw_destSpriteY = yPos;
		draw_spriteRight = editSize * pFont->itemWidth;
		draw_spriteBottom = height;

		draw_destSurface = 21;
		draw_backColor = backColor;
		draw_frontColor = frontColor;
		draw_textToPrint = game_tempStr;
		draw_transparency = 1;
		draw_fontIndex = fontIndex;
		draw_spriteOperation(DRAW_FILLRECT);

		draw_destSpriteY = yPos + (height - 8) / 2;

		draw_spriteOperation(DRAW_PRINTTEXT);
		if (pos == editSize)
			pos--;

		curSym = game_tempStr[pos];

		flag = 1;

		while (1) {
			game_tempStr[0] = curSym;
			game_tempStr[1] = 0;

			draw_destSpriteX = xPos + pFont->itemWidth * pos;
			draw_destSpriteY = yPos + height - 1;
			draw_spriteRight = pFont->itemWidth;
			draw_spriteBottom = 1;
			draw_destSurface = 21;
			draw_backColor = frontColor;
			draw_spriteOperation(DRAW_FILLRECT);

			if (flag != 0) {
				key = game_checkCollisions(handleMouse, -1,
				    &game_activeCollResId,
				    &game_activeCollIndex);
			}
			flag = 0;

			key = game_checkCollisions(handleMouse, -300,
			    &game_activeCollResId, &game_activeCollIndex);

			if (*pTotTime > 0) {
				*pTotTime -= 300;
				if (*pTotTime <= 1) {
					key = 0;
					game_activeCollResId = 0;
					break;
				}
			}

			game_tempStr[0] = curSym;
			game_tempStr[1] = 0;
			draw_destSpriteX = xPos + pFont->itemWidth * pos;
			draw_destSpriteY = yPos + height - 1;
			draw_spriteRight = pFont->itemWidth;
			draw_spriteBottom = 1;
			draw_destSurface = 21;
			draw_backColor = backColor;
			draw_frontColor = frontColor;
			draw_textToPrint = game_tempStr;
			draw_transparency = 1;
			draw_spriteOperation(DRAW_FILLRECT);

			draw_destSpriteY = yPos + (height - 8) / 2;
			draw_spriteOperation(DRAW_PRINTTEXT);

			if (key != 0 || game_activeCollResId != 0)
				break;

			key = game_checkCollisions(handleMouse, -300,
			    &game_activeCollResId, &game_activeCollIndex);

			if (*pTotTime > 0) {
				*pTotTime -= 300;
				if (*pTotTime <= 1) {
					key = 0;
					game_activeCollResId = 0;
					break;
				}

			}
			if (key != 0 || game_activeCollResId != 0)
				break;

			if (inter_terminate != 0)
				return 0;
		}

		if (key == 0 || game_activeCollResId != 0
		    || inter_terminate != 0)
			return 0;

		switch (key) {
		case 0x4d00:	// Right Arrow
			if (pos < strlen(str) && pos < editSize - 1) {
				pos++;
				continue;
			}
			return 0x5000;

		case 0x4b00:	// Left Arrow
			if (pos > 0) {
				pos--;
				continue;
			}
			return 0x4800;

		case 0xe08:	// Backspace
			if (pos > 0) {
				util_cutFromStr(str, pos - 1, 1);
				pos--;
				continue;
			}

		case 0x5300:	// Del

			if (pos >= strlen(str))
				continue;

			util_cutFromStr(str, pos, 1);
			continue;

		case 0x1c0d:	// Enter
		case 0x3b00:	// F1
		case 0x3c00:	// F2
		case 0x3d00:	// F3
		case 0x3e00:	// F4
		case 0x3f00:	// F5
		case 0x4000:	// F6
		case 0x4100:	// F7
		case 0x4200:	// F8
		case 0x4300:	// F9
		case 0x4400:	// F10
		case 0x4800:	// Up arrow
		case 0x5000:	// Down arrow
			return key;

		case 0x11b:	// Escape
			if (useMouse != 0)
				continue;

			game_forceHandleMouse = !game_forceHandleMouse;

			if (game_handleMouse != 0 &&
			    (useMouse != 0 || game_forceHandleMouse != 0))
				handleMouse = 1;
			else
				handleMouse = 0;

			if (pressedKeys[1] == 0)
				continue;

			while (pressedKeys[1] != 0);
			continue;

		default:

			savedKey = key;
			key &= 0xff;

			if ((inpType == 9 || inpType == 10) && key >= ' '
			    && key <= 0xff) {
				str1 = "0123456789-.,+ ";
				str2 = "0123456789-,,+ ";

				if ((savedKey >> 8) > 1
				    && (savedKey >> 8) < 12)
					key = ((savedKey >> 8) - 1) % 10 + '0';

				for (i = 0; str1[i] != 0; i++) {
					if (key == str1[i]) {
						key = str2[i];
						break;
					}
				}

				if (i == (int16)strlen(str1))
					key = 0;
			}

			if (key >= ' ' && key <= 0xff) {
				if (editSize == strlen(str))
					util_cutFromStr(str, strlen(str) - 1,
					    1);

				if (key >= 'a' && key <= 'z')
					key += ('A' - 'a');

				pos++;
				game_tempStr[0] = key;
				game_tempStr[1] = 0;

				util_insertStr(game_tempStr, str, pos - 1);

				//strupr(str);
			}
		}
	}
}

int16 game_multiEdit(int16 time, int16 index, int16 *pCurPos, Game_InputDesc * inpDesc) {
	Game_Collision *collArea;
	int16 descInd;
	int16 key;
	int16 found = -1;
	int16 i;

	descInd = 0;
	for (i = 0; i < 250; i++) {
		collArea = &game_collisionAreas[i];

		if (collArea->left == -1)
			continue;

		if ((collArea->id & 0x8000) == 0)
			continue;

		if ((collArea->flags & 0x0f) < 3)
			continue;

		if ((collArea->flags & 0x0f) > 10)
			continue;

		strcpy(game_tempStr, inter_variables + collArea->key);

		draw_destSpriteX = collArea->left;
		draw_destSpriteY = collArea->top;
		draw_spriteRight = collArea->right - collArea->left + 1;
		draw_spriteBottom = collArea->bottom - collArea->top + 1;

		draw_destSurface = 21;

		draw_backColor = inpDesc[descInd].backColor;
		draw_frontColor = inpDesc[descInd].frontColor;
		draw_textToPrint = game_tempStr;
		draw_transparency = 1;
		draw_fontIndex = inpDesc[descInd].fontIndex;
		draw_spriteOperation(DRAW_FILLRECT);
		draw_destSpriteY +=
		    ((collArea->bottom - collArea->top + 1) - 8) / 2;

		draw_spriteOperation(DRAW_PRINTTEXT);
		descInd++;
	}

	for (i = 0; i < 40; i++) {
		WRITE_VAR_OFFSET(i * 4 + 0x44, 0);
	}

	while (1) {
		descInd = 0;

		for (i = 0; i < 250; i++) {
			collArea = &game_collisionAreas[i];

			if (collArea->left == -1)
				continue;

			if ((collArea->id & 0x8000) == 0)
				continue;

			if ((collArea->flags & 0x0f) < 3)
				continue;

			if ((collArea->flags & 0x0f) > 10)
				continue;

			if (descInd == *pCurPos) {
				found = i;
				break;
			}

			descInd++;
		}

		assert(found != -1);

		collArea = &game_collisionAreas[found];

		key = game_inputArea(collArea->left, collArea->top,
		    collArea->right - collArea->left + 1,
		    collArea->bottom - collArea->top + 1,
		    inpDesc[*pCurPos].backColor, inpDesc[*pCurPos].frontColor,
		    inter_variables + collArea->key,
		    inpDesc[*pCurPos].fontIndex, collArea->flags, &time);

		if (inter_terminate != 0)
			return 0;

		switch (key) {
		case 0:
			if (game_activeCollResId == 0)
				return 0;

			if ((game_collisionAreas[game_activeCollIndex].
				flags & 0x0f) < 3)
				return 0;

			if ((game_collisionAreas[game_activeCollIndex].
				flags & 0x0f) > 10)
				return 0;

			*pCurPos = 0;
			for (i = 0; i < 250; i++) {
				collArea = &game_collisionAreas[i];

				if (collArea->left == -1)
					continue;

				if ((collArea->id & 0x8000) == 0)
					continue;

				if ((collArea->flags & 0x0f) < 3)
					continue;

				if ((collArea->flags & 0x0f) > 10)
					continue;

				if (i == game_activeCollIndex)
					break;

				pCurPos[0]++;
			}
			break;

		case 0x3b00:
		case 0x3c00:
		case 0x3d00:
		case 0x3e00:
		case 0x3f00:
		case 0x4000:
		case 0x4100:
		case 0x4200:
		case 0x4300:
		case 0x4400:
			return key;

		case 0x1c0d:

			if (index == 1)
				return key;

			if (*pCurPos == index - 1) {
				*pCurPos = 0;
				break;
			}

			pCurPos[0]++;
			break;

		case 0x5000:
			if (index - 1 > *pCurPos)
				pCurPos[0]++;
			break;

		case 0x4800:
			if (*pCurPos > 0)
				pCurPos[0]--;
			break;
		}
	}
}

int16 game_adjustKey(int16 key) {
	if (key <= 0x60 || key >= 0x7b)
		return key;

	return key - 0x20;
}

void game_collisionsBlock(void) {
	Game_InputDesc descArray[20];
	int16 array[250];
	char count;
	int16 collResId;
	char *startIP;
	int16 curCmd;
	int16 cmd;
	int16 cmdHigh;
	int16 key;
	int16 flags;
	int16 left;
	int16 top;
	int16 width;
	int16 height;
	int16 var_22;
	int16 index;
	int16 curEditIndex;
	int16 deltaTime;
	int16 descIndex2;
	int16 stackPos2;
	int16 descIndex;
	int16 timeVal;
	char *str;
	int16 pos;
	int16 savedCollStackSize;
	int16 i;
	int16 counter;
	int16 var_24;
	int16 var_26;
	int16 collStackPos;
	Game_Collision *collPtr;
	int16 timeKey;
	char *savedIP;

	if (game_shouldPushColls)
		game_pushCollisions(1);

	collResId = -1;
	inter_execPtr++;
	count = *inter_execPtr++;
	game_handleMouse = inter_execPtr[0];
	deltaTime = 1000 * (byte)inter_execPtr[1];
	descIndex2 = (byte)inter_execPtr[2];
	stackPos2 = (byte)inter_execPtr[3];
	descIndex = (byte)inter_execPtr[4];

	if (stackPos2 != 0 || descIndex != 0)
		deltaTime /= 100;

	timeVal = deltaTime;
	inter_execPtr += 6;

	startIP = inter_execPtr;
	WRITE_VAR(16, 0);
	var_22 = 0;
	index = 0;
	curEditIndex = 0;

	for (curCmd = 0; curCmd < count; curCmd++) {
		array[curCmd] = 0;
		cmd = *inter_execPtr++;

		if ((cmd & 0x40) != 0) {
			cmd -= 0x40;
			cmdHigh = (byte)*inter_execPtr;
			inter_execPtr++;
			cmdHigh <<= 8;
		} else {
			cmdHigh = 0;
		}

		if ((cmd & 0x80) != 0) {
			left = parse_parseValExpr();
			top = parse_parseValExpr();
			width = parse_parseValExpr();
			height = parse_parseValExpr();
		} else {
			left = inter_load16();
			top = inter_load16();
			width = inter_load16();
			height = inter_load16();
		}
		cmd &= 0x7f;

		debug(4, "game_collisionsBlock(%d)", cmd);

		switch (cmd) {
		case 3:
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:
		case 9:
		case 10:

			util_waitKey();
			var_22 = 1;
			key = parse_parseVarIndex();
			descArray[index].fontIndex = inter_load16();
			descArray[index].backColor = *inter_execPtr++;
			descArray[index].frontColor = *inter_execPtr++;

			if (cmd < 5 || cmd > 8) {
				descArray[index].ptr = 0;
			} else {
				descArray[index].ptr = inter_execPtr + 2;
				inter_execPtr += inter_load16();
			}

			if (left == -1)
				break;

			if ((cmd & 1) == 0) {
				game_addNewCollision(curCmd + 0x8000, left,
				    top,
				    left +
				    width *
				    draw_fonts[descArray[index].fontIndex]->
				    itemWidth - 1, top + height - 1, cmd, key,
				    0,
				    inter_execPtr - (char *)game_totFileData);

				inter_execPtr += 2;
				inter_execPtr += READ_LE_UINT16(inter_execPtr);
			} else {
				game_addNewCollision(curCmd + 0x8000, left,
				    top,
				    left +
				    width *
				    draw_fonts[descArray[index].fontIndex]->
				    itemWidth - 1, top + height - 1, cmd, key,
				    0, 0);
			}
			index++;
			break;

		case 21:
			key = inter_load16();
			array[curCmd] = inter_load16();
			flags = inter_load16() & 3;

			game_addNewCollision(curCmd + 0x8000, left, top,
			    left + width - 1,
			    top + height - 1,
			    (flags << 4) + cmdHigh + 2, key,
			    inter_execPtr - (char *)game_totFileData, 0);

			inter_execPtr += 2;
			inter_execPtr += READ_LE_UINT16(inter_execPtr);
			break;

		case 20:
			collResId = curCmd;
			// Fall through to case 2

		case 2:
			key = inter_load16();
			array[curCmd] = inter_load16();
			flags = inter_load16() & 3;

			game_addNewCollision(curCmd + 0x8000, left, top,
			    left + width - 1,
			    top + height - 1,
			    (flags << 4) + cmdHigh + 2, key, 0,
			    inter_execPtr - (char *)game_totFileData);

			inter_execPtr += 2;
			inter_execPtr += READ_LE_UINT16(inter_execPtr);
			break;

		case 0:
			inter_execPtr += 6;
			startIP = inter_execPtr;
			inter_execPtr += 2;
			inter_execPtr += READ_LE_UINT16(inter_execPtr);
			key = curCmd + 0xA000;

			game_addNewCollision(curCmd + 0x8000, left, top,
			    left + width - 1,
			    top + height - 1,
			    cmd + cmdHigh, key,
			    startIP - (char *)game_totFileData,
			    inter_execPtr - (char *)game_totFileData);

			inter_execPtr += 2;
			inter_execPtr += READ_LE_UINT16(inter_execPtr);
			break;

		case 1:
			key = inter_load16();
			array[curCmd] = inter_load16();
			flags = inter_load16() & 3;

			startIP = inter_execPtr;
			inter_execPtr += 2;
			inter_execPtr += READ_LE_UINT16(inter_execPtr);
			if (key == 0)
				key = curCmd + 0xa000;

			game_addNewCollision(curCmd + 0x8000, left, top,
			    left + width - 1,
			    top + height - 1,
			    (flags << 4) + cmd + cmdHigh, key,
			    startIP - (char *)game_totFileData,
			    inter_execPtr - (char *)game_totFileData);

			inter_execPtr += 2;
			inter_execPtr += READ_LE_UINT16(inter_execPtr);
			break;
		}
	}

	game_forceHandleMouse = 0;
	util_waitKey();

	do {
		if (var_22 != 0) {
			key =
			    game_multiEdit(deltaTime, index, &curEditIndex,
			    descArray);

			if (key == 0x1c0d) {
				for (i = 0; i < 250; i++) {
					if (game_collisionAreas[i].left == -1)
						continue;

					if ((game_collisionAreas[i].id & 0x8000) == 0)
						continue;

					if ((game_collisionAreas[i].flags & 1) != 0)
						continue;

					if ((game_collisionAreas[i].flags & 0x0f) <= 2)
						continue;

					collResId = game_collisionAreas[i].id;
					game_activeCollResId = collResId;
					collResId &= 0x7fff;
					game_activeCollIndex = i;
					break;
				}
				break;
			}
		} else {
			key =
			    game_checkCollisions(game_handleMouse, -deltaTime,
			    &game_activeCollResId, &game_activeCollIndex);
		}

		if ((key & 0xff) >= ' ' && (key & 0xff) <= 0xff &&
		    (key >> 8) > 1 && (key >> 8) < 12) {
			key = '0' + (((key >> 8) - 1) % 10) + (key & 0xff00);
		}

		if (game_activeCollResId == 0) {
			if (key != 0) {
				for (i = 0; i < 250; i++) {
					if (game_collisionAreas[i].left == -1)
						continue;

					if ((game_collisionAreas[i].
						id & 0x8000) == 0)
						continue;

					if (game_collisionAreas[i].key == key
					    || game_collisionAreas[i].key ==
					    0x7fff) {

						game_activeCollResId =
						    game_collisionAreas[i].id;
						game_activeCollIndex = i;
						break;
					}
				}

				if (game_activeCollResId == 0) {
					for (i = 0; i < 250; i++) {
						if (game_collisionAreas[i].left == -1)
							continue;

						if ((game_collisionAreas[i].id & 0x8000) == 0)
							continue;

						if ((game_collisionAreas[i].key & 0xff00) != 0)
							continue;

						if (game_collisionAreas[i].key == 0)
							continue;

						if (game_adjustKey(key & 0xff) == game_adjustKey(game_collisionAreas[i].key) || game_collisionAreas[i].key == 0x7fff) {
							game_activeCollResId = game_collisionAreas[i].id;
							game_activeCollIndex = i;
							break;
						}
					}
				}
			} else {

				if (deltaTime != 0 && VAR(16) == 0) {
					if (stackPos2 != 0) {
						collStackPos = 0;
						collPtr = game_collisionAreas;

						for (i = 0, collPtr = game_collisionAreas; collPtr->left != -1; i++, collPtr++) {
							if ((collPtr->id & 0x8000) == 0)
								continue;

							collStackPos++;
							if (collStackPos != stackPos2)
								continue;

							game_activeCollResId = collPtr->id;
							game_activeCollIndex = i;
							WRITE_VAR(2, inter_mouseX);
							WRITE_VAR(3, inter_mouseY);
							WRITE_VAR(4, game_mouseButtons);
							WRITE_VAR(16, array[(uint16)game_activeCollResId & ~0x8000]);

							if (collPtr->funcLeave != 0) {
								timeKey = util_getTimeKey();
								savedIP = inter_execPtr;
								inter_execPtr = (char *)game_totFileData + collPtr->funcLeave;
								game_shouldPushColls = 1;
								savedCollStackSize = game_collStackSize;
								inter_funcBlock(0);

								if (savedCollStackSize != game_collStackSize)
									game_popCollisions();

								game_shouldPushColls = 0;
								inter_execPtr = savedIP;
								deltaTime = timeVal - (util_getTimeKey() - timeKey);

								if (deltaTime < 2)
									deltaTime = 2;
							}

							if (VAR(16) == 0)
								game_activeCollResId = 0;
							break;
						}
					} else {
						if (descIndex != 0) {
							counter = 0;
							for (i = 0; i < 250; i++) {
								if (game_collisionAreas[i].left == -1)
									continue;

								if ((game_collisionAreas[i].id & 0x8000) == 0)
									continue;

								counter++;
								if (counter != descIndex)
									continue;

								game_activeCollResId = game_collisionAreas[i].id;
								game_activeCollIndex = i;
								break;
							}
						} else {
							for (i = 0; i < 250; i++) {
								if (game_collisionAreas[i].left == -1)
									continue;

								if ((game_collisionAreas[i].id & 0x8000) == 0)
									continue;

								game_activeCollResId = game_collisionAreas[i].id;
								game_activeCollIndex = i;
								break;
							}
						}
					}
				} else {
					if (descIndex2 != 0) {
						counter = 0;
						for (i = 0; i < 250; i++) {
							if (game_collisionAreas[i].left == -1)
								continue;

							if ((game_collisionAreas[i].id & 0x8000) == 0)
								continue;

							counter++;
							if (counter != descIndex2)
								continue;

							game_activeCollResId = game_collisionAreas[i].id;
							game_activeCollIndex = i;
							break;
						}
					}
				}
			}
		}

		if (game_activeCollResId == 0)
			continue;

		if (game_collisionAreas[game_activeCollIndex].funcLeave != 0)
			continue;

		WRITE_VAR(2, inter_mouseX);
		WRITE_VAR(3, inter_mouseY);
		WRITE_VAR(4, game_mouseButtons);
		WRITE_VAR(16, array[(uint16)game_activeCollResId & ~0x8000]);

		if (game_collisionAreas[game_activeCollIndex].funcEnter != 0) {
			savedIP = inter_execPtr;
			inter_execPtr = (char *)game_totFileData +
			    game_collisionAreas[game_activeCollIndex].
			    funcEnter;

			game_shouldPushColls = 1;

			collStackPos = game_collStackSize;
			inter_funcBlock(0);
			if (collStackPos != game_collStackSize)
				game_popCollisions();
			game_shouldPushColls = 0;
			inter_execPtr = savedIP;
		}

		WRITE_VAR(16, 0);
		game_activeCollResId = 0;
	}
	while (game_activeCollResId == 0 && inter_terminate == 0);

	if (((uint16)game_activeCollResId & ~0x8000) == collResId) {
		collStackPos = 0;
		var_24 = 0;
		var_26 = 1;
		for (i = 0; i < 250; i++) {
			if (game_collisionAreas[i].left == -1)
				continue;

			if ((game_collisionAreas[i].id & 0x8000) == 0)
				continue;

			if ((game_collisionAreas[i].flags & 0x0f) < 3)
				continue;

			if ((game_collisionAreas[i].flags & 0x0f) > 10)
				continue;

			if ((game_collisionAreas[i].flags & 0x0f) > 8) {
				strcpy(game_tempStr,
				    inter_variables + game_collisionAreas[i].key);
				while ((pos =
					util_strstr(" ", game_tempStr)) != 0) {
					util_cutFromStr(game_tempStr, pos - 1, 1);
					pos = util_strstr(" ", game_tempStr);
				}
				strcpy(inter_variables + game_collisionAreas[i].key, game_tempStr);
			}

			if ((game_collisionAreas[i].flags & 0x0f) >= 5 &&
			    (game_collisionAreas[i].flags & 0x0f) <= 8) {
				str = descArray[var_24].ptr;

				strcpy(game_tempStr, inter_variables + game_collisionAreas[i].key);

				if ((game_collisionAreas[i].flags & 0x0f) < 7)
					util_prepareStr(game_tempStr);

				pos = 0;
				do {
					strcpy(game_collStr, str);
					pos += strlen(str) + 1;

					str += strlen(str) + 1;

					if ((game_collisionAreas[i].flags & 0x0f) < 7)
						util_prepareStr(game_collStr);

					if (strcmp(game_tempStr, game_collStr) == 0) {
						VAR(17)++;
						WRITE_VAR(17 + var_26, 1);
						break;
					}
				} while (READ_LE_UINT16(descArray[var_24].ptr - 2) > pos);
				collStackPos++;
			} else {
				VAR(17 + var_26) = 2;
			}
			var_24++;
			var_26++;
		}

		if (collStackPos != (int16)VAR(17))
			WRITE_VAR(17, 0);
		else
			WRITE_VAR(17, 1);
	}

	savedIP = 0;
	if (inter_terminate == 0) {
		savedIP = (char *)game_totFileData +
		    game_collisionAreas[game_activeCollIndex].funcLeave;

		WRITE_VAR(2, inter_mouseX);
		WRITE_VAR(3, inter_mouseY);
		WRITE_VAR(4, game_mouseButtons);

		if (VAR(16) == 0) {
			WRITE_VAR(16, array[(uint16)game_activeCollResId & ~0x8000]);
		}
	}

	for (curCmd = 0; curCmd < count; curCmd++) {
		game_freeCollision(curCmd + 0x8000);
	}
	inter_execPtr = savedIP;
}

void game_prepareStart(void) {
	int16 i;

	game_clearCollisions();

	pPaletteDesc->unused2 = draw_unusedPalette2;
	pPaletteDesc->unused1 = draw_unusedPalette1;
	pPaletteDesc->vgaPal = draw_vgaPalette;

	vid_setFullPalette(pPaletteDesc);

	draw_backSurface = vid_initSurfDesc(videoMode, 320, 200, 0);

	vid_fillRect(draw_backSurface, 0, 0, 319, 199, 1);
	draw_frontSurface = pPrimarySurfDesc;
	vid_fillRect(draw_frontSurface, 0, 0, 319, 199, 1);

	util_setMousePos(152, 92);

	draw_cursorX = 152;
	inter_mouseX = 152;

	draw_cursorY = 92;
	inter_mouseY = 92;
	draw_invalidatedCount = 0;
	draw_noInvalidated = 1;
	draw_applyPal = 0;
	draw_paletteCleared = 0;
	draw_cursorWidth = 16;
	draw_cursorHeight = 16;
	draw_transparentCursor = 1;

	for (i = 0; i < 40; i++) {
		draw_cursorAnimLow[i] = -1;
		draw_cursorAnimDelays[i] = 0;
		draw_cursorAnimHigh[i] = 0;
	}

	draw_cursorAnimLow[1] = 0;
	draw_cursorSprites = vid_initSurfDesc(videoMode, 32, 16, 2);
	draw_cursorBack = vid_initSurfDesc(videoMode, 16, 16, 0);
	draw_renderFlags = 0;
	draw_backDeltaX = 0;
	draw_backDeltaY = 0;

	game_startTimeKey = util_getTimeKey();
}

void game_loadTotFile(char *path) {
	int16 handle;

	handle = data_openData(path);
	if (handle >= 0) {
		data_closeData(handle);
		game_totFileData = data_getData(path);
	} else {
		game_totFileData = 0;
	}
}

void game_loadExtTable(void) {
	int16 count, i;

	// Function is correct. [sev]

	game_extHandle = data_openData(game_curExtFile);
	if (game_extHandle < 0)
		return;

	data_readData(game_extHandle, (char *)&count, 2);
	count = FROM_LE_16(count);

	data_seekData(game_extHandle, 0, 0);
	game_extTable = (Game_ExtTable *)malloc(sizeof(Game_ExtTable)
	    + sizeof(Game_ExtItem) * count);

	data_readData(game_extHandle, (char *)&game_extTable->itemsCount, 2);
	game_extTable->itemsCount = FROM_LE_16(game_extTable->itemsCount);
	data_readData(game_extHandle, (char *)&game_extTable->unknown, 1);

	for (i = 0; i < count; i++) {
		data_readData(game_extHandle, (char *)&game_extTable->items[i].offset, 4);
		game_extTable->items[i].offset = FROM_LE_32(game_extTable->items[i].offset);
		data_readData(game_extHandle, (char *)&game_extTable->items[i].size, 2);
		game_extTable->items[i].size = FROM_LE_16(game_extTable->items[i].size);
		data_readData(game_extHandle, (char *)&game_extTable->items[i].width, 2);
		game_extTable->items[i].width = FROM_LE_16(game_extTable->items[i].width);
		data_readData(game_extHandle, (char *)&game_extTable->items[i].height, 2);
		game_extTable->items[i].height = FROM_LE_16(game_extTable->items[i].height);
	}
}

void game_loadImFile(void) {
	char path[20];
	int16 handle;

	if (game_totFileData[0x3d] != 0 && game_totFileData[0x3b] == 0)
		return;

	strcpy(path, "commun.im1");
	if (game_totFileData[0x3b] != 0)
		path[strlen(path) - 1] = '0' + game_totFileData[0x3b];

	handle = data_openData(path);
	if (handle < 0)
		return;

	data_closeData(handle);
	game_imFileData = data_getData(path);
}

void game_playTot(int16 skipPlay) {
	char savedTotName[20];
	int16 *oldCaptureCounter;
	int16 *oldBreakFrom;
	int16 *oldNestLevel;
	int16 captureCounter;
	int16 breakFrom;
	int16 nestLevel;
	char needTextFree;
	char needFreeResTable;
	char *curPtr;
	int32 variablesCount;
	char *filePtr;
	char *savedIP;
	int16 i;

	oldNestLevel = inter_nestLevel;
	oldBreakFrom = inter_breakFromLevel;
	oldCaptureCounter = scen_pCaptureCounter;
	savedIP = inter_execPtr;

	inter_nestLevel = &nestLevel;
	inter_breakFromLevel = &breakFrom;
	scen_pCaptureCounter = &captureCounter;
	strcpy(savedTotName, game_curTotFile);

	if (skipPlay == 0) {
		while (1) {
			for (i = 0; i < 4; i++) {
				draw_fontToSprite[i].sprite = -1;
				draw_fontToSprite[i].base = -1;
				draw_fontToSprite[i].width = -1;
				draw_fontToSprite[i].height = -1;
			}

			cd_stopPlaying();
			draw_animateCursor(4);
			inter_initControlVars();
			mult_initAll();
			mult_zeroMultData();

			for (i = 0; i < 20; i++)
				draw_spritesArray[i] = 0;

			draw_spritesArray[20] = draw_frontSurface;
			draw_spritesArray[21] = draw_backSurface;
			draw_spritesArray[23] = draw_cursorSprites;

			for (i = 0; i < 20; i++)
				game_soundSamples[i] = 0;

			game_totTextData = 0;
			game_totResourceTable = 0;
			game_imFileData = 0;
			game_extTable = 0;
			game_extHandle = -1;

			needFreeResTable = 1;
			needTextFree = 1;

			game_totToLoad[0] = 0;

			if (game_curTotFile[0] == 0 && game_totFileData == 0)
				break;

			game_loadTotFile(game_curTotFile);
			if (game_totFileData == 0) {
				draw_blitCursor();
				break;
			}

			strcpy(game_curImaFile, game_curTotFile);
			strcpy(game_curExtFile, game_curTotFile);

			game_curImaFile[strlen(game_curImaFile) - 4] = 0;
			strcat(game_curImaFile, ".ima");

			game_curExtFile[strlen(game_curExtFile) - 4] = 0;
			strcat(game_curExtFile, ".ext");

			debug(4, "IMA: %s", game_curImaFile);
			debug(4, "EXT: %s", game_curExtFile);

			filePtr = (char *)game_totFileData + 0x30;

			if (READ_LE_UINT32(filePtr) != (uint32)-1) {
				curPtr = game_totFileData;
				game_totTextData =
				    (Game_TotTextTable *) (curPtr +
				    READ_LE_UINT32((char *)game_totFileData + 0x30));

				game_totTextData->itemsCount = (int16)READ_LE_UINT16(&game_totTextData->itemsCount);

				for (i = 0; i < game_totTextData->itemsCount; ++i) {
					game_totTextData->items[i].offset = (int16)READ_LE_UINT16(&game_totTextData->items[i].offset);
					game_totTextData->items[i].size = (int16)READ_LE_UINT16(&game_totTextData->items[i].size);
				}

				needTextFree = 0;
			}

			filePtr = (char *)game_totFileData + 0x34;
			if (READ_LE_UINT32(filePtr) != (uint32)-1) {
				curPtr = game_totFileData;

				game_totResourceTable =
					(Game_TotResTable *)(curPtr +
				    READ_LE_UINT32((char *)game_totFileData + 0x34));

				game_totResourceTable->itemsCount = (int16)READ_LE_UINT16(&game_totResourceTable->itemsCount);

				for (i = 0; i < game_totResourceTable->itemsCount; ++i) {
					game_totResourceTable->items[i].offset = (int32)READ_LE_UINT32(&game_totResourceTable->items[i].offset);
					game_totResourceTable->items[i].size = (int16)READ_LE_UINT16(&game_totResourceTable->items[i].size);
					game_totResourceTable->items[i].width = (int16)READ_LE_UINT16(&game_totResourceTable->items[i].width);
					game_totResourceTable->items[i].height = (int16)READ_LE_UINT16(&game_totResourceTable->items[i].height);
				}

				needFreeResTable = 0;
			}

			game_loadImFile();
			game_loadExtTable();

			inter_animDataSize = READ_LE_UINT16((char *)game_totFileData + 0x38);
			if (inter_variables == 0) {
				variablesCount = READ_LE_UINT32((char *)game_totFileData + 0x2c);
				inter_variables = (char *)malloc(variablesCount * 4);
				for (i = 0; i < variablesCount; i++)
					WRITE_VAR(i, 0);
			}

			inter_execPtr = (char *)game_totFileData;
			inter_execPtr += READ_LE_UINT32((char *)game_totFileData + 0x64);

			inter_renewTimeInVars();

			WRITE_VAR(13, useMouse);
			WRITE_VAR(14, soundFlags);
			WRITE_VAR(15, videoMode);
			WRITE_VAR(16, language);

			inter_callSub(2);

			if (game_totToLoad[0] != 0)
				inter_terminate = 0;

			variablesCount = READ_LE_UINT32((char *)game_totFileData + 0x2c);
			draw_blitInvalidated();
			free(game_totFileData);
			game_totFileData = 0;

			if (needTextFree)
				free(game_totTextData);
			game_totTextData = 0;

			if (needFreeResTable)
				free(game_totResourceTable);
			game_totResourceTable = 0;

			free(game_imFileData);
			game_imFileData = 0;

			free(game_extTable);
			game_extTable = 0;

			if (game_extHandle >= 0)
				data_closeData(game_extHandle);

			game_extHandle = -1;

			for (i = 0; i < *scen_pCaptureCounter; i++)
				game_capturePop(0);

			mult_checkFreeMult();
			mult_freeAll();

			for (i = 0; i < 20; i++) {
				if (draw_spritesArray[i] != 0)
					vid_freeSurfDesc(draw_spritesArray[i]);
				draw_spritesArray[i] = 0;
			}
			snd_stopSound(0);

			for (i = 0; i < 20; i++)
				game_freeSoundSlot(i);

			if (game_totToLoad[0] == 0)
				break;

			strcpy(game_curTotFile, game_totToLoad);
		}
	}

	strcpy(game_curTotFile, savedTotName);

	inter_nestLevel = oldNestLevel;
	inter_breakFromLevel = oldBreakFrom;
	scen_pCaptureCounter = oldCaptureCounter;
	inter_execPtr = savedIP;
}

void game_start(void) {
	game_collisionAreas = (Game_Collision *)malloc(250 * sizeof(Game_Collision));
	game_prepareStart();
	game_playTot(0);

	free(game_collisionAreas);

	vid_freeSurfDesc(draw_cursorSprites);
	vid_freeSurfDesc(draw_cursorBack);
	vid_freeSurfDesc(draw_backSurface);
}

} // End of namespace Gob
