/* ScummVM - Scumm Interpreter
 * Copyright (C) 2004 Ivan Dubrov
 * Copyright (C) 2004-2006 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.
 *
 * $URL: https://svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-9-0/engines/gob/game.cpp $
 * $Id: game.cpp 23079 2006-06-13 08:10:54Z drmccoy $
 *
 */

#include "common/stdafx.h"
#include "common/endian.h"

#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"
#include "gob/music.h"
#include "gob/palanim.h"

namespace Gob {

int16 Game::_captureCount = 0;
Common::Rect Game::_captureStack[20];

Game::Game(GobEngine *vm) : _vm(vm) {
	_extTable = 0;
	_totFileData = 0;
	_totResourceTable = 0;
	_imFileData = 0;
	_extHandle = 0;
	_collisionAreas = 0;
	_shouldPushColls = 0;
	_totTextData = 0;

	// Collisions stack
	_collStackSize = 0;
	int i;

	for (i = 0; i < 5; i++) {
		_collStack[i] = 0;
		_collStackElemSizes[i] = 0;
	}

	for (i = 0; i < 60; i++) {
		_soundSamples[i] = 0;
		_soundIds[i] = 0;
		_soundTypes[i] = 0;
		_soundFromExt[i] = 0;
		_soundADL[i] = false;
	}

	_curTotFile[0] = 0;
	_curExtFile[0] = 0;
	_totToLoad[0] = 0;

	_startTimeKey = 0;
	_mouseButtons = 0;

	_lastCollKey = 0;
	_lastCollAreaIndex = 0;
	_lastCollId = 0;

	_activeCollResId = 0;
	_activeCollIndex = 0;
	_handleMouse = 0;
	_forceHandleMouse = 0;

	_tempStr[0] = 0;
	_curImaFile[0] = 0;
	_collStr[0] = 0;

	_backupedCount = 0;
	_curBackupPos = 0;
	
	for (i = 0; i < 5; i++) {
		_cursorXDeltaArray[i] = 0;
		_cursorYDeltaArray[i] = 0;
		_totTextDataArray[i] = 0;
		_totFileDataArray[i] = 0;
		_totResourceTableArray[i] = 0;
		_extTableArray[i] = 0;
		_extHandleArray[i] = 0;
		_imFileDataArray[i] = 0;
		_variablesArray[i] = 0;
		_curTotFileArray[i][0] = 0;
	}

	_imdFile = 0;
	_curImdFile[0] = 0;
	_imdX = 0;
	_imdY = 0;
	_imdFrameDataSize = 0;
	_imdVidBufferSize = 0;
	_imdFrameData = 0;
	_imdVidBuffer = 0;

//	warning("GOB2 Stub! _byte_2FC82, _byte_2FC83, _word_2FC80");
	_byte_2FC82 = 0;
	_byte_2FC83 = 0;
	_word_2FC80 = 0;

//	warning("GOB2 Stub! _byte_2FC9B, _word_2FC9C, _word_2FC9E, _word_2E51F, _off_2E51B, _off_2E517, _dword_2F2B6");
	_byte_2FC9B = 0;
	_word_2FC9C = 0;
	_word_2FC9E = 0;
	_word_2E51F = 0;
	_off_2E51B = 0;
	_off_2E517 = 0;
	_dword_2F2B6 = 0;
}

Game::~Game() {
	if (_imdFile) {
		if (_imdFile->palette)
			delete[] _imdFile->palette;
		if (_imdFile->surfDesc &&
				(_imdFile->surfDesc != _vm->_draw->_spritesArray[20]) &&
				(_imdFile->surfDesc != _vm->_draw->_spritesArray[21]))
			_vm->_video->freeSurfDesc(_imdFile->surfDesc);
		if (_imdFile->framesPos)
			delete[] _imdFile->framesPos;
		if (_imdFile->frameCoords)
			delete[] _imdFile->frameCoords;
		delete _imdFile;
	}
	if (_imdFrameData)
		delete[] _imdFrameData;
	if (_imdVidBuffer)
		delete[] _imdVidBuffer;
	if (_word_2FC80)
		delete[] _word_2FC80;
}

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

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

	commonHandle = -1;
	itemsCount = _extTable->itemsCount;
	item = &_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;
		debugC(7, DEBUG_FILEIO, "loadExtData(%d, %d, %d)", itemId, *pResWidth, *pResHeight);
	}

	debugC(7, DEBUG_FILEIO, "loadExtData(%d, 0, 0)", itemId);

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

	debugC(7, DEBUG_FILEIO, "size: %d off: %d", size, offset);
	if (offset >= 0) {
		handle = _extHandle;
	} else {
		offset = -(offset + 1);
		tableSize = 0;
		_vm->_dataio->closeData(_extHandle);
		strcpy(path, "commun.ex1");
		path[strlen(path) - 1] = *(_totFileData + 0x3c) + '0';
		commonHandle = _vm->_dataio->openData(path);
		handle = commonHandle;
	}

	debugC(7, DEBUG_FILEIO, "off: %d size: %d", offset, tableSize);
	_vm->_dataio->seekData(handle, offset + tableSize, SEEK_SET);
	// CHECKME: is the below correct?
	if (isPacked)
		dataBuf = new char[size];
	else
		dataBuf = new char[size];

	dataPtr = dataBuf;
	while (size > 32000) {
		// BUG: huge->far conversion. Need normalization?
		_vm->_dataio->readData(handle, (char *)dataPtr, 32000);
		size -= 32000;
		dataPtr += 32000;
	}
	_vm->_dataio->readData(handle, (char *)dataPtr, size);
	if (commonHandle != -1) {
		_vm->_dataio->closeData(commonHandle);
		_extHandle = _vm->_dataio->openData(_curExtFile);
	}

	if (isPacked != 0) {
		packedBuf = dataBuf;
		dataBuf = new char[READ_LE_UINT32(packedBuf)];
		_vm->_pack->unpackData(packedBuf, dataBuf);
		delete[] packedBuf;
	}

	return dataBuf;

}

