/* ScummVM - Scumm Interpreter
 * Copyright (C) 2006 The ScummVM project
 *
 * cinE Engine is (C) 2004-2005 by CinE Team
 *
 * 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-1/engines/cine/various.cpp $
 * $Id: various.cpp 22795 2006-05-31 10:55:09Z fingolfin $
 *
 */

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

#include "cine/cine.h"
#include "cine/font.h"
#include "cine/main_loop.h"
#include "cine/object.h"
#include "cine/sfx_player.h"
#include "cine/various.h"

namespace Cine {

int16 allowSystemMenu = 0;

int16 commandVar3[4];
int16 commandVar1;
int16 commandVar2;

unk1Struct messageTable[NUM_MAX_MESSAGE];

uint32 var6;
uint32 var8;
byte *var9;

uint16 var2;
uint16 var3;
uint16 var4;
uint16 var5;

int16 buildObjectListCommand(void);

void drawString(const char *string, byte param) {
}

void blitRawScreen(byte *frontBuffer) {
	gfxFlipRawPage(frontBuffer);
}

Common::File partFileHandle;

void waitPlayerInput(void) {
}

void freeAnimDataTable(void) {
}

void mainLoopSub1(void) {
}

void setTextWindow(uint16 param1, uint16 param2, uint16 param3, uint16 param4) {
}

uint16 errorVar;
byte menuVar;

void gfxFuncGen1(byte *param1, byte *param2, byte *param3, byte *param4, int16 param5) {
}

byte *page0c;

void ptrGfxFunc13(void) {
}

void gfxFuncGen2(void) {
}

uint16 allowPlayerInput;

uint16 checkForPendingDataLoadSwitch;

uint16 fadeRequired;
uint16 isDrawCommandEnabled;
uint16 waitForPlayerClick;
uint16 menuCommandLen;
uint16 var17;
uint16 var18;
uint16 var19;
uint16 var20;
byte var21;

int16 playerCommand;

char commandBuffer[80];

char currentPrcName[20];
char currentRelName[20];
char currentObjectName[20];
char currentMsgName[20];
char newPrcName[20];
char newRelName[20];
char newObjectName[20];
char newMsgName[20];

char currentBgName[8][15];
char currentCtName[15];
char currentPartName[15];
char currentDatName[30];

int16 saveVar2;

byte isInPause = 0;

uint16 defaultMenuBoxColor;

byte inputVar1 = 0;

uint16 inputVar2;
uint16 inputVar3;

const char **failureMessages;
const commandeType *defaultActionCommand;
const commandeType *systemMenu;
const commandeType *confirmMenu;
const char *commandPrepositionOn;

selectedObjStruct currentSelectedObject;

void initLanguage(Common::Language lang) {
	static const char *failureMessages_EN[] = {
		// EXAMINE
		"I don't see anything unusual.",
		"There's nothing of interest here.",
		"This isn't particularly interesting.",
		"You won't find anything.",
		// TAKE
		"I can't take that.",
		"I find it difficult.",
		"I don't see what I am supposed to take.",
		"I have difficulty in following you.",
		// INVENTORY (???)
		"There's no point.",
		"You have better things to do.",
		"Come on, don't let's waste any time.",
		"That doesn't seem to me to be a good idea.",
		// USE
		"I don't see why I should do that.",
		"It's had no effect whatsoever.",
		"It won't produce any results.",
		"Try and find something else.",
		// OPERATE
		"It doesn't work.",
		"Let suppose you are trying and don't let's mention it again.",
		"Nothing happens.",
		"You have better things to do.",
		// SPEAK
		"No answer.",
		"More action , less talking !",
		"I'd be very surprised if you got an answer",
		"A wall of silence ..."
	};

	static const commandeType defaultActionCommand_EN[] = {
		"EXAMINE",
		"TAKE",
		"INVENTORY",
		"USE",
		"OPERATE",
		"SPEAK",
		"NOACTION"
	};

	static const commandeType systemMenu_EN[] = {
		"Pause",
		"Restart Game",
		"Quit",
		"Backup Drive is A:",
		"Restore game",
		"Save game"
	};

	static const commandeType confirmMenu_EN[] = {
		"Ok, go ahead ...",
		"Absolutely Not!"
	};

	// \x82 == é, \x85 == à, \x87 == ç, \x88 == ê
	static const char *failureMessages_FR[] = {
		// EXAMINER
		"Je ne vois rien de special.",
		"Il n'y a rien d'int\x82ressant.",
		"Cela pr\x82sente peu d'int\x82r\x88ts.",
		"Vous ne trouvez rien.",
		// PRENDRE
		"Je ne peux pas prendre cela.",
		"Cela me semble difficile",
		"Je ne vois pas ce qu'il y a \x85 prendre",
		"j'ai du mal \x85 vous suivre.",
		// INVENTAIRE (???)
		"C'est inutile",
		"Vous avez mieux \x85 faire",
		"Allons, ne perdons pas de temps",
		"\x87""a ne me semble pas \x88tre une bonne id\x82""e",
		// UTILISER
		"Je ne vois pas pourquoi je ferais cela.",
		"C'est absolument sans effets",
		"Cela n'amenerait \x85 rien",
		"Essayez de trouver autre chose.",
		// ACTIONNER
		"Ca ne marche pas",
		"Supposons que vous essayez et n'en parlons plus.",
		"Rien n'y fait.",
		"Vous avez mieux \x85 faire.",
		// PARLER
		"Vous lui parlez . Sans r\x82ponse.",
		"Plus d'actes et moins de Paroles !",
		"Je serais bien surpris si vous obteniez une r\x82ponse.",
		"Un mur de silence ..."
	};

	static const commandeType defaultActionCommand_FR[] = {
		"EXAMINER",
		"PRENDRE",
		"INVENTAIRE",
		"UTILISER",
		"ACTIONNER",
		"PARLER",
		"NOACTION"
	};

	static const commandeType systemMenu_FR[] = {
		"Pause",
		"Nouvelle partie",
		"Quitter",
		"Lecteur de Svg. A:",
		"Charger une partie",
		"Sauver la partie"
	};

	static const commandeType confirmMenu_FR[] = {
		"Ok , Vas-y ...",
		"Surtout Pas !"
	};

	switch (lang) {
	case Common::FR_FRA:
		failureMessages = failureMessages_FR;
		defaultActionCommand = defaultActionCommand_FR;
		systemMenu = systemMenu_FR;
		confirmMenu = confirmMenu_FR;
		commandPrepositionOn = "sur";
		break;
	default:
		failureMessages = failureMessages_EN;
		defaultActionCommand = defaultActionCommand_EN;
		systemMenu = systemMenu_EN;
		confirmMenu = confirmMenu_EN;
		commandPrepositionOn = "on";
		break;
	}
}

void mainLoopSub3(void) {
}

int16 stopObjectScript(int16 entryIdx) {
	prcLinkedListStruct *currentHead = &objScriptList;
	prcLinkedListStruct *tempHead = currentHead;

	currentHead = tempHead->next;

	while (currentHead) {
		if (currentHead->scriptIdx == entryIdx) {
			currentHead->scriptIdx = -1;
			return 0;
		}

		currentHead = currentHead->next;
	}

	return -1;
}

void runObjectScript(int16 entryIdx) {
	uint16 i;
	prcLinkedListStruct *pNewElement;
	prcLinkedListStruct *currentHead = &objScriptList;
	prcLinkedListStruct *tempHead = currentHead;

	currentHead = tempHead->next;

	while (currentHead) {
		tempHead = currentHead;

		assert(tempHead);

		currentHead = tempHead->next;
	}

	pNewElement = (prcLinkedListStruct *)malloc(sizeof(prcLinkedListStruct));

	assert(pNewElement);

	pNewElement->next = tempHead->next;
	tempHead->next = pNewElement;

	// copy the stack into the script instance
	for (i = 0; i < SCRIPT_STACK_SIZE; i++) {
		pNewElement->stack[i] = 0;
	}

	for (i = 0; i < 50; i++) {
		pNewElement->localVars[i] = 0;
	}

	pNewElement->compareResult = 0;
	pNewElement->scriptPosition = 0;

	pNewElement->scriptPtr = (byte *)relTable[entryIdx].data;
	pNewElement->scriptIdx = entryIdx;

	computeScriptStack(pNewElement->scriptPtr, pNewElement->stack, relTable[entryIdx].size);
}

void addPlayerCommandMessage(int16 cmd) {
	overlayHeadElement *currentHeadPtr = overlayHead.next;
	overlayHeadElement *tempHead = &overlayHead;
	overlayHeadElement *pNewElement;

	while (currentHeadPtr) {
		tempHead = currentHeadPtr;
		currentHeadPtr = tempHead->next;
	}

	pNewElement = (overlayHeadElement *)malloc(sizeof(overlayHeadElement));

	assert(pNewElement);

	pNewElement->next = tempHead->next;
	tempHead->next = pNewElement;

	pNewElement->objIdx = cmd;
	pNewElement->type = 3;

	if (!currentHeadPtr) {
		currentHeadPtr = &overlayHead;
	}

	pNewElement->previous = currentHeadPtr->previous;
	currentHeadPtr->previous = pNewElement;
}

int16 getRelEntryForObject(uint16 param1, uint16 param2, selectedObjStruct *pSelectedObject) {
	int16 i;
	int16 di = -1;

	for (i = 0; i < NUM_MAX_REL; i++) {
		if (relTable[i].data && relTable[i].obj1Param1 == param1 && relTable[i].obj1Param2 == pSelectedObject->idx) {
			if (param2 == 1) {
				di = i;
			} else if (param2 == 2) {
				if (relTable[i].obj2Param == pSelectedObject->param) {
					di = i;
				}
			}
		}

		if (di != -1)
			break;
	}

	return di;
}

int16 getObjectUnderCursor(uint16 x, uint16 y) {
	overlayHeadElement *currentHead = overlayHead.previous;

	while (currentHead) {
		if (currentHead->type < 2) {
			if (objectTable[currentHead->objIdx].name[0]) {
				int16 objX;
				int16 objY;
				int16 frame;
				int16 part;
				int16 treshold;
				int16 height;
				int16 xdif;
				int16 ydif;

				objX = objectTable[currentHead->objIdx].x;
				objY = objectTable[currentHead->objIdx].y;

				frame = ABS((int16)(objectTable[currentHead->objIdx].frame));

				part = objectTable[currentHead->objIdx].part;

				if (currentHead->type == 0) {
					treshold = animDataTable[frame].var1;
				} else {
					treshold = animDataTable[frame].width / 2;
				}

				height = animDataTable[frame].height;

				xdif = x - objX;
				ydif = y - objY;

				if ((xdif >= 0) && ((treshold << 4) > xdif) && (ydif > 0) && (ydif < height)) {
					if (animDataTable[frame].ptr1) {
						if (gameType == Cine::GID_OS)
							return currentHead->objIdx;

						if (currentHead->type == 0)	{ // use generated mask
							if (gfxGetBit(x - objX, y - objY, animDataTable[frame].ptr2, animDataTable[frame].width)) {
								return currentHead->objIdx;
							}
						} else if (currentHead->type == 1) { // is mask
							if (gfxGetBit(x - objX, y - objY, animDataTable[frame].ptr1, animDataTable[frame].width * 4)) {
								return currentHead->objIdx;
							}
						}
					}
				}
			}
		}

		currentHead = currentHead->previous;
	}

	return -1;
}

static commandeType currentSaveName[10];

int16 loadSaveDirectory(void) {
	Common::InSaveFile *fHandle;

	if (gameType == Cine::GID_FW)
		fHandle = g_saveFileMan->openForLoading("FW.DIR");
	else
		fHandle = g_saveFileMan->openForLoading("OS.DIR");

	if (!fHandle) {
		return 0;
	}

	fHandle->read(currentSaveName, 10 * 20);
	delete fHandle;

	return 1;
}

int16 currentDisk;

void loadObjectScriptFromSave(Common::InSaveFile *fHandle) {
	int16 i;

	prcLinkedListStruct *newElement;
	prcLinkedListStruct *currentHead = &globalScriptsHead;
	prcLinkedListStruct *tempHead = currentHead;

	currentHead = tempHead->next;

	while (currentHead) {
		tempHead = currentHead;
		currentHead = tempHead->next;
	}

	newElement =
	    (prcLinkedListStruct *)malloc(sizeof(prcLinkedListStruct));

	newElement->next = tempHead->next;
	tempHead->next = newElement;

	for (i = 0; i < SCRIPT_STACK_SIZE; i++)
		newElement->stack[i] = fHandle->readUint16BE();

	for (i = 0; i < 50; i++)
		newElement->localVars[i] = fHandle->readUint16BE();

	newElement->compareResult = fHandle->readUint16BE();
	newElement->scriptPosition = fHandle->readUint16BE();
	newElement->scriptIdx = fHandle->readUint16BE();

	newElement->scriptPtr = (byte *)relTable[newElement->scriptIdx].data;
}

void loadGlobalScriptFromSave(Common::InSaveFile *fHandle) {
	int16 i;

	prcLinkedListStruct *newElement;
	prcLinkedListStruct *currentHead = &globalScriptsHead;
	prcLinkedListStruct *tempHead = currentHead;

	currentHead = tempHead->next;

	while (currentHead) {
		tempHead = currentHead;
		currentHead = tempHead->next;
	}

	newElement = (prcLinkedListStruct *)malloc(sizeof(prcLinkedListStruct));

	newElement->next = tempHead->next;
	tempHead->next = newElement;

	for (i = 0; i < SCRIPT_STACK_SIZE; i++)
		newElement->stack[i] = fHandle->readUint16BE();

	for (i = 0; i < 50; i++)
		newElement->localVars[i] = fHandle->readUint16BE();

	newElement->compareResult = fHandle->readUint16BE();
	newElement->scriptPosition = fHandle->readUint16BE();
	newElement->scriptIdx = fHandle->readUint16BE();

	newElement->scriptPtr = scriptTable[newElement->scriptIdx].ptr;
}

void loadOverlayFromSave(Common::InSaveFile *fHandle) {
	overlayHeadElement *newElement;
	overlayHeadElement *currentHead = &overlayHead;
	overlayHeadElement *tempHead = currentHead;

	currentHead = tempHead->next;

	while (currentHead) {
		tempHead = currentHead;
		currentHead = tempHead->next;
	}

	newElement = (overlayHeadElement *)malloc(sizeof(overlayHeadElement));

	fHandle->readUint32BE();
	fHandle->readUint32BE();

	newElement->objIdx = fHandle->readUint16BE();
	newElement->type = fHandle->readUint16BE();
	newElement->x = fHandle->readSint16BE();
	newElement->y = fHandle->readSint16BE();
	newElement->width = fHandle->readSint16BE();
	newElement->color = fHandle->readSint16BE();

	newElement->next = tempHead->next;
	tempHead->next = newElement;

	if (!currentHead)
		currentHead = &overlayHead;

	newElement->previous = currentHead->previous;
	currentHead->previous = newElement;
}

void setupGlobalScriptList(void) {
	prcLinkedListStruct *currentHead = globalScriptsHead.next;

	while (currentHead) {
		currentHead->scriptPtr = scriptTable[currentHead->scriptIdx].ptr;
		currentHead = currentHead->next;
	}
}

void setupObjectScriptList(void) {
	prcLinkedListStruct *currentHead = objScriptList.next;

	while (currentHead) {
		currentHead->scriptPtr = (byte *)relTable[currentHead->scriptIdx].data;
		currentHead = currentHead->next;
	}
}

int16 makeLoad(char *saveName) {
	int16 i;
	int16 size;
	Common::InSaveFile *fHandle;

	fHandle = g_saveFileMan->openForLoading(saveName);

	if (!fHandle) {
		drawString("Cette sauvegarde n'existe pas ...", 0);
		waitPlayerInput();
		// restoreScreen();
		checkDataDisk(-1);
		return -1;
	}

	g_sfxPlayer->stop();
	freeAnimDataTable();
	unloadAllMasks();
	// if (gameType == Cine::GID_OS) {
	// 	freeUnkList();
	// }
	freePrcLinkedList();
	releaseObjectScripts();
	closeEngine7();
	closePart();

	for (i = 0; i < NUM_MAX_REL; i++) {
		if (relTable[i].data) {
			free(relTable[i].data);
			relTable[i].data = NULL;
			relTable[i].size = 0;
			relTable[i].obj1Param1 = 0;
			relTable[i].obj1Param2 = 0;
			relTable[i].obj2Param = 0;
		}
	}

	for (i = 0; i < NUM_MAX_SCRIPT; i++) {
		if (scriptTable[i].ptr) {
			free(scriptTable[i].ptr);
			scriptTable[i].ptr = NULL;
			scriptTable[i].size = 0;
		}
	}

	for (i = 0; i < NUM_MAX_MESSAGE; i++) {
		messageTable[i].len = 0;

		if (messageTable[i].ptr) {
			free(messageTable[i].ptr);
			messageTable[i].ptr = NULL;
		}
	}

	for (i = 0; i < NUM_MAX_OBJECT; i++) {
		objectTable[i].part = 0;
		objectTable[i].name[0] = 0;
		objectTable[i].frame = 0;
		objectTable[i].mask = 0;
		objectTable[i].costume = 0;
	}

	for (i = 0; i < 255; i++) {
		globalVars[i] = 0;
	}

	var2 = 0;
	var3 = 0;
	var4 = 0;
	var5 = 0;

	strcpy(newPrcName, "");
	strcpy(newRelName, "");
	strcpy(newObjectName, "");
	strcpy(newMsgName, "");
	strcpy(currentBgName[0], "");
	strcpy(currentCtName, "");

	allowPlayerInput = 0;
	waitForPlayerClick = 0;
	playerCommand = -1;
	isDrawCommandEnabled = 0;

	strcpy(commandBuffer, "");

	globalVars[VAR_MOUSE_X_POS] = 0;
	globalVars[VAR_MOUSE_Y_POS] = 0;

	fadeRequired = 0;

	for (i = 0; i < 16; i++) {
		c_palette[i] = 0;
	}

	checkForPendingDataLoadSwitch = 0;

	currentDisk = fHandle->readUint16BE();

	fHandle->read(currentPartName, 13);
	fHandle->read(currentDatName, 13);

	saveVar2 = fHandle->readSint16BE();

	fHandle->read(currentPrcName, 13);
	fHandle->read(currentRelName, 13);
	fHandle->read(currentMsgName, 13);
	fHandle->read(currentBgName[0], 13);
	fHandle->read(currentCtName, 13);

	fHandle->readUint16BE();
	fHandle->readUint16BE();

	for (i = 0; i < 255; i++) {
		objectTable[i].x = fHandle->readSint16BE();
		objectTable[i].y = fHandle->readSint16BE();
		objectTable[i].mask = fHandle->readUint16BE();
		objectTable[i].frame = fHandle->readSint16BE();
		objectTable[i].costume = fHandle->readSint16BE();
		fHandle->read(objectTable[i].name, 20);
		objectTable[i].part = fHandle->readUint16BE();
	}

	for (i = 0; i < 16; i++) {
		c_palette[i] = fHandle->readUint16BE();
	}

	for (i = 0; i < 16; i++) {
		tempPalette[i] = fHandle->readUint16BE();
	}

	for (i = 0; i < 255; i++) {
		globalVars[i] = fHandle->readUint16BE();
	}

	for (i = 0; i < 16; i++) {
		zoneData[i] = fHandle->readUint16BE();
	}

	for (i = 0; i < 4; i++) {
		commandVar3[i] = fHandle->readUint16BE();
	}

	fHandle->read(commandBuffer, 0x50);

	defaultMenuBoxColor = fHandle->readUint16BE();
	bgVar0 = fHandle->readUint16BE();
	allowPlayerInput = fHandle->readUint16BE();
	playerCommand = fHandle->readSint16BE();
	commandVar1 = fHandle->readSint16BE();
	isDrawCommandEnabled = fHandle->readUint16BE();
	var5 = fHandle->readUint16BE();
	var4 = fHandle->readUint16BE();
	var3 = fHandle->readUint16BE();
	var2 = fHandle->readUint16BE();
	commandVar2 = fHandle->readSint16BE();
	defaultMenuBoxColor2 = fHandle->readUint16BE();

	fHandle->readUint16BE();
	fHandle->readUint16BE();

	for (i = 0; i < NUM_MAX_ANIMDATA; i++) {
		animDataTable[i].width = fHandle->readUint16BE();
		animDataTable[i].var1 = fHandle->readUint16BE();
		animDataTable[i].bpp = fHandle->readUint16BE();
		animDataTable[i].height = fHandle->readUint16BE();
		animDataTable[i].ptr1 = NULL;
		animDataTable[i].ptr2 = NULL;
		animDataTable[i].fileIdx = fHandle->readSint16BE();
		animDataTable[i].frameIdx = fHandle->readSint16BE();
		fHandle->read(animDataTable[i].name, 10);
		animDataTable[i].refresh = (fHandle->readByte() != 0);
	}

	// TODO: handle screen params (really required ?)
	fHandle->readUint16BE();
	fHandle->readUint16BE();
	fHandle->readUint16BE();
	fHandle->readUint16BE();
	fHandle->readUint16BE();
	fHandle->readUint16BE();

	size = fHandle->readSint16BE();
	for (i = 0; i < size; i++) {
		loadGlobalScriptFromSave(fHandle);
	}

	size = fHandle->readSint16BE();
	for (i = 0; i < size; i++) {
		loadObjectScriptFromSave(fHandle);
	}

	size = fHandle->readSint16BE();
	for (i = 0; i < size; i++) {
		loadOverlayFromSave(fHandle);
	}

	size = fHandle->readSint16BE();
	for (i = 0; i < size; i++) {
		// loadBgIncrustFromSave(fHandle);
	}

	delete fHandle;

	checkDataDisk(currentDisk);

	if (strlen(currentPartName)) {
		loadPart(currentPartName);
	}

	if (strlen(currentPrcName)) {
		loadPrc(currentPrcName);
		setupGlobalScriptList();
	}

	if (strlen(currentRelName)) {
		loadRel(currentRelName);
		setupObjectScriptList();
	}

	if (strlen(currentMsgName)) {
		loadMsg(currentMsgName);
	}

	if (strlen(currentBgName[0])) {
		loadBg(currentBgName[0]);
	}

	if (strlen(currentCtName)) {
		loadCt(currentCtName);
	}

	loadResourcesFromSave();
	//reincrustAllBg();

	setMouseCursor(MOUSE_CURSOR_NORMAL);

	if (strlen(currentDatName)) {
/*		i = saveVar2;
		saveVar2 = 0;
		loadMusic();
		if (i) {
			playMusic();
		}*/
	}

	return 0;
}

void makeSave(char *saveFileName) {
	int16 i;
	Common::OutSaveFile *fHandle;

	fHandle = g_saveFileMan->openForSaving(saveFileName);

	if (!fHandle) {
		drawString("Could not create save file ...", 0);
		waitPlayerInput();
		// restoreScreen();
		checkDataDisk(-1);
		return;
	}

	fHandle->writeUint16BE(currentDisk);
	fHandle->write(currentPartName, 13);
	fHandle->write(currentDatName, 13);
	fHandle->writeUint16BE(saveVar2);
	fHandle->write(currentPrcName, 13);
	fHandle->write(currentRelName, 13);
	fHandle->write(currentMsgName, 13);
	fHandle->write(currentBgName[0], 13);
	fHandle->write(currentCtName, 13);

	fHandle->writeUint16BE(0xFF);
	fHandle->writeUint16BE(0x20);

	for (i = 0; i < 255; i++) {
		fHandle->writeUint16BE(objectTable[i].x);
		fHandle->writeUint16BE(objectTable[i].y);
		fHandle->writeUint16BE(objectTable[i].mask);
		fHandle->writeUint16BE(objectTable[i].frame);
		fHandle->writeUint16BE(objectTable[i].costume);
		fHandle->write(objectTable[i].name, 20);
		fHandle->writeUint16BE(objectTable[i].part);
	}

	for (i = 0; i < 16; i++) {
		fHandle->writeUint16BE(c_palette[i]);
	}

	for (i = 0; i < 16; i++) {
		fHandle->writeUint16BE(tempPalette[i]);
	}

	for (i = 0; i < 255; i++) {
		fHandle->writeUint16BE(globalVars[i]);
	}

	for (i = 0; i < 16; i++) {
		fHandle->writeUint16BE(zoneData[i]);
	}

	for (i = 0; i < 4; i++) {
		fHandle->writeUint16BE(commandVar3[i]);
	}

	fHandle->write(commandBuffer, 0x50);

	fHandle->writeUint16BE(defaultMenuBoxColor);
	fHandle->writeUint16BE(bgVar0);
	fHandle->writeUint16BE(allowPlayerInput);
	fHandle->writeUint16BE(playerCommand);
	fHandle->writeUint16BE(commandVar1);
	fHandle->writeUint16BE(isDrawCommandEnabled);
	fHandle->writeUint16BE(var5);
	fHandle->writeUint16BE(var4);
	fHandle->writeUint16BE(var3);
	fHandle->writeUint16BE(var2);
	fHandle->writeUint16BE(commandVar2);
	fHandle->writeUint16BE(defaultMenuBoxColor2);

	fHandle->writeUint16BE(0xFF);
	fHandle->writeUint16BE(0x1E);

	for (i = 0; i < NUM_MAX_ANIMDATA; i++) {
		fHandle->writeUint16BE(animDataTable[i].width);
		fHandle->writeUint16BE(animDataTable[i].var1);
		fHandle->writeUint16BE(animDataTable[i].bpp);
		fHandle->writeUint16BE(animDataTable[i].height);
		fHandle->writeSint16BE(animDataTable[i].fileIdx);
		fHandle->writeSint16BE(animDataTable[i].frameIdx);
		fHandle->write(animDataTable[i].name, 10);

		// Horrifyingly, cinE used to dump the entire struct to the
		// save file, including the data pointers. While these pointers
		// would be invalid after loading, the loadResourcesFromSave()
		// function would still test if ptr1 was non-NULL, presumably
		// to see if the object was present in the room.

		fHandle->writeByte(animDataTable[i].ptr1 ? 1 : 0);
	}

	fHandle->writeUint16BE(0);  // Screen params, unhandled
	fHandle->writeUint16BE(0);
	fHandle->writeUint16BE(0);
	fHandle->writeUint16BE(0);
	fHandle->writeUint16BE(0);
	fHandle->writeUint16BE(0);

	{
		int16 numScript = 0;
		prcLinkedListStruct *currentHead = globalScriptsHead.next;

		while (currentHead) {
			numScript++;
			currentHead = currentHead->next;
		}

		fHandle->writeUint16BE(numScript);

		// actual save
		currentHead = globalScriptsHead.next;

		while (currentHead) {
			for (i = 0; i < SCRIPT_STACK_SIZE; i++) {
				fHandle->writeUint16BE(currentHead->stack[i]);
			}

			for (i = 0; i < 50; i++) {
				fHandle->writeUint16BE(currentHead->localVars[i]);
			}

			fHandle->writeUint16BE(currentHead->compareResult);
			fHandle->writeUint16BE(currentHead->scriptPosition);
			fHandle->writeUint16BE(currentHead->scriptIdx);

			currentHead = currentHead->next;
		}
	}

	{
		int16 numScript = 0;
		prcLinkedListStruct *currentHead = objScriptList.next;

		while (currentHead) {
			numScript++;
			currentHead = currentHead->next;
		}

		fHandle->writeUint16BE(numScript);

		// actual save
		currentHead = objScriptList.next;

		while (currentHead) {
			for (i = 0; i < SCRIPT_STACK_SIZE; i++) {
				fHandle->writeUint16BE(currentHead->stack[i]);
			}

			for (i = 0; i < 50; i++) {
				fHandle->writeUint16BE(currentHead->localVars[i]);
			}

			fHandle->writeUint16BE(currentHead->compareResult);
			fHandle->writeUint16BE(currentHead->scriptPosition);
			fHandle->writeUint16BE(currentHead->scriptIdx);

			currentHead = currentHead->next;
		}
	}

	{
		int16 numScript = 0;
		overlayHeadElement *currentHead = overlayHead.next;

		while (currentHead) {
			numScript++;
			currentHead = currentHead->next;
		}

		fHandle->writeUint16BE(numScript);

		// actual save
		currentHead = overlayHead.next;

		while (currentHead) {
			fHandle->writeUint32BE(0);
			fHandle->writeUint32BE(0);
			fHandle->writeUint16BE(currentHead->objIdx);
			fHandle->writeUint16BE(currentHead->type);
			fHandle->writeSint16BE(currentHead->x);
			fHandle->writeSint16BE(currentHead->y);
			fHandle->writeSint16BE(currentHead->width);
			fHandle->writeSint16BE(currentHead->color);

			currentHead = currentHead->next;
		}
	}

	// This corresponds to the loadBgIncrustFromSave() handling, I think.
	fHandle->writeUint16BE(0);

	delete fHandle;

	setMouseCursor(MOUSE_CURSOR_NORMAL);
}

void makeSystemMenu(void) {
	int16 numEntry;
	int16 mouseButton;
	int16 mouseX;
	int16 mouseY;
	int16 systemCommand;

	if (!allowSystemMenu) {
		manageEvents();
		getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton,(uint16 *)&mouseX, (uint16 *)&mouseY);

		while (mouseButton) {
			manageEvents();
			getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
		}

		numEntry = 6;

		if (!allowPlayerInput) {
			numEntry--;
		}

		systemCommand = makeMenuChoice(systemMenu, numEntry, mouseX, mouseY, 140);

		switch (systemCommand) {
		case 0:
			{
				drawString("PAUSE", 0);
				waitPlayerInput();
				break;
			}
		case 1:
			{
				getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
				if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) {
					//reinitEngine();
				}
				break;
			}
		case 2:
			{
				getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
				if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) {
					exitEngine = 1;
				}
				break;
			}
		case 3:	// Select save drive... change ?
			{
				break;
			}
		case 4:	// load game
			{
				if (loadSaveDirectory()) {
					int16 selectedSave;

					getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
					selectedSave = makeMenuChoice(currentSaveName, 10, mouseX, mouseY + 8, 180);

					if (selectedSave >= 0) {
						char saveNameBuffer[256];
						if (gameType == Cine::GID_FW)
							sprintf(saveNameBuffer, "FW.%1d", selectedSave);
						else
							sprintf(saveNameBuffer, "OS.%1d", selectedSave);

						getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
						if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) {
							char loadString[256];

							sprintf(loadString, "Chargement de | %s", currentSaveName[selectedSave]);
							drawString(loadString, 0);

							makeLoad(saveNameBuffer);
						} else {
							drawString("Chargement Annulé ...", 0);
							waitPlayerInput();
							checkDataDisk(-1);
						}
					} else {
						drawString("Chargement Annulé ...", 0);
						waitPlayerInput();
						checkDataDisk(-1);
					}
				} else {
					drawString("Aucune sauvegarde dans le lecteur ...", 0);
					waitPlayerInput();
					checkDataDisk(-1);
				}
				break;
			}
		case 5:
			{
				int16 selectedSave;

				loadSaveDirectory();
				selectedSave = makeMenuChoice(currentSaveName, 10, mouseX, mouseY + 8, 180);

				if (selectedSave >= 0) {
					char saveFileName[256];
					//makeTextEntryMenu("Veuillez entrer le Nom de la Sauvegarde .", &currentSaveName[selectedSave], 120);
					sprintf(currentSaveName[selectedSave], "temporary save name");

					if (gameType == Cine::GID_FW)
						sprintf(saveFileName, "FW.%1d", selectedSave);
					else
						sprintf(saveFileName, "OS.%1d", selectedSave);

					getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY);
					if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) {
						char saveString[256];

						Common::OutSaveFile *fHandle;

						if (gameType == Cine::GID_FW)
							fHandle = g_saveFileMan->openForSaving("FW.DIR");
						else
							fHandle = g_saveFileMan->openForSaving("OS.DIR");

						fHandle->write(currentSaveName, 200);
						delete fHandle;

						sprintf(saveString, "Sauvegarde de |%s", currentSaveName[selectedSave]);
						drawString(saveString, 0);

						makeSave(saveFileName);

						checkDataDisk(-1);
					} else {
						drawString("Sauvegarde Annulée ...", 0);
						waitPlayerInput();
						checkDataDisk(-1);
					}
				}
				break;
			}
		}
	}
}