void Game::freeCollision(int16 id) {
	int16 i;

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

int16 Game::checkMousePoint(int16 all, int16 *resId, int16 *resIndex) {
	Collision *ptr;
	int16 i;

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

	*resIndex = 0;

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

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

			if (_vm->_global->_inter_mouseX < ptr->left
			    || _vm->_global->_inter_mouseX > ptr->right
			    || _vm->_global->_inter_mouseY < ptr->top
			    || _vm->_global->_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 != _mouseButtons - 1
			    && (ptr->flags & 0xf0) >> 4 != 2)
				continue;

			if (_vm->_global->_inter_mouseX < ptr->left
			    || _vm->_global->_inter_mouseX > ptr->right
			    || _vm->_global->_inter_mouseY < ptr->top
			    || _vm->_global->_inter_mouseY > ptr->bottom)
				continue;

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

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

	return 0;
}

void Game::capturePush(int16 left, int16 top, int16 width, int16 height) {
	int16 right;

	if (_captureCount == 20)
		error("capturePush: Capture stack overflow!");

	_captureStack[_captureCount].left = left;
	_captureStack[_captureCount].top = top;
	_captureStack[_captureCount].right = left + width;
	_captureStack[_captureCount].bottom = top + height;

	_vm->_draw->_spriteTop = top;
	_vm->_draw->_spriteBottom = height;

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

	_vm->_draw->_spritesArray[30 + _captureCount] =
	    _vm->_video->initSurfDesc(_vm->_global->_videoMode, right - left + 1, height, 0);

	_vm->_draw->_sourceSurface = 21;
	_vm->_draw->_destSurface = 30 + _captureCount;

	_vm->_draw->_spriteLeft = left;
	_vm->_draw->_spriteRight = right - left + 1;
	_vm->_draw->_destSpriteX = 0;
	_vm->_draw->_destSpriteY = 0;
	_vm->_draw->_transparency = 0;
	_vm->_draw->spriteOperation(0);
	_captureCount++;
}

void Game::capturePop(char doDraw) {
	if (_captureCount <= 0)
		return;

	_captureCount--;
	if (doDraw) {
		_vm->_draw->_destSpriteX = _captureStack[_captureCount].left;
		_vm->_draw->_destSpriteY = _captureStack[_captureCount].top;
		_vm->_draw->_spriteRight =
		    _captureStack[_captureCount].width();
		_vm->_draw->_spriteBottom =
		    _captureStack[_captureCount].height();

		_vm->_draw->_transparency = 0;
		_vm->_draw->_sourceSurface = 30 + _captureCount;
		_vm->_draw->_destSurface = 21;
		_vm->_draw->_spriteLeft = _vm->_draw->_destSpriteX & 0xf;
		_vm->_draw->_spriteTop = 0;
		_vm->_draw->spriteOperation(0);
	}
	_vm->_video->freeSurfDesc(_vm->_draw->_spritesArray[30 + _captureCount]);
}

char *Game::loadTotResource(int16 id) {
	TotResItem *itemPtr;
	int32 offset;

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

void Game::loadSound(int16 slot, char *dataPtr) {
	Snd::SoundDesc *soundDesc;

	soundDesc = new Snd::SoundDesc;

	_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::freeSoundSlot(int16 slot) {
	if (slot == -1)
		slot = _vm->_parse->parseValExpr();

	if ((slot < 0) || (slot >= 60) || (_soundSamples[slot] == 0))
		return;

	if (_soundADL[slot]) {
		_vm->_music->stopPlay();
		if (_soundFromExt[slot] == 1) {
			delete[] ((char *) _soundSamples[slot]);
			_soundFromExt[slot] = 0;
		}
	} else {
		char* data = _soundSamples[slot]->data;

		_vm->_snd->freeSoundDesc(_soundSamples[slot], false);
		_soundSamples[slot] = 0;

		if (_soundFromExt[slot] == 1) {
			delete[] (data - 6);
			_soundFromExt[slot] = 0;
		}
	}
}

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;
	Video::FontDesc *pFont;
	char curSym;
	int16 key;
	const char *str1;
	const char *str2;
	int16 i;
	uint32 pos;
	int16 flag;
	int16 savedKey;

	if (_handleMouse != 0 &&
	    (_vm->_global->_useMouse != 0 || _forceHandleMouse != 0))
		handleMouse = 1;
	else
		handleMouse = 0;

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

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

		_vm->_draw->_destSpriteX = xPos;
		_vm->_draw->_destSpriteY = yPos;
		_vm->_draw->_spriteRight = editSize * pFont->itemWidth;
		_vm->_draw->_spriteBottom = height;

		_vm->_draw->_destSurface = 21;
		_vm->_draw->_backColor = backColor;
		_vm->_draw->_frontColor = frontColor;
		_vm->_draw->_textToPrint = _tempStr;
		_vm->_draw->_transparency = 1;
		_vm->_draw->_fontIndex = fontIndex;
		_vm->_draw->spriteOperation(DRAW_FILLRECT);

		_vm->_draw->_destSpriteY = yPos + (height - 8) / 2;

		_vm->_draw->spriteOperation(DRAW_PRINTTEXT);
		if (pos == editSize)
			pos--;

		curSym = _tempStr[pos];

		flag = 1;

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

			_vm->_draw->_destSpriteX = xPos + pFont->itemWidth * pos;
			_vm->_draw->_destSpriteY = yPos + height - 1;
			_vm->_draw->_spriteRight = pFont->itemWidth;
			_vm->_draw->_spriteBottom = 1;
			_vm->_draw->_destSurface = 21;
			_vm->_draw->_backColor = frontColor;
			_vm->_draw->spriteOperation(DRAW_FILLRECT);

			if (flag != 0) {
				key = checkCollisions(handleMouse, -1,
				    &_activeCollResId,
				    &_activeCollIndex);
			}
			flag = 0;

			key = checkCollisions(handleMouse, -300,
			    &_activeCollResId, &_activeCollIndex);

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

			_tempStr[0] = curSym;
			_tempStr[1] = 0;
			_vm->_draw->_destSpriteX = xPos + pFont->itemWidth * pos;
			_vm->_draw->_destSpriteY = yPos + height - 1;
			_vm->_draw->_spriteRight = pFont->itemWidth;
			_vm->_draw->_spriteBottom = 1;
			_vm->_draw->_destSurface = 21;
			_vm->_draw->_backColor = backColor;
			_vm->_draw->_frontColor = frontColor;
			_vm->_draw->_textToPrint = _tempStr;
			_vm->_draw->_transparency = 1;
			_vm->_draw->spriteOperation(DRAW_FILLRECT);

			_vm->_draw->_destSpriteY = yPos + (height - 8) / 2;
			_vm->_draw->spriteOperation(DRAW_PRINTTEXT);

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

			key = checkCollisions(handleMouse, -300,
			    &_activeCollResId, &_activeCollIndex);

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

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

			if (_vm->_inter->_terminate)
				return 0;
		}

		if (key == 0 || _activeCollResId != 0 || _vm->_inter->_terminate)
			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) {
				_vm->_util->cutFromStr(str, pos - 1, 1);
				pos--;
				continue;
			}

		case 0x5300:	// Del

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

			_vm->_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 (_vm->_global->_useMouse != 0)
				continue;

			_forceHandleMouse = !_forceHandleMouse;

			if (_handleMouse != 0 &&
			    (_vm->_global->_useMouse != 0 || _forceHandleMouse != 0))
				handleMouse = 1;
			else
				handleMouse = 0;

			if (_vm->_global->_pressedKeys[1] == 0)
				continue;

			while (_vm->_global->_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))
					_vm->_util->cutFromStr(str, strlen(str) - 1,
					    1);

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

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

				_vm->_util->insertStr(_tempStr, str, pos - 1);

				//strupr(str);
			}
		}
	}
}