const int16 choiceResultTable[] = {
	1,
	1,
	1,
	2,
	1,
	1,
	1
};

const int16 subObjectUseTable[] = {
	3,
	3,
	3,
	3,
	3,
	0,
	0
};

const int16 canUseOnItemTable[] = {
	1,
	0,
	0,
	1,
	1,
	0,
	0
};

commandeType objectListCommand[20];
int16 objListTab[20];

void makeTextEntry(const commandeType commandList[], uint16 height, uint16 X, uint16 Y, uint16 width) {
	byte color = 2;
	byte color2;
	int16 paramY = (height * 9) + 10;
	int16 currentX;
	int16 currentY;
	int16 i;
	uint16 j;

	if (X + width > 319) {
		X = 319 - width;
	}

	if (Y + paramY > 199) {
		Y = 199 - paramY;
	}

	color2 = defaultMenuBoxColor2;

	hideMouse();
	blitRawScreen(page1Raw);

	gfxDrawPlainBoxRaw(X, Y, X + width, Y + 4, color2, page1Raw);

	currentX = X + 4;
	currentY = Y + 4;

	for (i = 0; i < height; i++) {
		gfxDrawPlainBoxRaw(X, currentY, X + width, currentY + 9, color2, page1Raw);
		currentX = X + 4;

		for (j = 0; j < strlen(commandList[i]); j++) {
			byte currentChar = commandList[i][j];

			if (currentChar == ' ') {
				currentX += 5;
			} else {
				byte characterWidth = fontParamTable[currentChar].characterWidth;

				if (characterWidth) {
					byte characterIdx = fontParamTable[currentChar].characterIdx;
					drawSpriteRaw(textTable[characterIdx][0], textTable[characterIdx][1], 2, 8, page1Raw, currentX, currentY);
					currentX += characterWidth + 1;
				}
			}
		}

		currentY += 9;
	}

	gfxDrawPlainBoxRaw(X, currentY, X + width, currentY + 4, color2, page1Raw);	// bottom part
	gfxDrawLine(X + 1, Y + 1, X + width - 1, Y + 1, 0, page1Raw);	// top
	gfxDrawLine(X + 1, currentY + 3, X + width - 1, currentY + 3, 0, page1Raw);	// bottom
	gfxDrawLine(X + 1, Y + 1, X + 1, currentY + 3, 0, page1Raw);	// left
	gfxDrawLine(X + width - 1, Y + 1, X + width - 1, currentY + 3, 0, page1Raw);	// left

	gfxDrawLine(X, Y, X + width, Y, color, page1Raw);
	gfxDrawLine(X, currentY + 4, X + width, currentY + 4, color, page1Raw);
	gfxDrawLine(X, Y, X, currentY + 4, color, page1Raw);
	gfxDrawLine(X + width, Y, X + width, currentY + 4, color, page1Raw);

	blitRawScreen(page1Raw);
}

void processInventory(int16 x, int16 y) {
	int16 listSize = buildObjectListCommand();
	uint16 button;

	if (!listSize)
		return;

	makeTextEntry(objectListCommand, listSize, x, y, 140);

	do {
		manageEvents();
		getMouseData(mouseUpdateStatus, &button, &dummyU16, &dummyU16);
	} while (!button);
}

int16 buildObjectListCommand(void) {
	int16 i;
	int16 j;

	assert(gameType == Cine::GID_FW);

	for (i = 0; i < 20; i++) {
		objectListCommand[i][0] = 0;
	}

	j = 0;

	for (i = 0; i < 255; i++) {
		if (objectTable[i].name[0] && objectTable[i].costume == -2) {
			strcpy(objectListCommand[j], objectTable[i].name);
			objListTab[j] = i;
			j++;
		}
	}

	return j;
}

int16 buildObjectListCommand2(int16 param) {
	int16 i;
	int16 j;

	assert(gameType == Cine::GID_OS);

	for (i = 0; i < 20; i++) {
		objectListCommand[i][0] = 0;
	}

	j = 0;

	for (i = 0; i < 255; i++) {
		if (objectTable[i].name[0] && objectTable[i].costume == param) {
			strcpy(objectListCommand[j], objectTable[i].name);
			objListTab[j] = i;
			j++;
		}
	}

	return j;
}