int16 Game::multiEdit(int16 time, int16 index, int16 *pCurPos, InputDesc * inpDesc) {
	Collision *collArea;
	int16 descInd;
	int16 key;
	int16 found = -1;
	int16 i;

	descInd = 0;
	for (i = 0; i < 250; i++) {
		collArea = &_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(_tempStr, _vm->_global->_inter_variables + collArea->key);

		_vm->_draw->_destSpriteX = collArea->left;
		_vm->_draw->_destSpriteY = collArea->top;
		_vm->_draw->_spriteRight = collArea->right - collArea->left + 1;
		_vm->_draw->_spriteBottom = collArea->bottom - collArea->top + 1;

		_vm->_draw->_destSurface = 21;

		_vm->_draw->_backColor = inpDesc[descInd].backColor;
		_vm->_draw->_frontColor = inpDesc[descInd].frontColor;
		_vm->_draw->_textToPrint = _tempStr;
		_vm->_draw->_transparency = 1;
		_vm->_draw->_fontIndex = inpDesc[descInd].fontIndex;
		_vm->_draw->spriteOperation(DRAW_FILLRECT);
		_vm->_draw->_destSpriteY +=
		    ((collArea->bottom - collArea->top + 1) - 8) / 2;

		_vm->_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 = &_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 = &_collisionAreas[found];

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

		if (_vm->_inter->_terminate)
			return 0;

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

			if ((_collisionAreas[_activeCollIndex].
				flags & 0x0f) < 3)
				return 0;

			if ((_collisionAreas[_activeCollIndex].
				flags & 0x0f) > 10)
				return 0;

			*pCurPos = 0;
			for (i = 0; i < 250; i++) {
				collArea = &_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 == _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) {
	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 savedCollStackSize;
	int16 i;
	int16 counter;
	int16 var_24;
	int16 var_26;
	int16 _collStackPos;
	Collision *collPtr;
	int16 timeKey;
	char *savedIP;

	if (_shouldPushColls)
		pushCollisions(1);

	collResId = -1;
	_vm->_global->_inter_execPtr++;
	count = *_vm->_global->_inter_execPtr++;
	_handleMouse = _vm->_global->_inter_execPtr[0];
	deltaTime = 1000 * (byte)_vm->_global->_inter_execPtr[1];
	descIndex2 = (byte)_vm->_global->_inter_execPtr[2];
	stackPos2 = (byte)_vm->_global->_inter_execPtr[3];
	descIndex = (byte)_vm->_global->_inter_execPtr[4];

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

	timeVal = deltaTime;
	_vm->_global->_inter_execPtr += 6;

	startIP = _vm->_global->_inter_execPtr;
	WRITE_VAR(16, 0);
	var_22 = 0;
	index = 0;
	curEditIndex = 0;

	for (curCmd = 0; curCmd < count; curCmd++) {
		array[curCmd] = 0;
		cmd = *_vm->_global->_inter_execPtr++;

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

		if ((cmd & 0x80) != 0) {
			left = _vm->_parse->parseValExpr();
			top = _vm->_parse->parseValExpr();
			width = _vm->_parse->parseValExpr();
			height = _vm->_parse->parseValExpr();
		} else {
			left = _vm->_inter->load16();
			top = _vm->_inter->load16();
			width = _vm->_inter->load16();
			height = _vm->_inter->load16();
		}
		cmd &= 0x7f;

		debugC(1, DEBUG_COLLISIONS, "collisionsBlock(%d)", cmd);

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

			_vm->_util->waitKey();
			var_22 = 1;
			key = _vm->_parse->parseVarIndex();
			descArray[index].fontIndex = _vm->_inter->load16();
			descArray[index].backColor = *_vm->_global->_inter_execPtr++;
			descArray[index].frontColor = *_vm->_global->_inter_execPtr++;

			if (cmd < 5 || cmd > 8) {
				descArray[index].ptr = 0;
			} else {
				descArray[index].ptr = _vm->_global->_inter_execPtr + 2;
				_vm->_global->_inter_execPtr += _vm->_inter->load16();
			}

			if (left == -1)
				break;

			if ((cmd & 1) == 0) {
				addNewCollision(curCmd + 0x8000, left,
				    top,
				    left +
				    width *
				    _vm->_draw->_fonts[descArray[index].fontIndex]->
				    itemWidth - 1, top + height - 1, cmd, key,
				    0,
				    _vm->_global->_inter_execPtr - (char *)_totFileData);

				_vm->_global->_inter_execPtr += 2;
				_vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr);
			} else {
				addNewCollision(curCmd + 0x8000, left,
				    top,
				    left +
				    width *
				    _vm->_draw->_fonts[descArray[index].fontIndex]->
				    itemWidth - 1, top + height - 1, cmd, key,
				    0, 0);
			}
			index++;
			break;

		case 21:
			key = _vm->_inter->load16();
			array[curCmd] = _vm->_inter->load16();
			flags = _vm->_inter->load16() & 3;

			addNewCollision(curCmd + 0x8000, left, top,
			    left + width - 1,
			    top + height - 1,
			    (flags << 4) + cmdHigh + 2, key,
			    _vm->_global->_inter_execPtr - (char *)_totFileData, 0);

			_vm->_global->_inter_execPtr += 2;
			_vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr);
			break;

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

		case 2:
			key = _vm->_inter->load16();
			array[curCmd] = _vm->_inter->load16();
			flags = _vm->_inter->load16() & 3;

			addNewCollision(curCmd + 0x8000, left, top,
			    left + width - 1,
			    top + height - 1,
			    (flags << 4) + cmdHigh + 2, key, 0,
			    _vm->_global->_inter_execPtr - (char *)_totFileData);

			_vm->_global->_inter_execPtr += 2;
			_vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr);
			break;

		case 0:
			_vm->_global->_inter_execPtr += 6;
			startIP = _vm->_global->_inter_execPtr;
			_vm->_global->_inter_execPtr += 2;
			_vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr);
			key = curCmd + 0xA000;

			addNewCollision(curCmd + 0x8000, left, top,
			    left + width - 1,
			    top + height - 1,
			    cmd + cmdHigh, key,
			    startIP - (char *)_totFileData,
			    _vm->_global->_inter_execPtr - (char *)_totFileData);

			_vm->_global->_inter_execPtr += 2;
			_vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr);
			break;

		case 1:
			key = _vm->_inter->load16();
			array[curCmd] = _vm->_inter->load16();
			flags = _vm->_inter->load16() & 3;

			startIP = _vm->_global->_inter_execPtr;
			_vm->_global->_inter_execPtr += 2;
			_vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr);
			if (key == 0)
				key = curCmd + 0xa000;

			addNewCollision(curCmd + 0x8000, left, top,
			    left + width - 1,
			    top + height - 1,
			    (flags << 4) + cmd + cmdHigh, key,
			    startIP - (char *)_totFileData,
			    _vm->_global->_inter_execPtr - (char *)_totFileData);

			_vm->_global->_inter_execPtr += 2;
			_vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr);
			break;
		}
	}

	_forceHandleMouse = 0;
	_vm->_util->waitKey();

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

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

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

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

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

					collResId = _collisionAreas[i].id;
					_activeCollResId = collResId;
					collResId &= 0x7fff;
					_activeCollIndex = i;
					break;
				}
				break;
			}
		} else {
			key =
			    checkCollisions(_handleMouse, -deltaTime,
			    &_activeCollResId, &_activeCollIndex);
		}

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

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

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

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

						_activeCollResId =
						    _collisionAreas[i].id;
						_activeCollIndex = i;
						break;
					}
				}

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

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

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

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

						if (adjustKey(key & 0xff) == adjustKey(_collisionAreas[i].key) || _collisionAreas[i].key == 0x7fff) {
							_activeCollResId = _collisionAreas[i].id;
							_activeCollIndex = i;
							break;
						}
					}
				}
			} else {

				if (deltaTime != 0 && VAR(16) == 0) {
					if (stackPos2 != 0) {
						_collStackPos = 0;
						collPtr = _collisionAreas;

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

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

							_activeCollResId = collPtr->id;
							_activeCollIndex = i;
							WRITE_VAR(2, _vm->_global->_inter_mouseX);
							WRITE_VAR(3, _vm->_global->_inter_mouseY);
							WRITE_VAR(4, _mouseButtons);
							WRITE_VAR(16, array[(uint16)_activeCollResId & ~0x8000]);

							if (collPtr->funcLeave != 0) {
								timeKey = _vm->_util->getTimeKey();
								savedIP = _vm->_global->_inter_execPtr;
								_vm->_global->_inter_execPtr = (char *)_totFileData + collPtr->funcLeave;
								_shouldPushColls = 1;
								savedCollStackSize = _collStackSize;
								_vm->_inter->funcBlock(0);

								if (savedCollStackSize != _collStackSize)
									popCollisions();

								_shouldPushColls = 0;
								_vm->_global->_inter_execPtr = savedIP;
								deltaTime = timeVal - (_vm->_util->getTimeKey() - timeKey);

								if (deltaTime < 2)
									deltaTime = 2;
							}

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

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

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

								_activeCollResId = _collisionAreas[i].id;
								_activeCollIndex = i;
								break;
							}
						} else {
							for (i = 0; i < 250; i++) {
								if (_collisionAreas[i].left == -1)
									continue;

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

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

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

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

							_activeCollResId = _collisionAreas[i].id;
							_activeCollIndex = i;
							break;
						}
					}
				}
			}
		}

		if (_activeCollResId == 0)
			continue;

		if (_collisionAreas[_activeCollIndex].funcLeave != 0)
			continue;

		WRITE_VAR(2, _vm->_global->_inter_mouseX);
		WRITE_VAR(3, _vm->_global->_inter_mouseY);
		WRITE_VAR(4, _mouseButtons);
		WRITE_VAR(16, array[(uint16)_activeCollResId & ~0x8000]);

		if (_collisionAreas[_activeCollIndex].funcEnter != 0) {
			savedIP = _vm->_global->_inter_execPtr;
			_vm->_global->_inter_execPtr = (char *)_totFileData +
			    _collisionAreas[_activeCollIndex].
			    funcEnter;

			_shouldPushColls = 1;

			_collStackPos = _collStackSize;
			_vm->_inter->funcBlock(0);
			if (_collStackPos != _collStackSize)
				popCollisions();
			_shouldPushColls = 0;
			_vm->_global->_inter_execPtr = savedIP;
		}

		WRITE_VAR(16, 0);
		_activeCollResId = 0;
	}
	while (_activeCollResId == 0 && !_vm->_inter->_terminate && !_vm->_quitRequested);

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

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

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

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

			if ((_collisionAreas[i].flags & 0x0f) > 8) {
				char *ptr;
				strcpy(_tempStr,
				    _vm->_global->_inter_variables + _collisionAreas[i].key);
				while ((ptr = strchr(_tempStr, ' ')) != 0) {
					_vm->_util->cutFromStr(_tempStr, (ptr - _tempStr), 1);
					ptr = strchr(_tempStr, ' ');
				}
				strcpy(_vm->_global->_inter_variables + _collisionAreas[i].key, _tempStr);
			}

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

				strcpy(_tempStr, _vm->_global->_inter_variables + _collisionAreas[i].key);

				if ((_collisionAreas[i].flags & 0x0f) < 7)
					_vm->_util->prepareStr(_tempStr);

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

					str += strlen(str) + 1;

					if ((_collisionAreas[i].flags & 0x0f) < 7)
						_vm->_util->prepareStr(_collStr);

					if (strcmp(_tempStr, _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 (!_vm->_inter->_terminate) {
		savedIP = (char *)_totFileData +
		    _collisionAreas[_activeCollIndex].funcLeave;

		WRITE_VAR(2, _vm->_global->_inter_mouseX);
		WRITE_VAR(3, _vm->_global->_inter_mouseY);
		WRITE_VAR(4, _mouseButtons);

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

	for (curCmd = 0; curCmd < count; curCmd++) {
		freeCollision(curCmd + 0x8000);
	}
	_vm->_global->_inter_execPtr = savedIP;
}

void Game::loadTotFile(char *path) {
	int16 handle;

	handle = _vm->_dataio->openData(path);
	if (handle >= 0) {
		_vm->_dataio->closeData(handle);
		_totFileData = _vm->_dataio->getData(path);
	} else {
		_totFileData = 0;
	}
}

void Game::loadExtTable(void) {
	int16 count, i;

	// Function is correct. [sev]

	_extHandle = _vm->_dataio->openData(_curExtFile);
	if (_extHandle < 0)
		return;

	_vm->_dataio->readData(_extHandle, (char *)&count, 2);
	count = FROM_LE_16(count);

	_vm->_dataio->seekData(_extHandle, 0, 0);
	_extTable = new ExtTable;
	_extTable->items = 0;
	if (count)
		_extTable->items = new ExtItem[count];

	_vm->_dataio->readData(_extHandle, (char *)&_extTable->itemsCount, 2);
	_extTable->itemsCount = FROM_LE_16(_extTable->itemsCount);
	_vm->_dataio->readData(_extHandle, (char *)&_extTable->unknown, 1);

	for (i = 0; i < count; i++) {
		_vm->_dataio->readData(_extHandle, (char *)&_extTable->items[i].offset, 4);
		_extTable->items[i].offset = FROM_LE_32(_extTable->items[i].offset);
		_vm->_dataio->readData(_extHandle, (char *)&_extTable->items[i].size, 2);
		_extTable->items[i].size = FROM_LE_16(_extTable->items[i].size);
		_vm->_dataio->readData(_extHandle, (char *)&_extTable->items[i].width, 2);
		_extTable->items[i].width = FROM_LE_16(_extTable->items[i].width);
		_vm->_dataio->readData(_extHandle, (char *)&_extTable->items[i].height, 2);
		_extTable->items[i].height = FROM_LE_16(_extTable->items[i].height);
	}
}

void Game::loadImFile(void) {
	char path[20];
	int16 handle;

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

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

	handle = _vm->_dataio->openData(path);
	if (handle < 0)
		return;

	_vm->_dataio->closeData(handle);
	_imFileData = _vm->_dataio->getData(path);
}

void Game::start(void) {
	_collisionAreas = new Collision[250];
	prepareStart();
	playTot(0);

	delete[] _collisionAreas;

	_vm->_video->freeSurfDesc(_vm->_draw->_cursorSprites);
	_vm->_video->freeSurfDesc(_vm->_draw->_cursorBack);
	_vm->_video->freeSurfDesc(_vm->_draw->_backSurface);
}

// flagbits: 0 = freeInterVariables, 1 = skipPlay
void Game::totSub(int8 flags, char *newTotFile) {
	int8 curBackupPos;

	if (_backupedCount >= 5)
		return;

	_cursorXDeltaArray[_backupedCount] = _vm->_draw->_cursorXDeltaVar;
	_cursorYDeltaArray[_backupedCount] = _vm->_draw->_cursorYDeltaVar;
	_totTextDataArray[_backupedCount] = _totTextData;
	_totFileDataArray[_backupedCount] = _totFileData;
	_totResourceTableArray[_backupedCount] = _totResourceTable;
	_extTableArray[_backupedCount] = _extTable;
	_extHandleArray[_backupedCount] = _extHandle;
	_imFileDataArray[_backupedCount] = _imFileData;
	_variablesArray[_backupedCount] = _vm->_global->_inter_variables;
	strcpy(_curTotFileArray[_backupedCount], _curTotFile);

	curBackupPos = _curBackupPos;
	_backupedCount++;
	_curBackupPos = _backupedCount;

	_totTextData = 0;
	_totFileData = 0;
	_totResourceTable = 0;
	if (flags & 1)
		_vm->_global->_inter_variables = 0;

	strcpy(_curTotFile, newTotFile);
	strcat(_curTotFile, ".TOT");

	if (_vm->_inter->_terminate != 0)
		return;

	pushCollisions(0);

	if (flags & 2)
		playTot(-1);
	else
		playTot(0);

	if (_vm->_inter->_terminate != 2)
		_vm->_inter->_terminate = 0;

	popCollisions();

	if ((flags & 1) && (_vm->_global->_inter_variables != 0))
		delete[] _vm->_global->_inter_variables;

	_backupedCount--;
	_curBackupPos = curBackupPos;

	_vm->_draw->_cursorXDeltaVar = _cursorXDeltaArray[_backupedCount];
	_vm->_draw->_cursorYDeltaVar = _cursorYDeltaArray[_backupedCount];
	_totTextData = _totTextDataArray[_backupedCount];
	_totFileData = _totFileDataArray[_backupedCount];
	_totResourceTable = _totResourceTableArray[_backupedCount];
	_extTable = _extTableArray[_backupedCount];
	_extHandle = _extHandleArray[_backupedCount];
	_imFileData = _imFileDataArray[_backupedCount];
	_vm->_global->_inter_variables = _variablesArray[_backupedCount];
	strcpy(_curTotFile, _curTotFileArray[_backupedCount]);
	strcpy(_curExtFile, _curTotFile);
	_curExtFile[strlen(_curExtFile)-4] = '\0';
	strcat(_curExtFile, ".EXT");
}

void Game::switchTotSub(int16 index, int16 skipPlay) {
	int16 backupedCount;
	int16 curBackupPos;

	if ((_backupedCount - index) < 1)
		return;

	curBackupPos = _curBackupPos;
	backupedCount = _backupedCount;
	if (_curBackupPos == _backupedCount) {
		_cursorXDeltaArray[_backupedCount] = _vm->_draw->_cursorXDeltaVar;
		_cursorYDeltaArray[_backupedCount] = _vm->_draw->_cursorYDeltaVar;
		_totTextDataArray[_backupedCount] = _totTextData;
		_totFileDataArray[_backupedCount] = _totFileData;
		_totResourceTableArray[_backupedCount] = _totResourceTable;
		_extTableArray[_backupedCount] = _extTable;
		_extHandleArray[_backupedCount] = _extHandle;
		_imFileDataArray[_backupedCount] = _imFileData;
		_variablesArray[_backupedCount] = _vm->_global->_inter_variables;
		strcpy(_curTotFileArray[_backupedCount], _curTotFile);
		_backupedCount++;
	}
	_curBackupPos -= index;
	if (index >= 0)
		_curBackupPos--;

	_vm->_draw->_cursorXDeltaVar = _cursorXDeltaArray[_curBackupPos];
	_vm->_draw->_cursorYDeltaVar = _cursorYDeltaArray[_curBackupPos];
	_totTextData = _totTextDataArray[_curBackupPos];
	_totFileData = _totFileDataArray[_curBackupPos];
	_totResourceTable = _totResourceTableArray[_curBackupPos];
	_extTable = _extTableArray[_curBackupPos];
	_extHandle = _extHandleArray[_curBackupPos];
	_imFileData = _imFileDataArray[_curBackupPos];
	_vm->_global->_inter_variables = _variablesArray[_curBackupPos];
	strcpy(_curTotFile, _curTotFileArray[_curBackupPos]);
	strcpy(_curExtFile, _curTotFile);
	_curExtFile[strlen(_curExtFile)-4] = '\0';
	strcat(_curExtFile, ".EXT");

	if (_vm->_inter->_terminate != 0)
		return;

	_vm->_game->pushCollisions(0);
	_vm->_game->playTot(skipPlay);

	if (_vm->_inter->_terminate != 2)
		_vm->_inter->_terminate = 0;

	_vm->_game->popCollisions();

	_curBackupPos = curBackupPos;
	_backupedCount = backupedCount;
	_vm->_draw->_cursorXDeltaVar = _cursorXDeltaArray[_curBackupPos];
	_vm->_draw->_cursorYDeltaVar = _cursorYDeltaArray[_curBackupPos];
	_totTextData = _totTextDataArray[_curBackupPos];
	_totFileData = _totFileDataArray[_curBackupPos];
	_totResourceTable = _totResourceTableArray[_curBackupPos];
	_extTable = _extTableArray[_curBackupPos];
	_extHandle = _extHandleArray[_curBackupPos];
	_imFileData = _imFileDataArray[_curBackupPos];
	_vm->_global->_inter_variables = _variablesArray[_curBackupPos];
	strcpy(_curTotFile, _curTotFileArray[_curBackupPos]);
	strcpy(_curExtFile, _curTotFile);
	_curExtFile[strlen(_curExtFile)-4] = '\0';
	strcat(_curExtFile, ".EXT");
}

int16 Game::openLocTextFile(char *locTextFile, int language) {
	int n;

	n = strlen(locTextFile);
	if (n < 4)
		return -1;

	locTextFile[n - 4] = 0;
	switch (language) {
	case 0:
		strcat(locTextFile, ".dat");
		break;
	case 1:
		strcat(locTextFile, ".all");
		break;
	case 3:
		strcat(locTextFile, ".esp");
		break;
	case 4:
		strcat(locTextFile, ".ita");
		break;
	case 5:
		strcat(locTextFile, ".usa");
		break;
	case 6:
		strcat(locTextFile, ".ndl");
		break;
	case 7:
		strcat(locTextFile, ".kor");
		break;
	case 8:
		strcat(locTextFile, ".isr");
		break;
	default:
		strcat(locTextFile, ".ang");
		break;
	}
	return _vm->_dataio->openData(locTextFile);
}

char *Game::loadLocTexts(void) {
	char locTextFile[20];
	int16 handle;
	int i;

	strcpy(locTextFile, _curTotFile);

	handle = openLocTextFile(locTextFile, _vm->_global->_language);
	if ((handle < 0) && !scumm_stricmp(_vm->_game->_curTotFile, "intro0.tot")) {
		warning("Your game version doesn't support the requested language, using the first one available");
		for (i = 0; i < 10; i++) {
			handle = openLocTextFile(locTextFile, i);
			if (handle >= 0)
				_vm->_global->_language = i;
		}
	}

	if (handle >= 0) {
		_vm->_dataio->closeData(handle);
		return _vm->_dataio->getData(locTextFile);
	}
	return 0;
}

void Game::setCollisions(void) {
	char *savedIP;
	int16 left;
	int16 top;
	int16 width;
	int16 height;
	Collision *collArea;

	for (collArea = _collisionAreas; collArea->left != -1; collArea++) {
		if (((collArea->id & 0xC000) != 0x8000) || (collArea->field_12 == 0))
			continue;

		savedIP = _vm->_global->_inter_execPtr;
		_vm->_global->_inter_execPtr = _totFileData + collArea->field_12;
		left = _vm->_parse->parseValExpr();
		top = _vm->_parse->parseValExpr();
		width = _vm->_parse->parseValExpr();
		height = _vm->_parse->parseValExpr();
		if ((_vm->_draw->_renderFlags != 8) && (left != -1)) {
			left += _vm->_draw->_backDeltaX;
			top += _vm->_draw->_backDeltaY;
		}
		if (_vm->_draw->_word_2E8E2 != 2) {
			_vm->_draw->adjustCoords(0, &left, &top);
			if ((collArea->flags & 0x0F) < 3)
				_vm->_draw->adjustCoords(2, &width, &height);
			else {
				height &= 0xFFFE;
				_vm->_draw->adjustCoords(2, 0, &height);
			}
		}
		collArea->left = left;
		collArea->top = top;
		collArea->right = left + width - 1;
		collArea->bottom = top + height - 1;
		_vm->_global->_inter_execPtr = savedIP;
	}
}

void Game::collSub(int16 offset) {
	char *savedIP;
	int16 collStackSize;

	savedIP = _vm->_global->_inter_execPtr;
	_vm->_global->_inter_execPtr = _totFileData + offset;

	_shouldPushColls = 1;
	collStackSize = _collStackSize;

	_vm->_inter->funcBlock(0);

	if (collStackSize != _collStackSize)
		popCollisions();

	_shouldPushColls = 0;
	_vm->_global->_inter_execPtr = savedIP;
	setCollisions();
}

void Game::collAreaSub(int16 index, int8 enter) {
	uint16 collId;

	collId = _collisionAreas[index].id & 0xF000;

	if ((collId == 0xA000) || (collId == 0x9000))
		WRITE_VAR(17, collId);
	else if (enter == 0)
		WRITE_VAR(17, -(collId & 0x0FFF));
	else
		WRITE_VAR(17, collId & 0x0FFF);
	
	if (enter != 0) {
		if (_collisionAreas[index].funcEnter != 0)
			collSub(_collisionAreas[index].funcEnter);
	} else {
		if (_collisionAreas[index].funcLeave != 0)
			collSub(_collisionAreas[index].funcLeave);
	}
}

// "DEVinitscreen"
void Game::sub_ADD2(void) {
	_word_2FC9C = 0;
	_word_2FC9E = 0;

	if (_word_2E51F != 0) {
		_off_2E51B = new Video::SurfaceDesc;
		memcpy(_off_2E51B, _vm->_draw->_frontSurface, sizeof(Video::SurfaceDesc));
		_off_2E51B->height = _vm->_global->_primaryHeight - _word_2E51F;
		_vm->_draw->_frontSurface->height -= _off_2E51B->height;
		_vm->_draw->_frontSurface->vidPtr =
			_off_2E51B->vidPtr + ((_off_2E51B->width * _off_2E51B->height) / 4);

		_off_2E517 = new Video::SurfaceDesc;
		memcpy(_off_2E517, _off_2E51B, sizeof(Video::SurfaceDesc));
		_off_2E517->width = _vm->_global->_primaryWidth;
		_off_2E517->vidPtr = _vm->_draw->_frontSurface->vidPtr +
			((_vm->_draw->_frontSurface->width * _vm->_draw->_frontSurface->height ) / 4);
	}
	_vm->_draw->initBigSprite(21, 320, 200, 0);
	_vm->_draw->_backSurface = _vm->_draw->_spritesArray[21];
	_vm->_video->clearSurf(_vm->_draw->_backSurface);
	
	_vm->_draw->initBigSprite(23, 32, 16, 2);
	// TODO: That assignment is not in the original assembly, why?
	_vm->_draw->_cursorSpritesBack = _vm->_draw->_spritesArray[23];
	_vm->_draw->_cursorSprites = _vm->_draw->_cursorSpritesBack;
	_vm->_draw->_cursorBack = _vm->_video->initSurfDesc(_vm->_global->_videoMode, 16, 16, 0);

	_vm->_draw->_spritesArray[20] = _vm->_draw->_frontSurface;
	_vm->_draw->_spritesArray[21] = _vm->_draw->_backSurface;

/*	if (_word_2E51F != 0) {
		dword_2F92D = _off_2E51B;
		dword_2F931 = _off_2E517;
	}*/
}

// "DEVclosescreen"
void Game::sub_BB28(void) {
	_vm->_draw->freeSprite(23);
	_vm->_video->freeSurfDesc(_vm->_draw->_cursorBack);
	if (_off_2E51B != 0) {
		memcpy(_vm->_draw->_frontSurface, _off_2E51B, sizeof(Video::SurfaceDesc));
		_vm->_draw->_frontSurface->width = 320;
		_vm->_draw->_frontSurface->height = 200;
		delete _off_2E51B;
		delete _off_2E517;
		_off_2E51B = 0;
		_off_2E517 = 0;
	}
	if (_vm->_draw->_frontSurface != _vm->_draw->_backSurface)
		_vm->_draw->freeSprite(21);
}

Snd::SoundDesc *Game::loadSND(const char *path, int8 arg_4) {
	Snd::SoundDesc *soundDesc;
	int32 dsize;
	char *data;
	char *dataPtr;

	soundDesc = new Snd::SoundDesc;

	data = _vm->_dataio->getData(path);
	if (data == 0) {
		delete soundDesc;
		return 0;
	}
	soundDesc->data = data;
	soundDesc->flag = *data & 0x7F;
	if (*data == 0)
		soundDesc->flag = 8;
	dataPtr = data + 4;

	WRITE_LE_UINT16(dataPtr, READ_BE_UINT16(dataPtr));

	WRITE_LE_UINT32(data, (READ_LE_UINT32(data) >> 24) + ((READ_LE_UINT16(data) & 0xFF00) << 8) + ((READ_LE_UINT16(data + 2) & 0xFF) >> 8));

	soundDesc->size = READ_LE_UINT32(data);
	dsize = _vm->_dataio->getDataSize(path) - 6;
	if (dsize > soundDesc->size)
		soundDesc->size = dsize;

	soundDesc->frequency = READ_LE_UINT16(dataPtr);
	soundDesc->data += 6;
	soundDesc->timerTicks = 1193180 / READ_LE_UINT16(dataPtr);

	if (arg_4 & 2)
		arg_4 |= 1;
	if ((soundDesc->frequency < 4700) && (arg_4 & 1))
		arg_4 &= 0xFE;

	if (arg_4 & 1) {
		if ((_vm->_global->_soundFlags & BLASTER_FLAG) || (_vm->_global->_soundFlags & PROAUDIO_FLAG)) {
		}
	}

	return soundDesc;
}

int8 Game::openImd(const char *path, int16 x, int16 y, int16 repeat, int16 flags) {
	int i;
	int j;
	const char *src;
	byte *vidMem;
	Video::SurfaceDesc *surfDesc;

	if (path[0] != 0) {
		if (_imdFile == 0)
			_curImdFile[0] = 0;

		src = strrchr(path, '\\');
		src = src == 0 ? path : src+1;

		if (strcmp(_curImdFile, src) != 0) {
			closeImd();
			_imdFile = loadImdFile(path, 0, 2);
			if (_imdFile == 0)
				return 0;

			_imdX = _imdFile->x;
			_imdY = _imdFile->y;
			strcpy(_curImdFile, src);
			_imdFrameData = new byte[_imdFrameDataSize + 1000];
			_imdVidBuffer = new byte[_imdVidBufferSize + 1000];
			memset(_imdFrameData, 0, _imdFrameDataSize + 1000);
			memset(_imdVidBuffer, 0, _imdVidBufferSize + 1000);

			if (_vm->_video->_extraMode) {
				_byte_2FC83 = (flags & 0x80) ? 1 : 0;
				if (!(_imdFile->field_E & 0x100) || (_imdFile->field_E & 0x2000)) {
					setImdXY(_imdFile, 0, 0);
					_imdFile->surfDesc =
						_vm->_video->initSurfDesc(0x13, _imdFile->width, _imdFile->height, 0);
				} else {
					if (_byte_2FC82 == 0)
						_imdFile->surfDesc = _vm->_draw->_spritesArray[21];
					else
						_imdFile->surfDesc = _vm->_draw->_spritesArray[20];
					if ((x != -1) || (y != -1)) {
						_imdX = x != -1 ? x : _imdX;
						_imdY = y != -1 ? y : _imdY;
						setImdXY(_imdFile, _imdX, _imdY);
					}
				}
				if (flags & 0x40) {
					_imdX = x != -1 ? x : _imdX;
					_imdY = y != -1 ? y : _imdY;
					if (_vm->_video->_extraMode && ((_imdFile->surfDesc->vidMode & 0x7F) == 0x13)) {
						surfDesc = _vm->_video->initSurfDesc(0x13, _imdFile->width, _imdFile->height, 0);
						_vm->_video->drawSprite(_vm->_draw->_spritesArray[21], surfDesc, _imdX, _imdY,
								_imdX + _imdFile->width - 1, _imdY + _imdFile->height - 1, 0, 0, 0);
						vidMem = _imdFile->surfDesc->vidPtr;
						for (i = 0; i < _imdFile->height; i++)
							for (j = 0; j < _imdFile->width; j++, vidMem++) {
								*(vidMem) = *(surfDesc->vidPtr
										+ (j / 4)
										+ (surfDesc->width / 4 * i)
										+ (surfDesc->reserved2 * (j & 3)));
							}
						_vm->_video->freeSurfDesc(surfDesc);
					}
				}
			} else {
				if ((x != -1) || (y != -1)) {
					_imdX = x != -1 ? x : _imdX;
					_imdY = y != -1 ? y : _imdY;
					setImdXY(_imdFile, _imdX, _imdY);
				}
				_byte_2FC83 = (flags & 0x80) ? 1 : 0;
				if (_byte_2FC83 == 0)
					_imdFile->surfDesc = _vm->_draw->_spritesArray[21];
				else
					_imdFile->surfDesc = _vm->_draw->_spritesArray[20];
			}
		}
	}

	if (_imdFile == 0)
		return 0;

	if (repeat == -1) {
		closeImd();
		return 0;
	}

	_imdX = x != -1 ? x : _imdX;
	_imdY = y != -1 ? y : _imdY;

	WRITE_VAR(7, _imdFile->framesCount);

	return 1;
}

void Game::closeImd(void) {
	if (_imdFile == 0)
		return;

	if ((_imdFile->surfDesc != _vm->_draw->_spritesArray[20]) &&
			(_imdFile->surfDesc != _vm->_draw->_spritesArray[21]))
		_vm->_video->freeSurfDesc(_imdFile->surfDesc);

	finishImd(_imdFile);

	delete[] _imdFrameData;
	delete[] _imdVidBuffer;
	_imdFrameData = 0;
	_imdVidBuffer = 0;

	_imdFile = 0;
}

void Game::finishImd(Game::Imd *imdPtr) {
	if (imdPtr == 0)
		return;

/*
	if (dword_31345 != 0) {
		_vm->_sound->stopSound(0);
		dword_31345 = 0;
		delete off_31461;
		byte_31344 = 0;
	}
*/

	_vm->_dataio->closeData(imdPtr->fileHandle);

	if (imdPtr->frameCoords != 0)
		delete[] imdPtr->frameCoords;
	if (imdPtr->palette != 0)
		delete[] imdPtr->palette;
	if (imdPtr->framesPos != 0)
		delete[] imdPtr->framesPos;

	delete imdPtr;
	imdPtr = 0;
}

// flagsBit: 0 = read and set palette
//           1 = read palette
Game::Imd *Game::loadImdFile(const char *path, Video::SurfaceDesc *surfDesc, int8 flags) {
	int i;
	Imd *imdPtr;
	int16 handle;
	int16 setAllPalBak;
	char buf[18];
	Video::Color *palBak;

	int32 byte_31449 = 0;
	int32 byte_3144D = 0;

	buf[0] = 0;
	strcpy(buf, path);
	strcat(buf, ".IMD");

	handle = _vm->_dataio->openData(buf);

	if (handle < 0) {
		warning("Can't open IMD \"%s\"", buf);
		return 0;
	}

	imdPtr = new Imd;
	memset(imdPtr, 0, sizeof(Imd));

	imdPtr->palette = 0;

	_vm->_dataio->readData(handle, buf, 18);

	// "fileHandle" holds the major version while loading
	imdPtr->fileHandle = READ_LE_UINT16(buf);
	imdPtr->verMin = READ_LE_UINT16(buf + 2);
	imdPtr->framesCount = READ_LE_UINT16(buf + 4);
	imdPtr->x = READ_LE_UINT16(buf + 6);
	imdPtr->y = READ_LE_UINT16(buf + 8);
	imdPtr->width = READ_LE_UINT16(buf + 10);
	imdPtr->height = READ_LE_UINT16(buf + 12);
	imdPtr->field_E = READ_LE_UINT16(buf + 14);
	imdPtr->curFrame = READ_LE_UINT16(buf + 16);

	if (imdPtr->fileHandle != 0)
		imdPtr->verMin = 0;

	if ((imdPtr->verMin & 0xFF) < 2) {
		warning("IMD version incorrect (%d,%d)", imdPtr->fileHandle, imdPtr->verMin);
		_vm->_dataio->closeData(handle);
		delete imdPtr;
		return 0;
	}

	imdPtr->surfDesc = surfDesc;
	imdPtr->framesPos = 0;
	imdPtr->firstFramePos = imdPtr->curFrame;
	
	if (flags & 3) {
		imdPtr->palette = new Video::Color[256];
		_vm->_dataio->readData(handle, (char *) imdPtr->palette, 768);
	} else {
		_vm->_dataio->seekData(handle, 768, 1);
		imdPtr->palette = 0;
	}
	if ((flags & 3) == 1) {
		palBak = _vm->_global->_pPaletteDesc->vgaPal;
		setAllPalBak = _vm->_global->_setAllPalette;
		_vm->_global->_pPaletteDesc->vgaPal = imdPtr->palette;
		_vm->_global->_setAllPalette = 1;
		_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
		_vm->_global->_setAllPalette = setAllPalBak;
		_vm->_global->_pPaletteDesc->vgaPal = palBak;
	}

	if ((imdPtr->verMin & 0xFF) >= 3) {
		_vm->_dataio->readData(handle, buf, 2);
		imdPtr->stdX = READ_LE_UINT16(buf);
		if (imdPtr->stdX > 1) {
			warning("IMD ListI incorrect (%d)", imdPtr->stdX);
			_vm->_dataio->closeData(handle);
			delete imdPtr;
			return 0;
		}
		if (imdPtr->stdX != 0) {
			_vm->_dataio->readData(handle, buf, 8);
			imdPtr->stdX = READ_LE_UINT16(buf);
			imdPtr->stdY = READ_LE_UINT16(buf + 2);
			imdPtr->stdWidth = READ_LE_UINT16(buf + 4);
			imdPtr->stdHeight = READ_LE_UINT16(buf + 6);
		} else
			imdPtr->stdX = -1;
	} else
		imdPtr->stdX = -1;

	if ((imdPtr->verMin & 0xFF) >= 4) {
		_vm->_dataio->readData(handle, buf, 4);
		byte_31449 = READ_LE_UINT32(buf);
		imdPtr->framesPos = byte_31449 == 0 ? 0 : new int32[imdPtr->framesCount];
	} else
		imdPtr->framesPos = 0;

	if (imdPtr->verMin & 0x8000) {
		_vm->_dataio->readData(handle, buf, 4);
		byte_3144D = READ_LE_UINT32(buf);
	}

	if (imdPtr->verMin & 0x4000) {
		// loc_29C4F
//		error("GOB2 Stub! loadImdFile, imdPtr->verMin & 0x4000");
		// Sound stuff, I presume...
	}

	if (imdPtr->verMin & 0x2000) {
		_vm->_dataio->readData(handle, buf, 4);
		imdPtr->frameDataSize = READ_LE_UINT16(buf);
		imdPtr->vidBufferSize = READ_LE_UINT16(buf + 2);
	} else {
		imdPtr->frameDataSize = imdPtr->width * imdPtr->height + 1000;
		imdPtr->vidBufferSize = imdPtr->width * imdPtr->height + 1000;
	}

	if (imdPtr->framesPos != 0) {
		_vm->_dataio->seekData(handle, byte_31449, 0);
		for (i = 0; i < imdPtr->framesCount; i++) {
			_vm->_dataio->readData(handle, buf, 4);
			imdPtr->framesPos[i] = READ_LE_UINT32(buf);
		}
	}

	if (imdPtr->verMin & 0x8000) {
		_vm->_dataio->seekData(handle, byte_3144D, 0);
		imdPtr->frameCoords = new ImdCoord[imdPtr->framesCount];
		for (i = 0; i < imdPtr->framesCount; i++) {
			_vm->_dataio->readData(handle, buf, 8);
			imdPtr->frameCoords[i].left = READ_LE_UINT16(buf);
			imdPtr->frameCoords[i].top = READ_LE_UINT16(buf + 2);
			imdPtr->frameCoords[i].right = READ_LE_UINT16(buf + 4);
			imdPtr->frameCoords[i].bottom = READ_LE_UINT16(buf + 6);
		}
	} else
		imdPtr->frameCoords = 0;

	_vm->_dataio->seekData(handle, imdPtr->firstFramePos, 0);
	imdPtr->curFrame = 0;
	imdPtr->fileHandle = handle;
	imdPtr->filePos = imdPtr->firstFramePos;
	_imdFrameDataSize = imdPtr->frameDataSize;
	_imdVidBufferSize = imdPtr->vidBufferSize;
	if (flags & 0x80) {
		imdPtr->verMin |= 0x1000;
//		warning("GOB2 Stub! loadImdFile(), flags & 0x80");
	}

	return imdPtr;
}

void Game::setImdXY(Game::Imd *imdPtr, int16 x, int16 y) {
	int i;

	if (imdPtr->stdX != -1) {
		imdPtr->stdX = imdPtr->stdX - imdPtr->x + x;
		imdPtr->stdY = imdPtr->stdY - imdPtr->y + y;
	}
	
	if (imdPtr->frameCoords != 0) {
		for (i = 0; i < imdPtr->framesCount; i++) {
			imdPtr->frameCoords[i].left -= imdPtr->frameCoords[i].left - imdPtr->x + x;
			imdPtr->frameCoords[i].top -= imdPtr->frameCoords[i].top - imdPtr->y + y;
			imdPtr->frameCoords[i].right -= imdPtr->frameCoords[i].right - imdPtr->x + x;
			imdPtr->frameCoords[i].bottom -= imdPtr->frameCoords[i].bottom - imdPtr->y + y;
		}
	}

	imdPtr->x = x;
	imdPtr->y = y;
}

void Game::playImd(int16 frame, int16 arg_2, int16 arg_4, int16 arg_6, int16 arg_8, int16 lastFrame) {
	int16 var_1;
	int16 var_4 = 0;
	byte *vidMemBak;
	Video::SurfaceDesc *surfDescBak;
	Video::SurfaceDesc frontSurfBak;

	int8 byte_31344 = 0;

	if ((frame < 0) || (frame > lastFrame))
		return;

	if ((frame == arg_8) || ((frame == lastFrame) && (arg_2 == 8))) { // loc_1C3F0
		var_1 = 1;
		_vm->_draw->_applyPal = 0;
		if (arg_2 >= 4) {
			if (arg_4 != -1)
				memcpy( ((char *) (_vm->_global->_pPaletteDesc->vgaPal)) + arg_4 * 3,
						((char *) (_imdFile->palette)) + arg_4 * 3, (arg_6 - arg_4 + 1) * 3);
			else
				memcpy((char *) _vm->_global->_pPaletteDesc->vgaPal, (char *) _imdFile->palette, 768);
		}
	} else
		var_1 = 0;

	if ((var_1 == 1) && (arg_2 == 8) && (_byte_2FC83 != 0))
		_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);

	if (_vm->_video->_extraMode && (_imdFile->surfDesc->vidMode == 0x13)) {
		if ((_byte_2FC82 != 0) && (_word_2FC80 == _vm->_draw->_spritesArray[20]->vidPtr)) {
			vidMemBak = _vm->_draw->_spritesArray[20]->vidPtr;
			_vm->_draw->_spritesArray[20]->vidPtr = _vm->_draw->_spritesArray[21]->vidPtr;
			var_4 = viewImd(_imdFile, frame);
			_vm->_draw->_spritesArray[20]->vidPtr = vidMemBak;
		} else
			var_4 = viewImd(_imdFile, frame);
		if (_byte_2FC82 == 0) {
			if ((_imdFile->frameCoords == 0) || (_imdFile->frameCoords[frame].left == -1))
				_vm->_draw->invalidateRect(_imdX, _imdY,
						_imdX + _imdFile->width - 1, _imdY + _imdFile->height - 1);
			else
				_vm->_draw->invalidateRect(_imdFile->frameCoords[frame].left,
						_imdFile->frameCoords[frame].top, _imdFile->frameCoords[frame].right,
						_imdFile->frameCoords[frame].bottom);
		}
	} else {
		if ((_imdFile->field_E & 0x100) && (_vm->_video->_extraMode) &&
				(_byte_2FC82 != 0) && (sub_2C825(_imdFile) & 0x8000) && (_byte_2FC83 == 0)) {
			surfDescBak = _imdFile->surfDesc;
			if (_word_2FC80 == _vm->_draw->_spritesArray[20]->vidPtr)
				_imdFile->surfDesc = _vm->_draw->_spritesArray[21];
			else
				_imdFile->surfDesc = _vm->_draw->_spritesArray[20];
			setImdXY(_imdFile, _imdX, _imdY);
			var_4 = viewImd(_imdFile, frame);
			_imdFile->surfDesc = surfDescBak;
			setImdXY(_imdFile, 0, 0);
		} else {
			var_4 = viewImd(_imdFile, frame);
			if (!(var_4 & 0x800)) {
				if (_byte_2FC83 == 0) {
					if (_vm->_video->_extraMode) {
						if (_byte_2FC82 == 0) {
							memcpy((char *) &frontSurfBak, (char *) &_vm->_draw->_frontSurface,
									sizeof(Video::SurfaceDesc));
							memcpy((char *) &_vm->_draw->_frontSurface, (char *) &_vm->_draw->_spritesArray[21],
									sizeof(Video::SurfaceDesc));
							imdDrawFrame(_imdFile, frame, _imdX, _imdY);
							memcpy((char *) &_vm->_draw->_frontSurface, (char *) &frontSurfBak,
									sizeof(Video::SurfaceDesc));
							if ((_imdFile->frameCoords == 0) || (_imdFile->frameCoords[frame].left == -1))
								_vm->_draw->invalidateRect(_imdX, _imdY, _imdX + _imdFile->width - 1,
										_imdY + _imdFile->height - 1);
							else
								_vm->_draw->invalidateRect(_imdFile->frameCoords[frame].left,
										_imdFile->frameCoords[frame].top, _imdFile->frameCoords[frame].right,
										_imdFile->frameCoords[frame].bottom);
						} else {
							if (_word_2FC80 == _vm->_draw->_spritesArray[20]->vidPtr) { // loc_1C68D
								memcpy((char *) &frontSurfBak, (char *) &_vm->_draw->_frontSurface,
										sizeof(Video::SurfaceDesc));
								memcpy((char *) &_vm->_draw->_frontSurface, (char *) &_vm->_draw->_spritesArray[21],
										sizeof(Video::SurfaceDesc));
								imdDrawFrame(_imdFile, frame, _imdX, _imdY);
								memcpy((char *) &_vm->_draw->_frontSurface, (char *) &frontSurfBak,
										sizeof(Video::SurfaceDesc));
							} else
								imdDrawFrame(_imdFile, frame, _imdX, _imdY);
						}
					} else {
						if ((_imdFile->frameCoords == 0) || (_imdFile->frameCoords[frame].left == -1))
							_vm->_draw->invalidateRect(_imdX, _imdY, _imdX + _imdFile->width - 1,
									_imdY + _imdFile->height - 1);
						else
							_vm->_draw->invalidateRect(_imdFile->frameCoords[frame].left,
									_imdFile->frameCoords[frame].top, _imdFile->frameCoords[frame].right,
									_imdFile->frameCoords[frame].bottom);
					}
				} else
					if (_vm->_video->_extraMode)
						imdDrawFrame(_imdFile, frame, _imdX, _imdY);
			}
		}
	}

	if ((var_1 != 0) && (arg_2 == 16)) {
		if ((_vm->_draw->_spritesArray[20] != _vm->_draw->_spritesArray[21]) && (_byte_2FC83 == 0))
			_vm->_video->drawSprite(_vm->_draw->_spritesArray[21],
					_vm->_draw->_spritesArray[20], 0, 0,
					_vm->_draw->_spritesArray[21]->width - 1,
					_vm->_draw->_spritesArray[21]->height - 1, 0, 0, 0);
		_vm->_palanim->fade(_vm->_global->_pPaletteDesc, -2, 0);
		_vm->_draw->_noInvalidated = 1;
	}
	if ((var_1 != 0) && (arg_2 == 8) && (_byte_2FC83 == 0))
		_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);

	if (!(var_4 & 0x800)) {
		if (_vm->_draw->_cursorIndex == -1) {
			if (_byte_2FC82 != 0) {
				if (_word_2FC80 == _vm->_draw->_spritesArray[20]->vidPtr)
					_word_2FC80 = _vm->_draw->_spritesArray[21]->vidPtr;
				else
					_word_2FC80 = _vm->_draw->_spritesArray[20]->vidPtr;
//				warning("GOB2 Stub! sub_1BC3A(_word_2FC80);");
			} else
				_vm->_draw->blitInvalidated();
		} else
			_vm->_draw->animateCursor(-1);
	}

	if ((var_1 != 0) && ((arg_2 == 2) || (arg_2 == 4)))
		_vm->_palanim->fade(_vm->_global->_pPaletteDesc, -2, 0);

	// To allow quitting, etc. during IMDs
	_vm->_util->processInput();
	if (_vm->_quitRequested) 
		return;

	if (byte_31344 != 2) {
		if (var_4 & 0x800) {
			if (_dword_2F2B6 == 0)
				_vm->_util->delay(30);
			else {
				_dword_2F2B6 -= 30;
				if (_dword_2F2B6 < 0)
					_dword_2F2B6 = 0;
			}
		} else
			_vm->_util->waitEndFrame();
	}
	_vm->_inter->animPalette();
}