int16 selectSubObject(int16 x, int16 y) {
	int16 listSize = buildObjectListCommand();
	int16 selectedObject;

	if (!listSize) {
		return -2;
	}

	selectedObject = makeMenuChoice(objectListCommand, listSize, x, y, 140);

	if (selectedObject == -1)
		return -1;

	return objListTab[selectedObject];
}

int16 selectSubObject2(int16 x, int16 y, int16 param) {
	int16 listSize = buildObjectListCommand2(param);
	int16 selectedObject;

	if (!listSize) {
		return -2;
	}

	selectedObject = makeMenuChoice2(objectListCommand, listSize, x, y, 140);

	if (selectedObject == -1)
		return -1;

	if (selectedObject >= 8000) {
		return objListTab[selectedObject - 8000] + 8000;
	}

	return objListTab[selectedObject];
}

int16 canUseOnObject = 0;

void makeCommandLine(void) {
	uint16 x;
	uint16 y;

	commandVar1 = 0;
	commandVar2 = -10;

	if (playerCommand != -1) {
		strcpy(commandBuffer, defaultActionCommand[playerCommand]);
	} else {
		strcpy(commandBuffer, "");
	}

	if ((playerCommand != -1) && (choiceResultTable[playerCommand] == 2)) {	// need object selection ?
		int16 si;

		getMouseData(mouseUpdateStatus, &dummyU16, &x, &y);

		if (gameType == Cine::GID_FW) {
			si = selectSubObject(x, y + 8);
		} else {
			si = selectSubObject2(x, y + 8, -subObjectUseTable[playerCommand]);
		}

		if (si < 0) {
			playerCommand = -1;
			strcpy(commandBuffer, "");
		} else {
			if (gameType == Cine::GID_OS) {
				if (si >= 8000) {
					si -= 8000;
					canUseOnObject = canUseOnItemTable[playerCommand];
				} else {
					canUseOnObject = 0;
				}
			}

			commandVar3[0] = si;
			commandVar1 = 1;

			strcat(commandBuffer, " ");
			strcat(commandBuffer, objectTable[commandVar3[0]].name);
			strcat(commandBuffer, " ");
			strcat(commandBuffer, commandPrepositionOn);
		}
	} else {
		if (playerCommand == 2) {
			getMouseData(mouseUpdateStatus, &dummyU16, &x, &y);
			processInventory(x, y + 8);
			playerCommand = -1;
			commandVar1 = 0;
			strcpy(commandBuffer, "");
		}
	}

	if (gameType == Cine::GID_OS) {
		if (playerCommand != -1 && canUseOnObject != 0)	{ // call use on sub object
			int16 si;

			getMouseData(mouseUpdateStatus, &dummyU16, &x, &y);

			si = selectSubObject2(x, y + 8, -subObjectUseTable[playerCommand]);

			if (si) {
				if (si >= 8000) {
					si -= 8000;
				}

				commandVar3[commandVar1] = si;

				commandVar1++;

				// TODO: add command message draw
			}

			isDrawCommandEnabled = 1;

			if (playerCommand != -1 && choiceResultTable[playerCommand] == commandVar1) {
				selectedObjStruct obj;
				obj.idx = commandVar3[0];
				obj.param = commandVar3[1];
				int16 di = getRelEntryForObject(playerCommand, commandVar1, &obj);

				if (di != -1) {
					runObjectScript(di);
				}
			}
		}
	}

	if (allowSystemMenu == 0) {
		isDrawCommandEnabled = 1;
	}
}

uint16 needMouseSave = 0;

uint16 menuVar4 = 0;
uint16 menuVar5 = 0;

int16 makeMenuChoice(const commandeType commandList[], uint16 height, uint16 X, uint16 Y,
    uint16 width) {
	byte color = 2;
	byte color2;
	int16 paramY;
	int16 currentX;
	int16 currentY;
	int16 i;
	uint16 button;
	int16 var_A;
	int16 di;
	uint16 j;
	int16 mouseX;
	int16 mouseY;
	int16 var_16;
	int16 var_14;
	int16 currentSelection;
	int16 oldSelection;
	int16 var_4;

	if (allowSystemMenu)
		return -1;

	paramY = (height * 9) + 10;

	if (X + width > 319) {
		X = 319 - width;
	}

	if (Y + paramY > 199) {
		Y = 199 - paramY;
	}

	color2 = defaultMenuBoxColor2;

	hideMouse();
	blitRawScreen(page1Raw);

	gfxDrawPlainBoxRaw(X, Y, X + width, Y + 4, color2, page1Raw);

	currentX = X + 4;
	currentY = Y + 4;

	for (i = 0; i < height; i++) {
		gfxDrawPlainBoxRaw(X, currentY, X + width, currentY + 9, color2, page1Raw);
		currentX = X + 4;

		for (j = 0; j < strlen(commandList[i]); j++) {
			byte currentChar = commandList[i][j];

			if (currentChar == ' ') {
				currentX += 5;
			} else {
				byte characterWidth = fontParamTable[currentChar].characterWidth;

				if (characterWidth) {
					byte characterIdx = fontParamTable[currentChar].characterIdx;
					drawSpriteRaw(textTable[characterIdx][0], textTable[characterIdx][1], 2, 8, page1Raw, currentX, currentY);
					currentX += characterWidth + 1;
				}
			}
		}

		currentY += 9;
	}

	gfxDrawPlainBoxRaw(X, currentY, X + width, currentY + 4, color2, page1Raw);	// bottom part

	gfxDrawLine(X + 1, Y + 1, X + width - 1, Y + 1, 0, page1Raw);	// top
	gfxDrawLine(X + 1, currentY + 3, X + width - 1, currentY + 3, 0, page1Raw);	// bottom
	gfxDrawLine(X + 1, Y + 1, X + 1, currentY + 3, 0, page1Raw);	// left
	gfxDrawLine(X + width - 1, Y + 1, X + width - 1, currentY + 3, 0, page1Raw);	// left

	gfxDrawLine(X, Y, X + width, Y, color, page1Raw);
	gfxDrawLine(X, currentY + 4, X + width, currentY + 4, color, page1Raw);
	gfxDrawLine(X, Y, X, currentY + 4, color, page1Raw);
	gfxDrawLine(X + width, Y, X + width, currentY + 4, color, page1Raw);

	blitRawScreen(page1Raw);

	do {
		manageEvents();
		getMouseData(mouseUpdateStatus, &button, &dummyU16, &dummyU16);
	} while (button);

	var_A = 0;

	currentSelection = 0;

	di = currentSelection * 9 + Y + 4;
	gfxDrawPlainBoxRaw(X + 2, di - 1, X + width - 2, di + 7, 0, page1Raw);	// draw black box behind selection
	currentX = X + 4;

	for (j = 0; j < strlen(commandList[currentSelection]); j++) {
		byte currentChar = commandList[currentSelection][j];

		if (currentChar == ' ') {
			currentX += 5;
		} else {
			byte characterWidth = fontParamTable[currentChar].characterWidth;

			if (characterWidth) {
				byte characterIdx = fontParamTable[currentChar].characterIdx;
				drawSpriteRaw(textTable[characterIdx][0], textTable[characterIdx][1], 2, 8, page1Raw, currentX, di);
				currentX += characterWidth + 1;
			}
		}
	}

	blitRawScreen(page1Raw);

	manageEvents();
	getMouseData(mouseUpdateStatus, &button, (uint16 *)&mouseX, (uint16 *)&mouseY);

	var_16 = mouseX;
	var_14 = mouseY;

	menuVar = 0;

	do {
		manageEvents();
		getMouseData(mouseUpdateStatus, &button, (uint16 *)&mouseX, (uint16 *)&mouseY);

		if (button) {
			var_A = 1;
		}

		oldSelection = currentSelection;

		if (needMouseSave) {
			for (j = 0; j < 3; j++) {
				mainLoopSub6();
			}

			if (menuVar4 && currentSelection > 0) {	// go up
				currentSelection--;
			}

			if (menuVar5) {	// go down
				if (height - 1 > currentSelection) {
					currentSelection++;
				}
			}
		} else {
			if (mouseX > X && mouseX < X + width && mouseY > Y && mouseY < Y + height * 9) {
				currentSelection = (mouseY - (Y + 4)) / 9;

				if (currentSelection < 0)
					currentSelection = 0;

				if (currentSelection >= height)
					currentSelection = height - 1;
			}
		}

		if (currentSelection != oldSelection) {	// old != new
			if (needMouseSave) {
				hideMouse();
			}

			di = oldSelection * 9 + Y + 4;

			gfxDrawPlainBoxRaw(X + 2, di - 1, X + width - 2, di + 7, color2, page1Raw);	// restore color

			currentX = X + 4;

			for (j = 0; j < strlen(commandList[oldSelection]); j++) {
				byte currentChar = commandList[oldSelection][j];

				if (currentChar == ' ') {
					currentX += 5;
				} else {
					byte characterWidth = fontParamTable[currentChar].characterWidth;

					if (characterWidth) {
						byte characterIdx = fontParamTable[currentChar].characterIdx;
						drawSpriteRaw(textTable[characterIdx][0], textTable[characterIdx][1], 2, 8, page1Raw, currentX, di);
						currentX += characterWidth + 1;
					}
				}
			}

			di = currentSelection * 9 + Y + 4;

			gfxDrawPlainBoxRaw(X + 2, di - 1, X + width - 2, di + 7, 0, page1Raw);	// black new

			currentX = X + 4;

			for (j = 0; j < strlen(commandList[currentSelection]);
			    j++) {
				byte currentChar = commandList[currentSelection][j];

				if (currentChar == ' ') {
					currentX += 5;
				} else {
					byte characterWidth = fontParamTable[currentChar].characterWidth;

					if (characterWidth) {
						byte characterIdx = fontParamTable[currentChar].characterIdx;
						drawSpriteRaw(textTable[characterIdx][0], textTable[characterIdx][1], 2, 8, page1Raw, currentX, di);
						currentX += characterWidth + 1;
					}
				}
			}

			blitRawScreen(page1Raw);

			if (needMouseSave) {
				gfxFuncGen2();
			}
		}

	} while (!var_A);

	assert(!needMouseSave);

	var_4 = button;

	menuVar = 0;

	do {
		manageEvents();
		getMouseData(mouseUpdateStatus, &button, &dummyU16, &dummyU16);
	} while (button);

	if (var_4 == 2)	{	// recheck
		return -1;
	}

	return currentSelection;
}

int16 makeMenuChoice2(const commandeType commandList[], uint16 height, uint16 X, uint16 Y,
    uint16 width) {
	byte color = 2;
	byte color2;
	int16 paramY;
	int16 currentX;
	int16 currentY;
	int16 i;
	uint16 button;
	int16 var_A;
	int16 di;
	uint16 j;
	int16 mouseX;
	int16 mouseY;
	int16 var_16;
	int16 var_14;
	int16 currentSelection;
	int16 oldSelection;
	int16 var_4;

	if (allowSystemMenu)
		return -1;

	paramY = (height * 9) + 10;

	if (X + width > 319) {
		X = 319 - width;
	}

	if (Y + paramY > 199) {
		Y = 199 - paramY;
	}

	color2 = defaultMenuBoxColor2;

	hideMouse();
	blitRawScreen(page1Raw);

	gfxDrawPlainBoxRaw(X, Y, X + width, Y + 4, color2, page1Raw);

	currentX = X + 4;
	currentY = Y + 4;

	for (i = 0; i < height; i++) {
		gfxDrawPlainBoxRaw(X, currentY, X + width, currentY + 9, color2, page1Raw);
		currentX = X + 4;

		for (j = 0; j < strlen(commandList[i]); j++) {
			byte currentChar = commandList[i][j];

			if (currentChar == ' ') {
				currentX += 5;
			} else {
				byte characterWidth = fontParamTable[currentChar].characterWidth;

				if (characterWidth) {
					byte characterIdx = fontParamTable[currentChar].characterIdx;
					drawSpriteRaw(textTable[characterIdx][0], textTable[characterIdx][1], 2, 8, page1Raw, currentX, currentY);
					currentX += characterWidth + 1;
				}
			}
		}

		currentY += 9;
	}

	gfxDrawPlainBoxRaw(X, currentY, X + width, currentY + 4, color2, page1Raw);	// bottom part

	gfxDrawLine(X + 1, Y + 1, X + width - 1, Y + 1, 0, page1Raw);	// top
	gfxDrawLine(X + 1, currentY + 3, X + width - 1, currentY + 3, 0, page1Raw);	// bottom
	gfxDrawLine(X + 1, Y + 1, X + 1, currentY + 3, 0, page1Raw);	// left
	gfxDrawLine(X + width - 1, Y + 1, X + width - 1, currentY + 3, 0, page1Raw);	// left

	gfxDrawLine(X, Y, X + width, Y, color, page1Raw);
	gfxDrawLine(X, currentY + 4, X + width, currentY + 4, color, page1Raw);
	gfxDrawLine(X, Y, X, currentY + 4, color, page1Raw);
	gfxDrawLine(X + width, Y, X + width, currentY + 4, color, page1Raw);

	blitRawScreen(page1Raw);

	do {
		manageEvents();
		getMouseData(mouseUpdateStatus, &button, &dummyU16, &dummyU16);
	} while (button);

	var_A = 0;

	currentSelection = 0;

	di = currentSelection * 9 + Y + 4;
	gfxDrawPlainBoxRaw(X + 2, di - 1, X + width - 2, di + 7, 0, page1Raw);	// draw black box behind selection
	currentX = X + 4;

	for (j = 0; j < strlen(commandList[currentSelection]); j++) {
		byte currentChar = commandList[currentSelection][j];

		if (currentChar == ' ') {
			currentX += 5;
		} else {
			byte characterWidth = fontParamTable[currentChar].characterWidth;

			if (characterWidth) {
				byte characterIdx = fontParamTable[currentChar].characterIdx;
				drawSpriteRaw(textTable[characterIdx][0], textTable[characterIdx][1], 2, 8, page1Raw, currentX, di);
				currentX += characterWidth + 1;
			}
		}
	}

	blitRawScreen(page1Raw);

	manageEvents();
	getMouseData(mouseUpdateStatus, &button, (uint16 *)&mouseX, (uint16 *)&mouseY);

	var_16 = mouseX;
	var_14 = mouseY;

	menuVar = 0;

	do {
		manageEvents();
		getMouseData(mouseUpdateStatus, &button, (uint16 *)&mouseX, (uint16 *)&mouseY);

		if (button) {
			var_A = 1;
		}

		oldSelection = currentSelection;

		if (needMouseSave) {
			for (j = 0; j < 3; j++) {
				mainLoopSub6();
			}

			if (menuVar4 && currentSelection > 0) {	// go up
				currentSelection--;
			}

			if (menuVar5) {	// go down
				if (height - 1 > currentSelection) {
					currentSelection++;
				}
			}
		} else {
			if (mouseX > X && mouseX < X + width && mouseY > Y && mouseY < Y + height * 9) {
				currentSelection = (mouseY - (Y + 4)) / 9;

				if (currentSelection < 0)
					currentSelection = 0;

				if (currentSelection >= height)
					currentSelection = height - 1;
			}
		}

		if (currentSelection != oldSelection) {	// old != new
			if (needMouseSave) {
				hideMouse();
			}

			di = oldSelection * 9 + Y + 4;

			gfxDrawPlainBoxRaw(X + 2, di - 1, X + width - 2, di + 7, color2, page1Raw);	// restore color

			currentX = X + 4;

			for (j = 0; j < strlen(commandList[oldSelection]); j++) {
				byte currentChar = commandList[oldSelection][j];

				if (currentChar == ' ') {
					currentX += 5;
				} else {
					byte characterWidth = fontParamTable[currentChar].characterWidth;

					if (characterWidth) {
						byte characterIdx = fontParamTable[currentChar].characterIdx;
						drawSpriteRaw(textTable[characterIdx][0], textTable[characterIdx][1], 2, 8, page1Raw, currentX, di);
						currentX += characterWidth + 1;
					}
				}
			}

			di = currentSelection * 9 + Y + 4;

			gfxDrawPlainBoxRaw(X + 2, di - 1, X + width - 2, di + 7, 0, page1Raw);	// black new

			currentX = X + 4;

			for (j = 0; j < strlen(commandList[currentSelection]);
			    j++) {
				byte currentChar = commandList[currentSelection][j];

				if (currentChar == ' ') {
					currentX += 5;
				} else {
					byte characterWidth = fontParamTable[currentChar].characterWidth;

					if (characterWidth) {
						byte characterIdx = fontParamTable[currentChar].characterIdx;
						drawSpriteRaw(textTable[characterIdx][0], textTable[characterIdx][1], 2, 8, page1Raw, currentX, di);
						currentX += characterWidth + 1;
					}
				}
			}

			blitRawScreen(page1Raw);

			if (needMouseSave) {
				gfxFuncGen2();
			}
		}

	} while (!var_A);

	assert(!needMouseSave);

	var_4 = button;

	menuVar = 0;

	do {
		manageEvents();
		getMouseData(mouseUpdateStatus, &button, &dummyU16, &dummyU16);
	} while (button);

	if (var_4 == 2)	{	// recheck
		return currentSelection + 8000;
	}

	return currentSelection;
}

void drawMenuBox(char *command, int16 x, int16 y) {
	byte j;
	byte lColor = 2;

	hideMouse();

	gfxDrawPlainBoxRaw(x, y, x + 300, y + 10, 0, page2Raw);

	gfxDrawLine(x - 1, y - 1, x + 301, y - 1, lColor, page2Raw);	// top
	gfxDrawLine(x - 1, y + 11, x + 301, y + 11, lColor, page2Raw);	// bottom
	gfxDrawLine(x - 1, y - 1, x - 1, y + 11, lColor, page2Raw);	// left
	gfxDrawLine(x + 301, y - 1, x + 301, y + 11, lColor, page2Raw);	// right

	x += 2;
	y += 2;

	for (j = 0; j < strlen(command); j++) {
		byte currentChar = command[j];

		if (currentChar == ' ') {
			x += 5;
		} else {
			byte characterWidth = fontParamTable[currentChar].characterWidth;

			if (characterWidth) {
				byte characterIdx = fontParamTable[currentChar].characterIdx;
				drawSpriteRaw(textTable[characterIdx][0], textTable[characterIdx][1], 2, 8, page2Raw, x, y);
				x += characterWidth + 1;
			}
		}
	}

	gfxFuncGen2();
}