int16 Game::viewImd(Game::Imd *imdPtr, int16 frame) {
	int16 x;
	int16 y;
	int16 width;
	int16 height;
	int16 retVal;
	uint32 tmp;
	char buf[4];

	int8 var_4;
	int32 var_12 = 0;

	// .---
	int16 word_31451 = 0;
	int8 byte_31344 = 0;
	int8 byte_2DA60 = 0;
	int16 word_2DA61 = -1;
	// '---

	word_31451 = 0;

	if (imdPtr == 0)
		return (int16)0x8000;

	retVal = 0;
	var_4 = 0;

	if (frame != imdPtr->curFrame) {
		retVal |= 0x2000;
		if (frame == 0)
			imdPtr->filePos = imdPtr->firstFramePos;
		else if (frame == 1) {
			imdPtr->filePos = imdPtr->firstFramePos;
			_vm->_dataio->seekData(imdPtr->fileHandle, imdPtr->filePos, 0);
			_vm->_dataio->readData(imdPtr->fileHandle, buf, 2);
			tmp = READ_LE_UINT16(buf);
			imdPtr->filePos += tmp + 4;
		} else if (imdPtr->framesPos != 0)
			imdPtr->filePos = imdPtr->framesPos[frame];
		else
			error("Image %d innaccessible in IMD", frame);
		imdPtr->curFrame = frame;
		_vm->_dataio->seekData(imdPtr->fileHandle, imdPtr->filePos, 0);
	}

	x = imdPtr->x;
	y = imdPtr->y;
	width = imdPtr->width;
	height = imdPtr->height;

	do {
		if (frame != 0) {
			if (imdPtr->stdX != -1) {
				imdPtr->x = imdPtr->stdX;
				imdPtr->y = imdPtr->stdY;
				imdPtr->width = imdPtr->stdWidth;
				imdPtr->height = imdPtr->stdHeight;
				retVal |= 0x1000;
			}
			if ((imdPtr->frameCoords != 0) && (imdPtr->frameCoords[frame].left != -1)) {
				var_4 |= 0x400;
				imdPtr->x = imdPtr->frameCoords[frame].left;
				imdPtr->y = imdPtr->frameCoords[frame].top;
				imdPtr->width = imdPtr->frameCoords[frame].right - imdPtr->x + 1;
				imdPtr->height = imdPtr->frameCoords[frame].bottom - imdPtr->y + 1;
			}
		}

		_vm->_dataio->readData(imdPtr->fileHandle, buf, 2);
		tmp = READ_LE_UINT16(buf);

		imdPtr->filePos += 2;

		if ((tmp & 0xFFF8) == 0xFFF0) {
			if (tmp == 0xFFF0) {
				_vm->_dataio->readData(imdPtr->fileHandle, buf, 2);
				tmp = READ_LE_UINT16(buf);
				if (var_4 == 0)
					word_31451 = tmp;
				_vm->_dataio->readData(imdPtr->fileHandle, buf, 2);
				tmp = READ_LE_UINT16(buf);
				imdPtr->filePos += 4;
			} else if (tmp == 0xFFF1) {
				retVal = (int16)0x8000;
				continue;
			} else if (tmp == 0xFFF2) {
				_vm->_dataio->readData(imdPtr->fileHandle, buf, 2);
				tmp = READ_LE_UINT16(buf);
				imdPtr->filePos += 2;
				_vm->_dataio->seekData(imdPtr->fileHandle, tmp, 1);
				imdPtr->filePos += tmp;
				retVal = (int16)0x8000;
				continue;
			} else if (tmp == 0xFFF3) {
				_vm->_dataio->readData(imdPtr->fileHandle, buf, 4);
				tmp = READ_LE_UINT32(buf);
				imdPtr->filePos += 4;
				_vm->_dataio->seekData(imdPtr->fileHandle, tmp, 1);
				imdPtr->filePos += tmp;
				retVal = (int16)0x8000;
				continue;
			}
		}
		if (byte_31344 != 0) {
			if ((var_4 == 0) && (_vm->_global->_soundFlags & 0x14) && (byte_31344 == 2)) { // loc_2A503
				var_12 = _vm->_util->getTimeKey();
//				warning("GOB2 Stub! viewImd, IMD sound stuff");
			}
		}
		var_4 = 0;
		if (tmp == 0xFFFD) {
			_vm->_dataio->readData(imdPtr->fileHandle, buf, 2);
			frame = READ_LE_UINT16(buf);
			if ((imdPtr->framesPos != 0) && (byte_2DA60 == 0)) {
				word_2DA61 = frame;
				imdPtr->filePos = imdPtr->framesPos[frame];
				_vm->_dataio->seekData(imdPtr->fileHandle, imdPtr->filePos, 0);
				var_4 = 1;
				retVal |= 0x200;
				imdPtr->curFrame = frame;
			} else
				imdPtr->filePos += 2;
			continue;
		}
		if (tmp != 0) {
			imdPtr->filePos += tmp + 2;
			if (byte_2DA60 != 0) {
				_vm->_dataio->seekData(imdPtr->fileHandle, tmp + 2, 1);
			} else {
				_vm->_dataio->readData(imdPtr->fileHandle, (char *) _imdFrameData, tmp + 2);
				retVal |= *_imdFrameData;
				if (imdPtr->surfDesc == 0)
					continue;
				if (!(_vm->_video->_extraMode && (imdPtr->surfDesc->vidMode == 0x13)))
					imdRenderFrame(imdPtr);
//				else
//					warning("GOB2 Stub! viedImd, sub_2C69A(imdPtr);");
			}
		} else
			retVal |= 0x800;
	} while (var_4 != 0);

	if (byte_2DA60 != 0) {
		byte_2DA60 = 0;
		retVal |= 0x100;
	}

	imdPtr->x = x;
	imdPtr->y = y;
	imdPtr->width = width;
	imdPtr->height = height;
	imdPtr->curFrame++;
	
	return retVal;
}