void makeActionMenu(void) {
	uint16 mouseButton;
	uint16 mouseX;
	uint16 mouseY;

	getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY);

	if (gameType == Cine::GID_OS) {
		playerCommand = makeMenuChoice2(defaultActionCommand, 6, mouseX, mouseY, 70);

		if (playerCommand >= 8000) {
			playerCommand -= 8000;
			canUseOnObject = 1;
		}
	} else {
		playerCommand =  makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70);
	}
}

uint16 executePlayerInput(void) {
	uint16 var_5E;
	uint16 var_2;
	uint16 mouseButton;
	uint16 mouseX;
	uint16 mouseY;

	canUseOnObject = 0;

	if (isInPause) {
		drawString("PAUSE", 0);
		waitPlayerInput();
		isInPause = 0;
	}

	if (allowPlayerInput) {
		uint16 currentEntry = 0;
		uint16 di = 0;

		if (isDrawCommandEnabled) {
			drawMenuBox(commandBuffer, 10, defaultMenuBoxColor);
			isDrawCommandEnabled = 0;
		}

		getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY);

		while (mouseButton && currentEntry < 200) {
			if (mouseButton & 1) {
				di |= 1;
			}

			if (mouseButton & 2) {
				di |= 2;
			}

			getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY);

			currentEntry++;
		}

		if (di) {
			mouseButton = di;
		}

		if (playerCommand != -1) {
			if (mouseButton & 1) {
				if (mouseButton & 2) {
					makeSystemMenu();
				} else {
					int16 si;
					do {
						manageEvents();
						getMouseData(mouseUpdateStatus, &mouseButton, &dummyU16, &dummyU16);
					} while (mouseButton);

					si = getObjectUnderCursor(mouseX,
					    mouseY);

					if (si != -1) {
						commandVar3[commandVar1] = si;
						commandVar1++;

						strcat(commandBuffer, " ");
						strcat(commandBuffer, objectTable[si].name);

						isDrawCommandEnabled = 1;

						if (choiceResultTable[playerCommand] == commandVar1) {
							int16 relEntry;

							drawMenuBox(commandBuffer, 10, defaultMenuBoxColor);
							selectedObjStruct obj;
							obj.idx = commandVar3[0];
							obj.param = commandVar3[1];

							relEntry = getRelEntryForObject(playerCommand, commandVar1, &obj);

							if (relEntry != -1) {
								runObjectScript(relEntry);
							} else {
								addPlayerCommandMessage(playerCommand);
							}

							playerCommand = -1;

							commandVar1 = 0;
							strcpy(commandBuffer, "");
						}
					} else {
						globalVars[VAR_MOUSE_X_POS] = mouseX;
						globalVars[VAR_MOUSE_Y_POS] = mouseY;
					}
				}
			} else {
				if (mouseButton & 2) {
					if (mouseButton & 1) {
						makeSystemMenu();
					}

					makeActionMenu();
					makeCommandLine();
				} else {
					int16 objIdx;

					objIdx = getObjectUnderCursor(mouseX, mouseY);

					if (commandVar2 != objIdx) {
						if (objIdx != -1) {
							char command[256];

							strcpy(command, commandBuffer);
							strcat(command, " ");
							strcat(command, objectTable[objIdx].name);

							drawMenuBox(command, 10, defaultMenuBoxColor);
						} else {
							isDrawCommandEnabled = 1;
						}
					}

					commandVar2 = objIdx;
				}
			}
		} else {
			if (mouseButton & 2) {
				if (!(mouseButton & 1)) {
					if (gameType == Cine::GID_OS) {
						playerCommand = makeMenuChoice2(defaultActionCommand, 6, mouseX, mouseY, 70);

						if (playerCommand >= 8000) {
							playerCommand -= 8000;
							canUseOnObject = 1;
						}
					} else {
						playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70);
					}

					makeCommandLine();
				} else {
					makeSystemMenu();
				}
			} else {
				if (mouseButton & 1) {
					if (!(mouseButton & 2)) {
						int16 objIdx;
						int16 relEntry;

						globalVars[VAR_MOUSE_X_POS] = mouseX;
						if (!mouseX) {
							globalVars[VAR_MOUSE_X_POS]++;
						}

						globalVars[VAR_MOUSE_Y_POS] = mouseY;

						objIdx = getObjectUnderCursor(mouseX, mouseY);

						if (objIdx != -1) {
							currentSelectedObject.idx = objIdx;
							currentSelectedObject.param = -1;

							relEntry = getRelEntryForObject(6, 1, &currentSelectedObject);

							if (relEntry != -1) {
								runObjectScript(relEntry);
							}
						}
					} else {
						makeSystemMenu();
					}
				}
			}
		}
	} else {
		uint16 di = 0;
		getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY);

		while (mouseButton) {
			if (mouseButton & 1) {
				di |= 1;
			}

			if (mouseButton & 2) {
				di |= 2;
			}

			manageEvents();
			getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY);
		}

		if (di) {
			mouseButton = di;
		}

		if ((mouseButton & 1) && (mouseButton & 2)) {
			makeSystemMenu();
		}
	}

	var_2 = menuVar & 0x7F;
	var_5E = var_2;

	if (menuVar & 0x80) {
		var_5E = 0;
		var_2 = 0;
	}

	if (inputVar1 && allowPlayerInput) {	// use keyboard
		inputVar1 = 0;

		switch (globalVars[VAR_MOUSE_X_MODE]) {
		case 1:
			mouseX = objectTable[1].x + 12;
			break;
		case 2:
			mouseX = objectTable[1].x + 7;
			break;
		default:
			mouseX = globalVars[VAR_MOUSE_X_POS];
			break;
		}

		switch (globalVars[VAR_MOUSE_Y_MODE]) {
		case 1:
			mouseY = objectTable[1].y + 34;
			break;
		case 2:
			mouseY = objectTable[1].y + 28;
			break;
		default:
			mouseY = globalVars[VAR_MOUSE_Y_POS];
			break;
		}

		if (var_5E == bgVar0) {
			var_5E = 0;

			globalVars[VAR_MOUSE_X_POS] = mouseX;
			globalVars[VAR_MOUSE_Y_POS] = mouseY;
		} else {
			if (inputVar2) {
				if (inputVar2 == 2) {
					globalVars[VAR_MOUSE_X_POS] = 1;
				} else {
					globalVars[VAR_MOUSE_X_POS] = 320;
				}
			} else {
				globalVars[VAR_MOUSE_X_POS] = mouseX;
			}

			if (inputVar3) {
				if (inputVar3 == 2) {
					globalVars[VAR_MOUSE_Y_POS] = 1;
				} else {
					globalVars[VAR_MOUSE_Y_POS] = 200;
				}
			} else {
				globalVars[VAR_MOUSE_Y_POS] = mouseY;
			}
		}

		bgVar0 = var_5E;
	} else {		// don't use keyboard for move -> shortcuts to commands
		getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY);

		switch (var_2 - 59) {
		case 0:
			if (allowPlayerInput) {
				playerCommand = 0;
				makeCommandLine();
			}
			break;
		case 1:
			if (allowPlayerInput) {
				playerCommand = 1;
				makeCommandLine();
			}
			break;
		case 2:
			if (allowPlayerInput) {
				playerCommand = 2;
				makeCommandLine();
			}
			break;
		case 3:
			if (allowPlayerInput) {
				playerCommand = 3;
				makeCommandLine();
			}
			break;
		case 4:
			if (allowPlayerInput) {
				playerCommand = 4;
				makeCommandLine();
			}
			break;
		case 5:
			if (allowPlayerInput) {
				playerCommand = 5;
				makeCommandLine();
			}
			break;
		case 6:
		case 7:
		case 8:
		case 23:
			break;
		case 9:
		case 24:
			makeSystemMenu();
			break;
		default:
			//  printf("Unhandled case %d in last part of executePLayerInput\n",var2-59);
			break;
		}
	}

	return var_5E;
}

void drawSprite(overlayHeadElement *currentOverlay, byte *spritePtr,
				byte *maskPtr, uint16 width, uint16 height, byte *page, int16 x, int16 y) {
#if 0
	byte *ptr = NULL;
	byte i = 0;
	uint16 si = 0;
	overlayHeadElement *pCurrentOverlay = currentOverlay;

	while (pCurrentOverlay) { // unfinished, probably for mask handling..
		if (pCurrentOverlay->type == 5) {
			int16 maskX;
			int16 maskY;
			int16 maskWidth;
			int16 maskHeight;
			uint16 maskSpriteIdx;
	 
			if (!si) {
				ptr = (byte *)malloc(width * height);
				si = 1;
			}
	 
			maskX = objectTable[pCurrentOverlay->objIdx].x;
			maskY = objectTable[pCurrentOverlay->objIdx].y;
	 
			maskSpriteIdx = objectTable[pCurrentOverlay->objIdx].frame;
	 
			maskWidth = animDataTable[maskSpriteIdx].width / 2;
			maskHeight = animDataTable[maskSpriteIdx].height;
	 
			gfxSpriteFunc2(spritePtr, width, height, animDataTable[maskSpriteIdx].ptr1, maskWidth, maskHeight, ptr, maskX - x,maskY - y, i++);
		}
	 
		pCurrentOverlay = pCurrentOverlay->next;
	} 
	 
	if (si) {
		gfxSpriteFunc1(ptr, width, height, page, x, y);
		free(ptr);
	} else
#endif

	if (gameType == Cine::GID_OS) {
		drawSpriteRaw2(spritePtr, objectTable[currentOverlay->objIdx].part, width, height, page, x, y);
	} else {
		drawSpriteRaw(spritePtr, maskPtr, width, height, page, x, y);
	}

}

int16 additionalBgVScroll = 0;

void backupOverlayPage(void) {
	byte *bgPage;
	byte *scrollBg;

	bgPage = additionalBgTable[currentAdditionalBgIdx];

	if (bgPage) {
		if (!additionalBgVScroll) {
			memcpy(page1Raw, bgPage, 320 * 200);
		} else {
			int16 i;

			scrollBg = additionalBgTable[currentAdditionalBgIdx2];

			for (i = additionalBgVScroll;
			    i < 200 + additionalBgVScroll; i++) {
				if (i > 200) {
					memcpy(page1Raw + (i - additionalBgVScroll) * 320, scrollBg + (i - 200) * 320, 320);
				} else {
					memcpy(page1Raw + (i - additionalBgVScroll) * 320, bgPage + (i) * 320, 320);
				}
			}
		}
	}
}