void Game::imdDrawFrame(Imd *imdPtr, int16 frame, int16 x, int16 y) {
	// In the original asm, "sub_2C348" is called instead of Video::drawSprite();
	// it basically just blits.

	if (frame == 0)
		_vm->_video->drawSprite(imdPtr->surfDesc, _vm->_draw->_frontSurface, 0, 0,
				imdPtr->width - 1, imdPtr->height - 1, x, y, 1);
	else if ((imdPtr->frameCoords != 0) && (imdPtr->frameCoords[frame].left != -1))
		_vm->_video->drawSprite(imdPtr->surfDesc, _vm->_draw->_frontSurface,
				imdPtr->frameCoords[frame].left, imdPtr->frameCoords[frame].top,
				imdPtr->frameCoords[frame].right, imdPtr->frameCoords[frame].bottom,
				imdPtr->frameCoords[frame].left, imdPtr->frameCoords[frame].top, 1);
	else if (imdPtr->stdX != -1)
		_vm->_video->drawSprite(imdPtr->surfDesc, _vm->_draw->_frontSurface,
				imdPtr->stdX, imdPtr->stdY, imdPtr->stdX + imdPtr->stdWidth - 1,
				imdPtr->stdY + imdPtr->stdHeight - 1, x + imdPtr->stdX,
				y + imdPtr->stdY, 1);
	else
		_vm->_video->drawSprite(imdPtr->surfDesc, _vm->_draw->_frontSurface, 0, 0,
				imdPtr->width - 1, imdPtr->height - 1, x, y, 0);
}