uint16 computeMessageLength(const byte *ptr, uint16 width, uint16 *numWords, uint16 *messageWidth, uint16 *lineResult) {
	const byte *localPtr = ptr;

	uint16 var_2 = 0;
	uint16 localLineResult = 0;
	uint16 var_6 = 0;
	uint16 var_8 = 0;
	uint16 localMessageWidth = 0;
	uint16 var_16 = 0;
	uint16 finished = 0;
	uint16 si = 0;
	uint16 di = 0;

	while (!finished) {
		byte character = *(localPtr++);

		if (character == ' ') {
			var_8 = var_16;
			var_6 = localMessageWidth;
			localLineResult = si;
			var_2 = di;

			if (si + 5 < width) {
				var_16++;
				si += 5;
			} else {
				finished = 1;
			}
		} else if (character == 0x7C || character == 0) {
			finished = 1;
			si = 0;
		} else {
			if (fontParamTable[character].characterWidth) {
				uint16 var_C = fontParamTable[character].characterWidth + 1;

				if (si + var_C < width) {
					si += var_C;
					localMessageWidth += var_C;
				} else {
					finished = 1;

					if (localLineResult) {
						var_16 = var_8;
						localMessageWidth = var_6;
						si = localLineResult;
						di = var_2;
					}
				}
			}
		}

		di++;
	}

	*numWords = var_16;
	*messageWidth = localMessageWidth;
	*lineResult = si;

	return di;
}

void drawDialogueMessage(byte msgIdx, int16 x, int16 y, int16 width, int16 color) {
	byte color2 = 2;
	byte endOfMessageReached = 0;
	int16 localX;
	int16 localY;
	int16 localWidth;

	char *messagePtr = (char *)messageTable[msgIdx].ptr;

	if (!messagePtr) {
		freeOverlay(msgIdx, 2);
		return;
	}

	var20 += strlen(messagePtr);

	gfxDrawPlainBoxRaw(x, y, x + width, y + 4, color, page1Raw);

	localX = x + 4;
	localY = y + 4;
	localWidth = width - 8;

	do {
		uint16 messageLength = 0;
		uint16 numWords;
		uint16 messageWidth;
		uint16 lineResult;
		char *endOfMessagePtr;
		uint16 fullLineWidth;
		uint16 interWordSize;
		uint16 interWordSizeRemain;
		byte currentChar;
		byte characterWidth;

		while (messagePtr[messageLength] == ' ') {
			messageLength++;
		}

		messagePtr += messageLength;

		messageLength = computeMessageLength((byte *) messagePtr, localWidth, &numWords, &messageWidth, &lineResult);

		endOfMessagePtr = messagePtr + messageLength;

		if (lineResult) {
			fullLineWidth = localWidth - messageWidth;

			if (numWords) {
				interWordSize = fullLineWidth / numWords;
				interWordSizeRemain = fullLineWidth % numWords;
			} else {
				interWordSize = 5;
				interWordSizeRemain = 0;
			}
		} else {
			interWordSize = 5;
			interWordSizeRemain = 0;
		}

		gfxDrawPlainBoxRaw(x, localY, x + width, localY + 9, color, page1Raw);

		do {
			currentChar = *(messagePtr++);

			if (currentChar == 0) {
				endOfMessageReached = 1;
			} else if (currentChar == ' ') {
				localX += interWordSizeRemain + interWordSize;

				if (interWordSizeRemain)
					interWordSizeRemain = 0;
			} else {
				characterWidth = fontParamTable[currentChar].characterWidth;

				if (characterWidth) {
					byte characterIdx = fontParamTable[currentChar].characterIdx;
					drawSpriteRaw(textTable[characterIdx][0], textTable[characterIdx][1], 2, 8, page1Raw, localX, localY);
					localX += characterWidth + 1;
				}
			}
		} while ((messagePtr < endOfMessagePtr) && !endOfMessageReached);

		localX = x + 4;
		localY += 9;
	} while (!endOfMessageReached);

	gfxDrawPlainBoxRaw(x, localY, x + width, localY + 4, color, page1Raw);

	gfxDrawLine(x + 1, y + 1, x + width - 1, y + 1, 0, page1Raw);	// top
	gfxDrawLine(x + 1, localY + 3, x + width - 1, localY + 3, 0, page1Raw);	// bottom
	gfxDrawLine(x + 1, y + 1, x + 1, localY + 3, 0, page1Raw);	// left
	gfxDrawLine(x + width - 1, y + 1, x + width - 1, localY + 3, 0, page1Raw);	// right

	gfxDrawLine(x, y, x + width, y, color2, page1Raw);
	gfxDrawLine(x, localY + 4, x + width, localY + 4, color2, page1Raw);
	gfxDrawLine(x, y, x, localY + 4, color2, page1Raw);
	gfxDrawLine(x + width, y, x + width, localY + 4, color2, page1Raw);

	freeOverlay(msgIdx, 2);
}

void drawFailureMessage(byte cmd) {
	byte color2 = 2;
	byte endOfMessageReached = 0;
	int16 localX;
	int16 localY;
	int16 localWidth;

	byte msgIdx = cmd * 4 + rand() % 4;

	const char *messagePtr = failureMessages[msgIdx];
	int len = strlen(messagePtr);

	var20 += len;

	int16 width = 6 * len + 20;

	if (width > 300)
		width = 300;
	
	int16 x = (320 - width) / 2;
	int16 y = 80;
	int16 color = 4;

	gfxDrawPlainBoxRaw(x, y, x + width, y + 4, color, page1Raw);

	localX = x + 4;
	localY = y + 4;
	localWidth = width - 8;

	do {
		uint16 messageLength = 0;
		uint16 numWords;
		uint16 messageWidth;
		uint16 lineResult;
		const char *endOfMessagePtr;
		uint16 fullLineWidth;
		uint16 interWordSize;
		uint16 interWordSizeRemain;
		byte currentChar;
		byte characterWidth;

		while (messagePtr[messageLength] == ' ') {
			messageLength++;
		}

		messagePtr += messageLength;

		messageLength = computeMessageLength((const byte *) messagePtr, localWidth, &numWords, &messageWidth, &lineResult);

		endOfMessagePtr = messagePtr + messageLength;

		if (lineResult) {
			fullLineWidth = localWidth - messageWidth;

			if (numWords) {
				interWordSize = fullLineWidth / numWords;
				interWordSizeRemain = fullLineWidth % numWords;
			} else {
				interWordSize = 5;
				interWordSizeRemain = 0;
			}
		} else {
			interWordSize = 5;
			interWordSizeRemain = 0;
		}

		gfxDrawPlainBoxRaw(x, localY, x + width, localY + 9, color, page1Raw);

		do {
			currentChar = *(messagePtr++);

			if (currentChar == 0) {
				endOfMessageReached = 1;
			} else if (currentChar == ' ') {
				localX += interWordSizeRemain + interWordSize;

				if (interWordSizeRemain)
					interWordSizeRemain = 0;
			} else {
				characterWidth = fontParamTable[currentChar].characterWidth;

				if (characterWidth) {
					byte characterIdx = fontParamTable[currentChar].characterIdx;
					drawSpriteRaw(textTable[characterIdx][0], textTable[characterIdx][1], 2, 8, page1Raw, localX, localY);
					localX += characterWidth + 1;
				}
			}
		} while ((messagePtr < endOfMessagePtr) && !endOfMessageReached);

		localX = x + 4;
		localY += 9;
	} while (!endOfMessageReached);

	gfxDrawPlainBoxRaw(x, localY, x + width, localY + 4, color, page1Raw);

	gfxDrawLine(x + 1, y + 1, x + width - 1, y + 1, 0, page1Raw);	// top
	gfxDrawLine(x + 1, localY + 3, x + width - 1, localY + 3, 0, page1Raw);	// bottom
	gfxDrawLine(x + 1, y + 1, x + 1, localY + 3, 0, page1Raw);	// left
	gfxDrawLine(x + width - 1, y + 1, x + width - 1, localY + 3, 0, page1Raw);	// right

	gfxDrawLine(x, y, x + width, y, color2, page1Raw);
	gfxDrawLine(x, localY + 4, x + width, localY + 4, color2, page1Raw);
	gfxDrawLine(x, y, x, localY + 4, color2, page1Raw);
	gfxDrawLine(x + width, y, x + width, localY + 4, color2, page1Raw);

	freeOverlay(cmd, 3);
}

void drawOverlays(void) {
	overlayHeadElement *currentOverlay;

	backupOverlayPage();

	var20 = 0;

	currentOverlay = &overlayHead;

	currentOverlay = currentOverlay->next;

	while (currentOverlay) {
		switch (currentOverlay->type) {
		case 0:	// sprite
			{
				objectStruct *objPtr;
				int16 x;
				int16 y;

				assert(currentOverlay->objIdx <= NUM_MAX_OBJECT);

				objPtr = &objectTable[currentOverlay->objIdx];

				x = objPtr->x;
				y = objPtr->y;

				if (objPtr->frame >= 0) {
					if (gameType == Cine::GID_OS) {
						uint16 partVar1;
						uint16 partVar2;
						AnimData *pPart;
						pPart = &animDataTable[objPtr->frame];

						partVar1 = pPart->var1;
						partVar2 = pPart->height;

						if (pPart->ptr1) {
							drawSprite(currentOverlay, pPart->ptr1, pPart->ptr1, partVar1, partVar2, page1Raw, x, y);
						}
					} else {
						uint16 partVar1;
						uint16 partVar2;
						AnimData *pPart;
						int16 part = objPtr->part;

						assert(part >= 0 && part <= NUM_MAX_ANIMDATA);

						pPart = &animDataTable[objPtr->frame];

						partVar1 = pPart->var1;
						partVar2 = pPart->height;

						if (pPart->ptr1) {
							drawSprite(currentOverlay, pPart->ptr1, pPart->ptr2, partVar1, partVar2, page1Raw, x, y);
						}
					}
				}
				break;
			}
		case 2:	// text
			{
				byte messageIdx;
				int16 x;
				int16 y;
				uint16 partVar1;
				uint16 partVar2;

				// gfxWaitVSync();
				// hideMouse();

				messageIdx = currentOverlay->objIdx;
				x = currentOverlay->x;
				y = currentOverlay->y;
				partVar1 = currentOverlay->width;
				partVar2 = currentOverlay->color;

				blitRawScreen(page1Raw);

				drawDialogueMessage(messageIdx, x, y, partVar1, partVar2);

				//blitScreen(page0, NULL);

				gfxFuncGen2();

				waitForPlayerClick = 1;

				break;
			}
		case 3:
			{
				// gfxWaitSync()
				// hideMouse();

				blitRawScreen(page1Raw);

				drawFailureMessage(currentOverlay->objIdx);

				//blitScreen(page0, NULL);

				gfxFuncGen2();

				waitForPlayerClick = 1;

				break;
			}
		case 4:
			{
				objectStruct *objPtr;
				int16 x;
				int16 y;

				assert(currentOverlay->objIdx <= NUM_MAX_OBJECT);

				objPtr = &objectTable[currentOverlay->objIdx];

				x = objPtr->x;
				y = objPtr->y;

				if (objPtr->frame >= 0) {
					uint16 partVar1;
					uint16 partVar2;
					AnimData *pPart;
					int16 part = objPtr->part;

					assert(part >= 0 && part <= NUM_MAX_ANIMDATA);

					pPart = &animDataTable[objPtr->frame];

					partVar1 = pPart->width / 2;
					partVar2 = pPart->height;

					if (pPart->ptr1) {
						gfxFillSprite(pPart->ptr1, partVar1, partVar2, page1Raw, x, y);
					}
				}
				break;
			}
		case 20:
			{
				objectStruct *objPtr;
				int16 x;
				int16 y;

				var5 = currentOverlay->x;

				assert(currentOverlay->objIdx <= NUM_MAX_OBJECT);

				objPtr = &objectTable[currentOverlay->objIdx];

				x = objPtr->x;
				y = objPtr->y;

				if (objPtr->frame >= 0) {
					if (var5 <= 8) {
						if (additionalBgTable[var5]) {
							if (animDataTable[objPtr->frame].bpp == 1) {
								int16 x2;
								int16 y2;

								x2 = animDataTable[objPtr->frame].width / 2;
								y2 = animDataTable[objPtr->frame].height;

								if (animDataTable[objPtr->frame].ptr1) {
									// drawSpriteRaw(animDataTable[objPtr->frame].ptr1, animDataTable[objPtr->frame].ptr1, x2, y2,
									//				additionalBgTable[currentAdditionalBgIdx], x, y);
								}
							}
						}
					}
				}
				break;
			}
		}

		currentOverlay = currentOverlay->next;
	}
}