void Game::imdRenderFrame(Imd *imdPtr) {
	int i;
	int16 imdX;
	int16 imdY;
	int16 imdW;
	int16 imdH;
	int16 sW;
	uint16 pixCount, pixWritten;
	uint8 type;
	byte *imdVidMem;
	byte *imdVidMemBak;
	byte *dataPtr = 0;
	byte *srcPtr = 0;
	byte *srcPtrBak = 0;

	dataPtr = (byte *) _imdFrameData;
	imdX = imdPtr->x;
	imdY = imdPtr->y;
	imdW = imdPtr->width;
	imdH = imdPtr->height;
	sW = imdPtr->surfDesc->width;
	imdVidMem = imdPtr->surfDesc->vidPtr + sW * imdY + imdX;

	type = *dataPtr++;
	srcPtr = dataPtr;

	if (type & 0x10) {
		type ^= 0x10;
		dataPtr++; // => 0x3C8       |_ palette
		dataPtr += 48; // => 0x3C9   |  stuff
	}

	srcPtr = dataPtr;
	if (type & 0x80) {
		srcPtr = (byte *) _imdVidBuffer;
		type &= 0x7F;
		if ((type == 2) && (imdW == sW)) {
			imdFrameUncompressor(imdVidMem, dataPtr);
			return;
		} else
			imdFrameUncompressor(srcPtr, dataPtr);
	}

	if (type == 2) {
		for (i = 0; i < imdH; i++) {
			memcpy(imdVidMem, srcPtr, imdW);
			srcPtr += imdW;
			imdVidMem += sW;
		}
	} else if (type == 1) {
		imdVidMemBak = imdVidMem;
		for (i = 0; i < imdH; i++) {
			pixWritten = 0;
			while (pixWritten < imdW) {
				pixCount = *srcPtr++;
				if (pixCount & 0x80) {
					pixCount = (pixCount & 0x7F) + 1;
					// Just to be safe
					pixCount = (pixWritten + pixCount) > imdW ? imdW - pixWritten : pixCount;
					pixWritten += pixCount;
					memcpy(imdVidMem, srcPtr, pixCount);
					imdVidMem += pixCount;
					srcPtr += pixCount;
				} else {
					pixCount = (pixCount + 1) % 256;
					pixWritten += pixCount;
					imdVidMem += pixCount;
				}
			}
			imdVidMemBak += sW;
			imdVidMem = imdVidMemBak;
		}
	} else if (type == 0x42) { // loc_2AFC4
		warning("=> type == 0x42");
	} else if ((type & 0xF) == 2) { // loc_2AFEC
		warning("=> (type & 0xF) == 2");
	} else { // loc_2B021
		srcPtrBak = srcPtr;
		for (i = 0; i < imdH; i += 2) {
			pixWritten = 0;
			while (pixWritten < imdW) {
				pixCount = *srcPtr++;
				if (pixCount & 0x80) {
					pixCount = (pixCount & 0x7F) + 1;
					// Just to be safe
					pixCount = (pixWritten + pixCount) > imdW ? imdW - pixWritten : pixCount;
					pixWritten += pixCount;
					memcpy(imdVidMem, srcPtr, pixCount);
					memcpy(imdVidMem + sW, srcPtr, pixCount);
					imdVidMem += pixCount;
					srcPtr += pixCount;
				} else {
					pixCount = (pixCount + 1) % 256;
					pixWritten += pixCount;
					imdVidMem += pixCount;
				}
			}
			srcPtrBak += sW + sW;
			srcPtr = srcPtrBak;
		}
	}
}

void Game::imdFrameUncompressor(byte *dest, byte *src) {
	int i;
	byte buf[4370];
	int16 chunkLength;
	int16 frameLength;
	uint16 bufPos1;
	uint16 bufPos2;
	uint16 tmp;
	uint8 chunkBitField;
	uint8 chunkCount;
	bool mode;

	frameLength = READ_LE_UINT16(src);
	src += 4;
	bufPos1 = 4078;
	mode = 0; // 275h (jnz +2)
	if ((READ_LE_UINT16(src) == 0x1234) && (READ_LE_UINT16(src + 2) == 0x5678)) {
		src += 4;
		bufPos1 = 273;
		mode = 1; // 123Ch (cmp al, 12h)
	}
	memset(buf, 32, bufPos1);
	chunkCount = 1;
	chunkBitField = 0;

	while (frameLength > 0) {
		chunkCount--;
		if (chunkCount == 0) {
			tmp = *src++;
			chunkCount = 8;
			chunkBitField = tmp;
		}
		if (chunkBitField % 2) {
			chunkBitField >>= 1;
			buf[bufPos1] = *src;
			*dest++ = *src++;
			bufPos1 = (bufPos1 + 1) % 4096;
			frameLength--;
			continue;
		}
		chunkBitField >>= 1;

		tmp = READ_LE_UINT16(src);
		src += 2;
		chunkLength = ((tmp & 0xF00) >> 8) + 3;

		if ((mode && ((chunkLength & 0xFF) == 0x12)) || (!mode && (chunkLength == 0)))
			chunkLength = *src++ + 0x12;

		bufPos2 = (tmp & 0xFF) + ((tmp >> 4) & 0x0F00);
		if (((tmp + chunkLength) >= 4096) || ((chunkLength + bufPos1) >= 4096)) {
			for (i = 0; i < chunkLength; i++, dest++) {
				*dest = buf[bufPos2];
				buf[bufPos1] = buf[bufPos2];
				bufPos1 = (bufPos1 + 1) % 4096;
				bufPos2 = (bufPos2 + 1) % 4096;
			}
			frameLength -= chunkLength;
		} else if (((tmp + chunkLength) < bufPos1) || ((chunkLength + bufPos1) < bufPos2)) {
			memcpy(dest, buf + bufPos2, chunkLength);
			dest += chunkLength;
			memmove(buf + bufPos1, buf + bufPos2, chunkLength);
			bufPos1 += chunkLength;
			bufPos2 += chunkLength;
			frameLength -= chunkLength;
		} else {
			for (i = 0; i < chunkLength; i++, dest++, bufPos1++, bufPos2++) {
				*dest = buf[bufPos2];
				buf[bufPos1] = buf[bufPos2];
			}
			frameLength -= chunkLength;
		}
	}
}

int16 Game::sub_2C825(Imd *imdPtr) {
//	warning("GOB2 Stub! sub_2C825()");
	return 0;
}

} // End of namespace Gob