void flip(void) {
	blitRawScreen(page1Raw);
}

uint16 processKeyboard(uint16 param) {
	return 0;
}

void mainLoopSub6(void) {
}

void checkForPendingDataLoad(void) {
	if (newPrcName[0] != 0) {
		freePrcLinkedList();
		resetglobalScriptsHead();

		loadPrc(newPrcName);

		strcpy(currentPrcName, newPrcName);
		strcpy(newPrcName, "");

		addScriptToList0(1);
	}

	if (newRelName[0] != 0) {
		releaseObjectScripts();
		resetObjectScriptHead();

		loadRel(newRelName);

		strcpy(currentRelName, newRelName);
		strcpy(newRelName, "");
	}

	if (newObjectName[0] != 0) {
		unloadAllMasks();
		resetMessageHead();

		loadObject(newObjectName);

		strcpy(currentObjectName, newObjectName);
		strcpy(newObjectName, "");
	}

	if (newMsgName[0] != 0) {
		loadMsg(newMsgName);

		strcpy(currentMsgName, newMsgName);
		strcpy(newMsgName, "");
	}
}

uint16 exitEngine;

void hideMouse(void) {
}

void closeEngine7(void) {
}

void removeExtention(char *dest, const char *source) {
	byte *ptr;

	strcpy(dest, source);

	ptr = (byte *) strchr(dest, '.');

	if (ptr) {
		*ptr = 0;
	}
}

uint16 var22;

uint16 defaultMenuBoxColor2;

uint16 zoneData[NUM_MAX_ZONE];

void addMessage(byte param1, int16 param2, int16 param3, int16 param4, int16 param5) {
	overlayHeadElement *currentHead = &overlayHead;
	overlayHeadElement *tempHead = currentHead;
	overlayHeadElement *newElement;

	currentHead = tempHead->next;

	while (currentHead) {
		tempHead = currentHead;
		currentHead = tempHead->next;
	}

	newElement = (overlayHeadElement *) malloc(sizeof(overlayHeadElement));

	newElement->next = tempHead->next;
	tempHead->next = newElement;

	newElement->objIdx = param1;
	newElement->type = 2;

	newElement->x = param2;
	newElement->y = param3;
	newElement->width = param4;
	newElement->color = param5;

	if (!currentHead)
		currentHead = &overlayHead;

	newElement->previous = currentHead->previous;

	currentHead->previous = newElement;
}

SeqListElement seqList;

void addSeqListElement(int16 param0, int16 param1, int16 param2, int16 param3, int16 param4, int16 param5, int16 param6, int16 param7, int16 param8) {
	SeqListElement *currentHead = &seqList;
	SeqListElement *tempHead = currentHead;
	SeqListElement *newElement;

	currentHead = tempHead->next;

	while (currentHead && currentHead->varE < param7) {
		tempHead = currentHead;
		currentHead = tempHead->next;
	}

	newElement = (SeqListElement *)malloc(sizeof(SeqListElement));

	newElement->next = tempHead->next;
	tempHead->next = newElement;

	newElement->var6 = param0;
	newElement->var4 = param1;
	newElement->var8 = param2;
	newElement->varA = param3;
	newElement->varC = param4;
	newElement->var14 = 0;
	newElement->var16 = 0;
	newElement->var18 = param5;
	newElement->var1A = param6;
	newElement->varE = param7;
	newElement->var10 = param8;
	newElement->var12 = param8;
	newElement->var1C = 0;
	newElement->var1E = 0;
}

void resetSeqList() {
	seqList.next = NULL;
}

void computeMove1(SeqListElement *element, int16 x, int16 y, int16 param1,
    int16 param2, int16 x2, int16 y2) {
	element->var16 = 0;
	element->var14 = 0;

	if (y2) {
		if (y - param2 > y2) {
			element->var16 = 2;
		}

		if (y + param2 < y2) {
			element->var16 = 1;
		}
	}

	if (x2) {
		if (x - param1 > x2) {
			element->var14 = 2;
		}

		if (x + param1 < x2) {
			element->var14 = 1;
		}
	}
}

uint16 computeMove2(SeqListElement *element) {
	int16 returnVar = 0;

	if (element->var16 == 1) {
		returnVar = 4;
	} else if (element->var16 == 2) {
		returnVar = 3;
	}

	if (element->var14 == 1) {
		returnVar = 1;
	} else if (element->var14 == 2) {
		returnVar = 2;
	}

	return returnVar;
}

// sort all the gfx stuff...

void resetGfxEntityEntry(uint16 objIdx) {
#if 0
	overlayHeadElement* tempHead = &overlayHead;
	byte* var_16 = NULL;
	uint16 var_10 = 0;
	uint16 var_12 = 0;
	overlayHeadElement* currentHead = tempHead->next;
	byte* var_1A = NULL;
	overlayHeadElement* var1E = &overlayHead;

	while (currentHead) {
		tempHead2 = currentHead->next;

		if (currentHead->objIdx == objIdx && currentHead->type!=2 && currentHead->type!=3 && currentHead->type!=0x14) {
			tempHead->next = tempHead2;

			if (tempHead2) {
				tempHead2->previous = currentHead->previous;
			} else {
				seqVar0 = currentHead->previous;
			}

			var_22 = var_16;

			if (!var_22) {
				// todo: goto?
			}

			var_22->previous = currentHead;
		} else {
		}

		if (currentHead->type == 0x14) {
		} else {
		}

		if (currentHead->type == 0x2 || currentHead->type == 0x3) {
			si = 10000;
		} else {
			si = objectTable[currentHead->objIdx];
		}

		if (objectTable[objIdx]>si) {
			var1E = currentHead;
		}

		tempHead = tempHead->next;

	}

	if (var_1A) {
		currentHead = var_16;
		var_22 = var_1E->next;
		var_1E->next = currentHead;
		var_1A->next = var_22;

		if (var_1E != &gfxEntityHead) {
			currentHead->previous = var_1E;
		}

		if (!var_22) {
			seqVar0 = var_1A;
		} else {
			var_22->previous = var_1A;
		}

	}
#endif
}

uint16 addAni(uint16 param1, uint16 param2, byte *ptr, SeqListElement *element, uint16 param3, int16 *param4) {
	byte *currentPtr = ptr;
	byte *ptrData;
	byte *ptr2;
	int16 di;

	assert(ptr);
	assert(element);
	assert(param4);

	dummyU16 = READ_BE_UINT16((currentPtr + param1 * 2) + 8);

	ptrData = ptr + dummyU16;

	assert(*ptrData);

	di = (objectTable[param2].costume + 1) % (*ptrData);
	ptr2 = (ptrData + (di * 8)) + 1;

	if ((checkCollision(param2, ptr2[0], ptr2[1], ptr2[2], ptr[0]) & 1)) {
		return 0;
	}

	objectTable[param2].x += (int8)ptr2[4];
	objectTable[param2].y += (int8)ptr2[5];
	objectTable[param2].mask += (int8)ptr2[6];

	if (objectTable[param2].frame) {
		resetGfxEntityEntry(param2);
	}

	objectTable[param2].frame = ptr2[7] + element->var8;

	if (param3 || !element->var14) {
		objectTable[param2].costume = di;
	} else {
		*param4 = di;
	}

	return 1;
}

void processSeqListElement(SeqListElement *element) {
	int16 x;
	int16 y;
	byte *ptr1;
	int16 var_10;
	int16 var_4;
	int16 var_2;

	if (element->var12 < element->var10) {
		element->var12++;
		return;
	}

	element->var12 = 0;

	x = objectTable[element->var6].x;
	y = objectTable[element->var6].y;
	ptr1 = animDataTable[element->varA].ptr1;

	if (ptr1) {
		uint16 param1;
		uint16 param2;

		param1 = ptr1[1];
		param2 = ptr1[2];

		assert(element->varC == 255);

		if (globalVars[VAR_MOUSE_X_POS] || globalVars[VAR_MOUSE_Y_POS]) {
			computeMove1(element, ptr1[4] + x, ptr1[5] + y, param1, param2, globalVars[VAR_MOUSE_X_POS], globalVars[VAR_MOUSE_Y_POS]);
		} else {
			element->var16 = 0;
			element->var14 = 0;
		}

		var_10 = computeMove2(element);

		if (var_10) {
			element->var1C = var_10;
			element->var1E = var_10;
		}

		var_4 = -1;

		if ((element->var16 == 1
			&& !addAni(3, element->var6, ptr1, element, 0, &var_4)) || (element->var16 == 2	&& !addAni(2, element->var6, ptr1, element, 0,
			    &var_4))) {
			if (element->varC == 255) {
				globalVars[VAR_MOUSE_Y_POS] = 0;
			}
		}

		if ((element->var14 == 1
			&& !addAni(0, element->var6, ptr1, element, 1, &var_2))) {
			if (element->varC == 255) {
				globalVars[VAR_MOUSE_X_POS] = 0;

				if (var_4 != -1) {
					objectTable[element->var6].costume = var_4;
				}
			}
		}

		if ((element->var14 == 2 && !addAni(1, element->var6, ptr1, element, 1, &var_2))) {
			if (element->varC == 255) {
				globalVars[VAR_MOUSE_X_POS] = 0;

				if (var_4 != -1) {
					objectTable[element->var6].costume = var_4;
				}
			}
		}

		if (element->var16 + element->var14) {
			if (element->var1C) {
				if (element->var1E) {
					objectTable[element->var6].costume = 0;
					element->var1E = 0;
				}

				addAni(element->var1C + 3, element->var6, ptr1, element, 1, (int16 *) & var2);

			}
		}

	}
}

void processSeqList(void) {
	SeqListElement *currentHead = &seqList;
	SeqListElement *tempHead = currentHead;

	currentHead = tempHead->next;

	while (currentHead) {
		if (currentHead->var4 != -1) {
			processSeqListElement(currentHead);
		}

		tempHead = currentHead;
		currentHead = tempHead->next;
	}
}

} // End of namespace Cine
