diff options
| author | Gregory Montoir | 2006-11-03 21:23:07 +0000 | 
|---|---|---|
| committer | Gregory Montoir | 2006-11-03 21:23:07 +0000 | 
| commit | 13d9cdbd26b1c07edf47b9e4731b9d652a294ba5 (patch) | |
| tree | 4ecb39b0e40f592b36fbd5ef427e2ad4ea29ebd1 | |
| parent | c71e6599bc160329319c0c05ca453184a45fb0f7 (diff) | |
| download | scummvm-rg350-13d9cdbd26b1c07edf47b9e4731b9d652a294ba5.tar.gz scummvm-rg350-13d9cdbd26b1c07edf47b9e4731b9d652a294ba5.tar.bz2 scummvm-rg350-13d9cdbd26b1c07edf47b9e4731b9d652a294ba5.zip | |
added 'touche' engine for the game 'Touche: The Adventures of the 5th Musketeer'
svn-id: r24592
| -rw-r--r-- | base/plugins.cpp | 5 | ||||
| -rwxr-xr-x | configure | 8 | ||||
| -rw-r--r-- | engines/engines.mk | 15 | ||||
| -rw-r--r-- | engines/touche/graphics.cpp | 210 | ||||
| -rw-r--r-- | engines/touche/graphics.h | 54 | ||||
| -rw-r--r-- | engines/touche/module.mk | 19 | ||||
| -rw-r--r-- | engines/touche/opcodes.cpp | 822 | ||||
| -rw-r--r-- | engines/touche/plugin.cpp | 154 | ||||
| -rw-r--r-- | engines/touche/resource.cpp | 645 | ||||
| -rw-r--r-- | engines/touche/saveload.cpp | 418 | ||||
| -rw-r--r-- | engines/touche/staticres.cpp | 659 | ||||
| -rw-r--r-- | engines/touche/touche.cpp | 3271 | ||||
| -rw-r--r-- | engines/touche/touche.h | 778 | ||||
| -rw-r--r-- | engines/touche/ui.cpp | 557 | 
14 files changed, 7608 insertions, 7 deletions
| diff --git a/base/plugins.cpp b/base/plugins.cpp index 8738fbe26f..bcca62ce1c 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -150,6 +150,9 @@ public:  		#ifndef DISABLE_AGI  		LINK_PLUGIN(AGI)  		#endif +		#ifndef DISABLE_TOUCHE +		LINK_PLUGIN(TOUCHE) +		#endif  		return pl;  	} @@ -181,7 +184,7 @@ PluginManager::~PluginManager() {  		delete *pp;  	}  } -	 +  void PluginManager::addPluginProvider(PluginProvider *pp) {  	_providers.push_back(pp);  } @@ -60,6 +60,7 @@ _build_kyra=yes  _build_lure=no  _build_cine=yes  _build_agi=no +_build_touche=no  _need_memalign=no  _build_plugins=no  _nasm=auto @@ -332,6 +333,7 @@ Optional Features:    --enable-lure            build the Lure of the Temptress engine    --disable-cine           don't build the Cinematique engine evo 1    --enable-agi             build the AGI engine +  --enable-touche          build the Touche: The Adventures of the Fifth Musketeer engine    --enable-plugins         build engines as loadable modules instead of                             static linking them    --disable-mt32emu        don't enable the integrated MT-32 emulator @@ -400,6 +402,7 @@ for ac_option in $@; do        --enable-lure)            _build_lure=yes ;;        --disable-cine)           _build_cine=no ;;        --enable-agi)             _build_agi=yes ;; +      --enable-touche)          _build_touche=yes ;;        --disable-hq-scalers)     _build_hq_scalers=no ;;        --disable-scalers)        _build_scalers=no ;;        --enable-alsa)            _alsa=yes       ;; @@ -685,6 +688,7 @@ add_flag_to_config_mk $_build_gob         'DISABLE_GOB'  add_flag_to_config_mk $_build_lure        'DISABLE_LURE'  add_flag_to_config_mk $_build_cine        'DISABLE_CINE'  add_flag_to_config_mk $_build_agi         'DISABLE_AGI' +add_flag_to_config_mk $_build_touche      'DISABLE_TOUCHE'  add_flag_to_config_mk $_build_hq_scalers  'DISABLE_HQ_SCALERS'  add_flag_to_config_mk $_build_scalers     'DISABLE_SCALERS' @@ -1270,10 +1274,12 @@ fi  if test "$_build_cine" = yes ; then  	echo "    Cinematique evo 1"  fi -  if test "$_build_agi" = yes ; then  	echo "    AGI"  fi +if test "$_build_touche" = yes ; then +	echo "    Touche: The Adventures of the Fifth Musketeer" +fi  echo diff --git a/engines/engines.mk b/engines/engines.mk index d4be1c1674..a00b509d02 100644 --- a/engines/engines.mk +++ b/engines/engines.mk @@ -45,19 +45,19 @@ endif  ifdef DISABLE_SAGA  DEFINES += -DDISABLE_SAGA -else  +else  MODULES += engines/saga  endif  ifdef DISABLE_KYRA  DEFINES += -DDISABLE_KYRA -else  +else  MODULES += engines/kyra  endif  ifdef DISABLE_GOB  DEFINES += -DDISABLE_GOB -else  +else  MODULES += engines/gob  endif @@ -69,13 +69,18 @@ endif  ifdef DISABLE_CINE  DEFINES += -DDISABLE_CINE -else  +else  MODULES += engines/cine  endif  ifdef DISABLE_AGI  DEFINES += -DDISABLE_AGI -else  +else  MODULES += engines/agi  endif +ifdef DISABLE_TOUCHE +DEFINES += -DDISABLE_TOUCHE +else +MODULES += engines/touche +endif diff --git a/engines/touche/graphics.cpp b/engines/touche/graphics.cpp new file mode 100644 index 0000000000..ec0c63ff79 --- /dev/null +++ b/engines/touche/graphics.cpp @@ -0,0 +1,210 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 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$ + * $Id: $ + * + */ + +#include "common/stdafx.h" +#include "common/endian.h" + +#include "touche/graphics.h" + +namespace Touche { + +int Graphics::getStringWidth16(const char *str) { +	int w = 0; +	while (*str) { +		char chr = *str++; +		w += getCharWidth16((uint8)chr); +		if (*str == '\\') { +			break; +		} +	} +	return w; +} + +int Graphics::getCharWidth16(uint8 chr) { +	assert(chr >= 32 && chr < 174); +	const uint8 *chrData = _fontData + _fontOffs[chr - 32]; +	return chrData[2]; +} + +void Graphics::drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str) { +	while (*str) { +		uint8 chr = (uint8)*str++; +		x += drawChar16(dst, dstPitch, chr, x, y, color); +	} +} + +int Graphics::drawChar16(uint8 *dst, int dstPitch, uint8 chr, int x, int y, uint16 color) { +	dst += y * dstPitch + x; +	uint8 color1 = color & 0xFF; +	uint8 color2 = color >> 8; +	assert(chr >= 32 && chr < 174); +	const uint8 *chrData = _fontData + _fontOffs[chr - 32]; +	int chrHeight = chrData[1]; +	int chrWidth = chrData[2]; +	chrData += 3; +	while (chrHeight--) { +		int shiftCount = 0; +		int mask = 0; +		for (int i = 0; i < chrWidth; ++i) { +			if (shiftCount == 0) { +				mask = READ_BE_UINT16(chrData); chrData += 2; +				shiftCount = 8; +			} +			int b = (mask & 0xC000) >> 14; +			mask <<= 2; +			--shiftCount; +			if (b) { +				if (b & 2) { +					dst[i] = color2; +				} else { +					dst[i] = color1; +				} +			} +		} +		dst += dstPitch; +	} +	return chrWidth; +} + +void Graphics::fillRect(uint8 *dst, int dstPitch, int x, int y, int w, int h, uint8 color) { +	dst += y * dstPitch + x; +	while (h--) { +		memset(dst, color, w); +		dst += dstPitch; +	} +} + +void Graphics::drawRect(uint8 *dst, int dstPitch, int x, int y, int w, int h, uint8 color1, uint8 color2) { +	int x1 = x; +	int y1 = y; +	int x2 = x + w - 1; +	int y2 = y + h - 1; +	drawLineHV(dst, dstPitch, x1, y1, x2, y1, color1); +	drawLineHV(dst, dstPitch, x1, y1, x1, y2, color1); +	drawLineHV(dst, dstPitch, x2, y1 + 1, x2, y2, color2); +	drawLineHV(dst, dstPitch, x1 + 1, y2, x2, y2, color2); +} + +void Graphics::drawLineHV(uint8 *dst, int dstPitch, int x1, int y1, int x2, int y2, uint8 color) { +	if (x2 < x1) { +		SWAP(x2, x1); +	} +	if (y2 < y1) { +		SWAP(y2, y1); +	} +	if (y1 == y2) { +		for (int x = x1; x < x2; ++x) { +			dst[y1 * dstPitch + x] = color; +		} +	} else { +		for (int y = y1; y < y2; ++y) { +			dst[y * dstPitch + x1] = color; +		} +	} +} + +void Graphics::drawLine(uint8 *dst, int dstPitch, int x1, int y1, int x2, int y2, uint8 color) { +	assert(x1 >= 0 && y1 >= 0 && x2 >= 0 && y2 >= 0); + +	dst += y1 * dstPitch + x1; + +	int yInc, dy = y2 - y1; +	if (dy < 0) { +		dy = -dy; +		yInc = -dstPitch; +	} else { +		yInc = dstPitch; +	} + +	int xInc, dx = x2 - x1; +	if (dx < 0) { +		dx = -dx; +		xInc = -1; +	} else { +		xInc = 1; +	} + +	int step = 0; + +	if (dx > dy) { +		for (int i = 0; i < dx + 1; ++i) { +			*dst = color; +			dst += xInc; +			step += dy; +			if (step > dx) { +				step -= dx; +				dst += yInc; +			} +		} +	} else { +		for (int i = 0; i < dy + 1; ++i) { +			*dst = color; +			dst += yInc; +			step += dx; +			if (step > 0) { +				step -= dy; +				dst += xInc; +			} +		} +	} +} + +void Graphics::copyRect(uint8 *dst, int dstPitch, int dstX, int dstY, const uint8 *src, int srcPitch, int srcX, int srcY, int w, int h, int flags) { +	if (w != 0 && h != 0) { +		if (flags & kHFlipped) { +			srcY += h - 1; +			srcPitch = -srcPitch; +		} +		int u = 1; +		if (flags & kVFlipped) { +			srcX += w - 1; +			u = -1; +		} +		dst += dstY * dstPitch + dstX; +		src += srcY * srcPitch + srcX; +		while (h--) { +			for (int i = 0; i < w; ++i) { +				if ((flags & kTransparent) == 0 || src[u * i] != 0) { +					dst[i] = src[u * i]; +				} +			} +			dst += dstPitch; +			src += srcPitch; +		} +	} +} + +void Graphics::copyMask(uint8 *dst, int dstPitch, int dstX, int dstY, const uint8 *src, int srcPitch, int srcX, int srcY, int w, int h, uint8 fillColor) { +	dst += dstY * dstPitch * dstX; +	src += srcY * srcPitch + srcX; +	while (h--) { +		for (int i = 0; i < w; ++i) { +			if (src[i] != 0) { +				dst[i] = fillColor; +			} +		} +		dst += dstPitch; +		src += srcPitch; +	} +} + +} // namespace Touche diff --git a/engines/touche/graphics.h b/engines/touche/graphics.h new file mode 100644 index 0000000000..a55e60c4f3 --- /dev/null +++ b/engines/touche/graphics.h @@ -0,0 +1,54 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 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$ + * $Id: $ + * + */ + +#ifndef TOUCHE_GRAPHICS_H +#define TOUCHE_GRAPHICS_H + +#include "common/util.h" + +namespace Touche { + +struct Graphics { +	enum { +		kVFlipped    = 1 << 0, +		kHFlipped    = 1 << 1, +		kTransparent = 1 << 2 +	}; + +	static int getStringWidth16(const char *str); +	static int getCharWidth16(uint8 chr); +	static void drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str); +	static int drawChar16(uint8 *dst, int dstPitch, uint8 chr, int x, int y, uint16 color); +	static void fillRect(uint8 *dst, int dstPitch, int x, int y, int w, int h, uint8 color); +	static void drawRect(uint8 *dst, int dstPitch, int x, int y, int w, int h, uint8 color1, uint8 color2); +	static void drawLineHV(uint8 *dst, int dstPitch, int x1, int y1, int x2, int y2, uint8 color); +	static void drawLine(uint8 *dst, int dstPitch, int x1, int y1, int x2, int y2, uint8 color); +	static void copyRect(uint8 *dst, int dstPitch, int dstX, int dstY, const uint8 *src, int srcPitch, int srcX, int srcY, int w, int h, int flags = 0); +	static void copyMask(uint8 *dst, int dstPitch, int dstX, int dstY, const uint8 *src, int srcPitch, int srcX, int srcY, int w, int h, uint8 fillColor); + +	static const uint16 _fontOffs[]; +	static const uint8 _fontData[]; +}; + +} // namespace Touche + +#endif diff --git a/engines/touche/module.mk b/engines/touche/module.mk new file mode 100644 index 0000000000..c2d12ca813 --- /dev/null +++ b/engines/touche/module.mk @@ -0,0 +1,19 @@ +MODULE := engines/touche + +MODULE_OBJS := \ +	graphics.o \ +	plugin.o \ +	opcodes.o \ +	resource.o \ +	saveload.o \ +	staticres.o \ +	touche.o \ +	ui.o + +# This module can be built as a plugin +ifdef BUILD_PLUGINS +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/touche/opcodes.cpp b/engines/touche/opcodes.cpp new file mode 100644 index 0000000000..fc71d2835c --- /dev/null +++ b/engines/touche/opcodes.cpp @@ -0,0 +1,822 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 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$ + * $Id: $ + * + */ + +#include "common/stdafx.h" + +#include "touche/touche.h" + +namespace Touche { + +void ToucheEngine::op_nop() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_nop()"); +} + +void ToucheEngine::op_jnz() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_jnz()"); +	if (*_script.stackDataPtr != 0) { +		_script.dataOffset = _script.readNextWord(); +	} else { +		_script.dataOffset += 2; +	} +} + +void ToucheEngine::op_jz() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_jz()"); +	if (*_script.stackDataPtr == 0) { +		_script.dataOffset = _script.readNextWord(); +	} else { +		_script.dataOffset += 2; +	} +} + +void ToucheEngine::op_jmp() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_jmp()"); +	_script.dataOffset = _script.readNextWord(); +} + +void ToucheEngine::op_true() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_true()"); +	*_script.stackDataPtr = -1; +} + +void ToucheEngine::op_false() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_false()"); +	*_script.stackDataPtr = 0; +} + +void ToucheEngine::op_push() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_push()"); +	--_script.stackDataPtr; +	*_script.stackDataPtr = 0; +} + +void ToucheEngine::op_testFalse() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_testFalse()"); +	if (*_script.stackDataPtr == 0) { +		*_script.stackDataPtr = -1; +	} else { +		*_script.stackDataPtr = 0; +	} +} + +void ToucheEngine::op_add() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_add()"); +	int16 val = *_script.stackDataPtr++; +	*_script.stackDataPtr += val; +} + +void ToucheEngine::op_sub() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_sub()"); +	int16 val = *_script.stackDataPtr++; +	*_script.stackDataPtr -= val; +} + +void ToucheEngine::op_mul() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_mul()"); +	int16 val = *_script.stackDataPtr++; +	*_script.stackDataPtr *= val; +} + +void ToucheEngine::op_div() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_div()"); +	int16 val = *_script.stackDataPtr++; +	if (val != 0) { +		*_script.stackDataPtr /= val; +	} else { +		*_script.stackDataPtr = 0; +	} +} + +void ToucheEngine::op_mod() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_mod()"); +	int16 val = *_script.stackDataPtr++; +	if (val != 0) { +		*_script.stackDataPtr %= val; +	} else { +		*_script.stackDataPtr = 0; +	} +} + +void ToucheEngine::op_and() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_and()"); +	uint16 val = *_script.stackDataPtr++; +	*_script.stackDataPtr &= val; +} + +void ToucheEngine::op_or() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_or()"); +	uint16 val = *_script.stackDataPtr++; +	*_script.stackDataPtr |= val; +} + +void ToucheEngine::op_not() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_not()"); +	uint16 val = *_script.stackDataPtr; +	*_script.stackDataPtr = ~val; +} + +void ToucheEngine::op_testGreater() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_testGreater()"); +	int16 val = *_script.stackDataPtr++; +	if (val > *_script.stackDataPtr) { +		*_script.stackDataPtr = -1; +	} else { +		*_script.stackDataPtr = 0; +	} +} + +void ToucheEngine::op_testEquals() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_testEquals()"); +	int16 val = *_script.stackDataPtr++; +	if (val == *_script.stackDataPtr) { +		*_script.stackDataPtr = -1; +	} else { +		*_script.stackDataPtr = 0; +	} +} + +void ToucheEngine::op_testLower() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_testLower()"); +	int16 val = *_script.stackDataPtr++; +	if (val < *_script.stackDataPtr) { +		*_script.stackDataPtr = -1; +	} else { +		*_script.stackDataPtr = 0; +	} +} + +void ToucheEngine::op_fetchScriptWord() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_fetchScriptWord()"); +	int16 val = _script.readNextWord(); +	*_script.stackDataPtr = val; +} + +void ToucheEngine::op_testGreaterOrEquals() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_testGreaterOrEquals()"); +	int16 val = *_script.stackDataPtr++; +	if (val >= *_script.stackDataPtr) { +		*_script.stackDataPtr = -1; +	} else { +		*_script.stackDataPtr = 0; +	} +} + +void ToucheEngine::op_testLowerOrEquals() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_testLowerOrEquals()"); +	int16 val = *_script.stackDataPtr++; +	if (val <= *_script.stackDataPtr) { +		*_script.stackDataPtr = -1; +	} else { +		*_script.stackDataPtr = 0; +	} +} + +void ToucheEngine::op_testNotEquals() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_testNotEquals()"); +	int16 val = *_script.stackDataPtr++; +	if (val != *_script.stackDataPtr) { +		*_script.stackDataPtr = -1; +	} else { +		*_script.stackDataPtr = 0; +	} +} + +void ToucheEngine::op_endConversation() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_endConversation()"); +	_script.quitFlag = 1; +	_conversationEnded = true; +	_disabledInputCounter = false; +} + +void ToucheEngine::op_stopScript() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_stopScript()"); +	_script.quitFlag = 1; +} + +void ToucheEngine::op_getFlag() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_getFlag()"); +	uint16 fl = _script.readNextWord(); +	*_script.stackDataPtr = _flagsTable[fl]; +} + +void ToucheEngine::op_setFlag() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_setFlag()"); +	uint16 flag = _script.readNextWord(); +	int16 val = *_script.stackDataPtr; +	_flagsTable[flag] = val; +	switch (flag) { +	case 104: +		_currentKeyCharNum = val; +		break; +	case 612: +		_flagsTable[613] = getRandomNumber(val); +		break; +	case 614: +	case 615: +		_fullRedrawCounter = 1; +		break; +	default: +		break; +	} +} + +void ToucheEngine::op_fetchScriptByte() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_fetchScriptByte()"); +	int16 val = _script.readNextByte(); +	*_script.stackDataPtr = val; +} + +void ToucheEngine::op_getScriptValue() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_getScriptValue()"); +	uint8 index = _script.readNextByte(); +	assert(index < _script.stackDataBasePtr[2]); +	*_script.stackDataPtr = _script.stackDataBasePtr[3 + index]; +} + +void ToucheEngine::op_setScriptValue() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_setScriptValue()"); +	uint8 index = _script.readNextByte(); +	assert(index < _script.stackDataBasePtr[2]); +	_script.stackDataBasePtr[3 + index] = *_script.stackDataPtr; +} + +void ToucheEngine::op_getKeyCharWalkBox() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_getKeyCharWalkBox()"); +	int16 keyChar = _script.readNextWord(); +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	*_script.stackDataPtr = _keyCharsTable[keyChar].walkDataNum; +} + +void ToucheEngine::op_startSound() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_startSound()"); +	_newSoundNum = _script.readNextWord(); +	_newSoundDelay = _script.readNextWord(); +	_newSoundPriority = 1; +} + +void ToucheEngine::op_initKeyCharTalk() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_initKeyCharTalk()"); +	int16 keyChar = _script.readNextWord(); +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	int16 num = _script.readNextWord(); +	if (num == -1) { +		num = _script.readNextWord(); +		num = _keyCharsTable[num].pointsDataNum; +	} +	sortPointsData(-1, num); +	buildWalkPointsList(keyChar); +	_keyCharsTable[keyChar].flags &= ~0x10; +	if (_script.keyCharNum == keyChar) { +		removeFromTalkTable(_script.keyCharNum); +		_keyCharsTable[keyChar].waitingKeyCharPosTable[0] = -1; +		_keyCharsTable[keyChar].waitingKeyCharPosTable[2] = -1; +		_keyCharsTable[keyChar].waitingKeyChar = _script.keyCharNum; +		_keyCharsTable[keyChar].waitingKeyCharPosTable[1] = num; +		_script.quitFlag = 3; +	} +} + +void ToucheEngine::op_loadRoom() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_loadRoom()"); +	int16 num = _script.readNextWord(); +	res_loadRoom(num); +} + +void ToucheEngine::op_updateRoom() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_updateRoom()"); +	int16 area = _script.readNextWord(); +	updateRoomAreas(area, 0); +} + +void ToucheEngine::op_startTalk() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_startTalk()"); +	int16 keyChar = _script.readNextWord(); +	int16 num = _script.readNextWord(); +	if (num == 750) { +		return; +	} +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +		num += _currentKeyCharNum & 1; +	} +	addToTalkTable(keyChar, num, _script.keyCharNum); +	_script.quitFlag = 3; +} + +void ToucheEngine::op_loadSprite() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_loadSprite()"); +	int16 index = _script.readNextWord(); +	int16 num = _script.readNextWord(); +	res_loadSprite(num, index); +} + +void ToucheEngine::op_loadSequence() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_loadSequence()"); +	int16 index = _script.readNextWord(); +	int16 num = _script.readNextWord(); +	res_loadSequence(num, index); +} + +void ToucheEngine::op_setKeyCharBox() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_setKeyCharBox()"); +	int16 keyChar = _script.readNextWord(); +	int16 num = _script.readNextWord(); +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	setKeyCharBox(keyChar, num); +} + +void ToucheEngine::op_initKeyCharScript() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_initKeyCharScript()"); +	int16 keyChar = _script.readNextWord(); +	int16 color = _script.readNextWord(); +	int16 f1 = _script.readNextWord(); +	int16 f2 = _script.readNextWord(); +	int16 f3 = _script.readNextWord(); +	setKeyCharTextColor(keyChar, color); +	initKeyCharScript(keyChar, f1, f2, f3); +} + +void ToucheEngine::op_setKeyCharFrame() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_setKeyCharFrame()"); +	int16 keyChar = _script.readNextWord(); +	int16 val1 = _script.readNextWord(); +	int16 val2 = _script.readNextWord(); +	int16 val3 = _script.readNextWord(); +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	setKeyCharFrame(keyChar, val1, val2, val3); +} + +void ToucheEngine::op_setKeyCharDirection() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_setKeyCharDirection()"); +	int16 keyChar = _script.readNextWord(); +	int16 dir = _script.readNextWord(); +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	setKeyCharFacingDirection(keyChar, dir); +} + +void ToucheEngine::op_clearConversationChoices() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_clearConversationChoices()"); +	clearConversationChoices(); +} + +void ToucheEngine::op_addConversationChoice() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_addConversationChoice()"); +	int16 num = _script.readNextWord(); +	addConversationChoice(num); +} + +void ToucheEngine::op_removeConversationChoice() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_removeConversationChoice()"); +	int16 num = _script.readNextWord(); +	removeConversationChoice(num); +} + +void ToucheEngine::op_getInventoryItem() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_getInventoryItem()"); +	int16 keyChar = _script.readNextWord(); +	uint16 item = _script.readNextWord(); +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	assert(item < sizeof(_keyCharsTable[keyChar].inventoryItems)); +	*_script.stackDataPtr = _keyCharsTable[keyChar].inventoryItems[item]; +} + +void ToucheEngine::op_setInventoryItem() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_setInventoryItem()"); +	int16 keyChar = _script.readNextWord(); +	uint16 item = _script.readNextWord(); +	if (item == 4) { +		setKeyCharMoney(); +	} +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	assert(item < sizeof(_keyCharsTable[keyChar].inventoryItems)); +	_keyCharsTable[keyChar].inventoryItems[item] = *_script.stackDataPtr; +	if (item == 4 && !_hideInventoryTexts) { +		drawAmountOfMoneyInInventory(); +	} +} + +void ToucheEngine::op_startEpisode() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_startEpisode()"); +	_newEpisodeNum = _script.readNextWord(); +	_flagsTable[0] = _script.readNextWord(); +	_disabledInputCounter = 1; +	_script.quitFlag = 1; +} + +void ToucheEngine::op_setConversationNum() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_setConversationNum()"); +	_conversationNum = _script.readNextWord(); +} + +void ToucheEngine::op_enableInventoryItem() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_enableInventoryItem()"); +	int16 flag = _script.readNextWord(); +	int16 item = _script.readNextWord(); +	int16 rnd = _script.readNextWord(); +	changeInventoryItemState(flag, item, rnd, 1); +} + +void ToucheEngine::op_enableInput() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_enableInput()"); +	++_disabledInputCounter; +} + +void ToucheEngine::op_disableInput() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_disableInput()"); +	if (_disabledInputCounter != 0) { +		--_disabledInputCounter; +	} +} + +void ToucheEngine::op_faceKeyChar() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_faceKeyChar()"); +	int16 keyChar1 = _script.readNextWord(); +	int16 keyChar2 = _script.readNextWord(); +	if (keyChar1 == 256) { +		keyChar1 = _currentKeyCharNum; +	} +	if (_keyCharsTable[keyChar1].xPos <= _keyCharsTable[keyChar2].xPos) { +		_keyCharsTable[keyChar2].facingDirection = 3; +	} else { +		_keyCharsTable[keyChar2].facingDirection = 0; +	} +} + +void ToucheEngine::op_getKeyCharCurrentAnim() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_getKeyCharCurrentAnim()"); +	int16 keyChar = _script.readNextWord(); +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	*_script.stackDataPtr = _keyCharsTable[keyChar].currentAnim; +} + +void ToucheEngine::op_getCurrentKeyChar() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_getCurrentKeyChar()"); +	*_script.stackDataPtr = _currentKeyCharNum; +} + +void ToucheEngine::op_isKeyCharActive() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_isKeyCharActive()"); +	int16 keyChar = _script.readNextWord(); +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	*_script.stackDataPtr = _keyCharsTable[keyChar].num != 0 ? 1 : 0; +} + +void ToucheEngine::op_setPalette() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_setPalette()"); +	int16 r = _script.readNextWord(); +	int16 g = _script.readNextWord(); +	int16 b = _script.readNextWord(); +	setPalette(0, 240, r, g, b); +} + +void ToucheEngine::op_changeWalkPath() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_changeWalkPath()"); +	int16 num1 = _script.readNextWord(); +	int16 num2 = _script.readNextWord(); +	int16 val = _script.readNextWord(); +	changeWalkPath(num1, num2, val); +} + +void ToucheEngine::op_lockWalkPath() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_lockWalkPath()"); +	int16 num1 = _script.readNextWord(); +	int16 num2 = _script.readNextWord(); +	lockWalkPath(num1, num2); +} + +void ToucheEngine::op_initializeKeyChar() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_initializeKeyChar()"); +	int16 keyChar = _script.readNextWord(); +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	initKeyChars(keyChar); +} + +void ToucheEngine::op_setupWaitingKeyChars() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_setupWaitingKeyChars()"); +	int16 keyChar = _script.readNextWord(); +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	int16 val1 = _script.readNextWord(); +	int16 val2 = _script.readNextWord(); +	if (val1 == -1) { +		_waitingSetKeyCharNum2 = keyChar; +		_waitingSetKeyCharNum1 = val2; +		_waitingSetKeyCharNum3 = _script.keyCharNum; +		_script.quitFlag = 3; +	} else { +		_keyCharsTable[_script.keyCharNum].waitingKeyCharPosTable[0] = -1; +		_keyCharsTable[_script.keyCharNum].waitingKeyCharPosTable[1] = -1; +		_keyCharsTable[_script.keyCharNum].waitingKeyCharPosTable[2] = -1; +		_keyCharsTable[_script.keyCharNum].waitingKeyChar = keyChar; +		assert(val1 >= 0 && val1 < 3); +		_keyCharsTable[_script.keyCharNum].waitingKeyCharPosTable[val1] = val2; +		_script.quitFlag = 3; +	} +} + +void ToucheEngine::op_updateRoomAreas() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_setupWaitingKeyChars()"); +	int16 area = _script.readNextWord(); +	updateRoomAreas(area, 1); +} + +void ToucheEngine::op_unlockWalkPath() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_unlockWalkPath()"); +	int16 num1 = _script.readNextWord(); +	int16 num2 = _script.readNextWord(); +	unlockWalkPath(num1, num2); +} + +void ToucheEngine::op_addItemToInventoryAndRedraw() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_addItemToInventoryAndRedraw()"); +	int16 inventory = _script.readNextWord(); +	int16 item = *_script.stackDataPtr; +	if (inventory == 256) { +		inventory = _currentKeyCharNum; +	} +	addItemToInventory(inventory, item); +	if (_currentKeyCharNum == inventory && !_hideInventoryTexts) { +		drawInventory(_currentKeyCharNum, 1); +	} +} + +void ToucheEngine::op_giveItemTo() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_giveItemTo()"); +	_giveItemToCounter = 1; +	_giveItemToObjectNum = _script.readNextWord(); +	_giveItemToKeyCharNum = _script.keyCharNum; +	_script.quitFlag = 3; +} + +void ToucheEngine::op_resetHitBoxes() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_resetHitBoxes()"); +	int16 num = _script.readNextWord(); +	if (num & 0x4000) { +		num &= 0xFF; +		_keyCharsTable[num].strNum = 1; +	} else { +		for (uint i = 0; i < _programHitBoxTable.size(); ++i) { +			if (_programHitBoxTable[i].item == num) { +				_programHitBoxTable[i].str = _programHitBoxTable[i].defaultStr; +				break; +			} +		} +	} +} + +void ToucheEngine::op_fadePalette() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_fadePalette()"); +	int16 fadeOut = _script.readNextWord(); +	if (fadeOut) { +		fadePalette(0, 240, 255, -2, 128); +	} else { +		fadePalette(0, 240, 0, 2, 128); +	} +} + +void ToucheEngine::op_disableInventoryItem() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_disableInventoryItem()"); +	int16 flag = _script.readNextWord(); +	int16 item = _script.readNextWord(); +	int16 rnd = _script.readNextWord(); +	changeInventoryItemState(flag, item, rnd, 0); +} + +void ToucheEngine::op_getInventoryItemFlags() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_getInventoryItemFlags()"); +	int16 item = _script.readNextWord(); +	int16 flags = _inventoryItemsInfoTable[item]; +	if (flags & 0x10) { +		flags &= 0xF; +	} else { +		flags &= ~0xF; +	} +	*_script.stackDataPtr = flags; +} + +void ToucheEngine::op_drawInventory() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_drawInventory()"); +	int16 num = _script.readNextWord(); +	drawInventory(num, 1); +} + +void ToucheEngine::op_stopKeyCharScript() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_stopKeyCharScript()"); +	int16 keyChar = _script.readNextWord(); +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	_keyCharsTable[keyChar].flags |= kScriptStopped; +} + +void ToucheEngine::op_restartKeyCharScript() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_restartKeyCharScript()"); +	int16 keyChar = _script.readNextWord(); +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	KeyChar *key = &_keyCharsTable[keyChar]; +	key->flags &= ~(kScriptStopped | kScriptPaused); +	key->scriptDataOffset = key->scriptDataStartOffset; +	key->scriptStackPtr = &key->scriptStackTable[39]; +} + +void ToucheEngine::op_getKeyCharCurrentWalkBox() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_getKeyCharCurrentWalkBox()"); +	int16 keyChar = _script.readNextWord(); +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	*_script.stackDataPtr = _keyCharsTable[keyChar].currentWalkBox; +} + +void ToucheEngine::op_getKeyCharPointsDataNum() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_getKeyCharPointsDataNum()"); +	int16 keyChar = _script.readNextWord(); +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	*_script.stackDataPtr = _keyCharsTable[keyChar].pointsDataNum; +} + +void ToucheEngine::op_setupFollowingKeyChar() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_setupFollowingKeyChar()"); +	int16 val = _script.readNextWord(); +	int16 keyChar = _script.readNextWord(); +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	_keyCharsTable[keyChar].followingKeyCharNum = val; +	_keyCharsTable[keyChar].flags |= 0x10; +	_keyCharsTable[keyChar].followingKeyCharPos = -1; +} + +void ToucheEngine::op_startAnimation() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_startAnimation()"); +	int16 num = _script.readNextWord(); +	int16 pos = _script.readNextWord(); +	int16 keyChar = *_script.stackDataPtr; +	addToAnimationTable(num, pos, keyChar, 3); +} + +void ToucheEngine::op_setKeyCharTextColor() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_setKeyCharTextColor()"); +	int16 keyChar = _script.readNextWord(); +	uint16 color = _script.readNextWord(); +	setKeyCharTextColor(keyChar, color); +} + +void ToucheEngine::op_startMusic() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_startMusic()"); +	_newMusicNum = _script.readNextWord(); +} + +void ToucheEngine::op_copyPaletteColor() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_copyPaletteColor()"); +	int16 src = _script.readNextWord(); +	int16 dst = _script.readNextWord(); +	copyPaletteColor(src, dst); +} + +void ToucheEngine::op_delay() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_delay()"); +	int16 delay = _script.readNextWord(); +	_keyCharsTable[_script.keyCharNum].delay = delay; +	_script.quitFlag = 3; +} + +void ToucheEngine::op_lockHitBox() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_lockHitBox()"); +	int16 num = _script.readNextWord(); +	lockUnlockHitBox(num, 1); +} + +void ToucheEngine::op_removeItemFromInventory() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_removeItemFromInventory()"); +	int16 keyChar = _script.readNextWord(); +	int16 item = *_script.stackDataPtr; +	if (keyChar == 256) { +		keyChar = _currentKeyCharNum; +	} +	removeItemFromInventory(keyChar, item); +	if (keyChar == _currentKeyCharNum && !_hideInventoryTexts) { +		drawInventory(_currentKeyCharNum, 1); +	} +} + +void ToucheEngine::op_unlockHitBox() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_unlockHitBox()"); +	int16 num = _script.readNextWord(); +	lockUnlockHitBox(num, 0); +} + +void ToucheEngine::op_addRoomArea() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_addRoomArea()"); +	int16 num = _script.readNextWord(); +	uint16 flag = _script.readNextWord(); +	addRoomArea(num, flag); +} + +void ToucheEngine::op_setKeyCharFlags() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_setKeyCharFlags()"); +	int16 keyChar = _script.readNextWord(); +	uint16 flags = _script.readNextWord(); +	flags &= 0xFF00; +	_keyCharsTable[keyChar].flags |= flags; +} + +void ToucheEngine::op_unsetKeyCharFlags() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_unsetKeyCharFlags()"); +	int16 keyChar = _script.readNextWord(); +	uint16 flags = _script.readNextWord(); +	flags &= 0xFF00; +	_keyCharsTable[keyChar].flags &= ~flags; +} + +void ToucheEngine::op_loadVoice() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_loadVoice()"); +	int16 num = _script.readNextWord(); +	res_loadSpeech(num); +} + +void ToucheEngine::op_drawSpriteOnBackdrop() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_drawSpriteOnBackdrop()"); +	int16 num = _script.readNextWord(); +	int16 x = _script.readNextWord(); +	int16 y = _script.readNextWord(); +	drawSpriteOnBackdrop(num, x, y); +} + +void ToucheEngine::op_startPaletteFadeIn() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_startPaletteFadeIn()"); +	_flagsTable[290] = 0; +	_flagsTable[605] = 0; +	_flagsTable[607] = 0; +	_flagsTable[608] = 0xFF; +	_flagsTable[609] = 0xFF; +	_flagsTable[610] = 0; +	_flagsTable[603] = _script.readNextWord(); +} + +void ToucheEngine::op_startPaletteFadeOut() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_startPaletteFadeOut()"); +	_flagsTable[290] = 0; +	_flagsTable[605] = 0xFF; +	_flagsTable[607] = 0; +	_flagsTable[608] = 0xFF; +	_flagsTable[609] = 0xFF; +	_flagsTable[610] = 0; +	_flagsTable[603] = -_script.readNextWord(); +} + +void ToucheEngine::op_setRoomAreaState() { +	debugC(9, kDebugOpcodes, "ToucheEngine::op_setRoomAreaState()"); +	int16 num = _script.readNextWord(); +	int16 val = _script.readNextWord(); +	setRoomAreaState(num, val); +} + +} // namespace Touche diff --git a/engines/touche/plugin.cpp b/engines/touche/plugin.cpp new file mode 100644 index 0000000000..47ec8d1afe --- /dev/null +++ b/engines/touche/plugin.cpp @@ -0,0 +1,154 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 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$ + * $Id: $ + * + */ + +#include "common/stdafx.h" +#include "common/config-manager.h" +#include "common/fs.h" +#include "common/md5.h" + +#include "base/plugins.h" + +#include "touche/touche.h" + +struct GameVersion { +	const char *description; +	uint32 filesize; +	const char *md5digest; +	Common::Language language; +	Common::Platform platform; +}; + +static const GameVersion toucheGameVersionsTable[] = { +	{ +		"Touche: The Adventures of the Fifth Musketeer", +		26350211, +		"2af0177f8887e3430f345e6b4d8b1414", +		Common::EN_ANY, +		Common::kPlatformPC +	}, +	{ +		"Touche: Les Aventures du Cinquieme Mousquetaire", +		26558232, +		"1caa20bb4d4fc2ce8eb867b6610082b3", +		Common::FR_FRA, +		Common::kPlatformPC +	} +}; + +static const PlainGameDescriptor toucheGameDescriptor = { +	"touche", "Touche: The Adventures of the Fifth Musketeer" +}; + +static const char *toucheDetectFileName = "TOUCHE.DAT"; + +static Common::String Engine_TOUCHE_md5digest(const FilesystemNode *file) { +	static const int md5DataSize = 4096; +	uint8 md5digest[16]; +	if (Common::md5_file(*file, md5digest, md5DataSize)) { +		char md5sum[32 + 1]; +		for (int i = 0; i < 16; ++i) { +			sprintf(md5sum + i * 2, "%02x", (int)md5digest[i]); +		} +		return md5sum; +	} +	return ""; +} + +static uint32 Engine_TOUCHE_filesize(const FilesystemNode *file) { +	Common::File f; +	if (f.open(file->path().c_str())) { +		return f.size(); +	} +	return 0; +} + +GameList Engine_TOUCHE_gameIDList() { +	GameList games; +	games.push_back(toucheGameDescriptor); +	return games; +} + +GameDescriptor Engine_TOUCHE_findGameID(const char *gameid) { +	if (scumm_stricmp(toucheGameDescriptor.gameid, gameid) == 0) { +		return toucheGameDescriptor; +	} +	return GameDescriptor(); +} + +DetectedGameList Engine_TOUCHE_detectGames(const FSList &fslist) { +	bool foundFile = false; +	FSList::const_iterator file; +	for (file = fslist.begin(); file != fslist.end(); ++file) { +		if (file->isDirectory()) { +			continue; +		} +		for (int i = 0; i < ARRAYSIZE(toucheGameVersionsTable); ++i) { +			if (scumm_stricmp(file->name().c_str(), toucheDetectFileName) == 0) { +				foundFile = true; +				break; +			} +		} +		if (foundFile) { +			break; +		} +	} +	DetectedGameList detectedGames; +	if (foundFile) { +		Common::String md5digest = Engine_TOUCHE_md5digest(file); +		if (!md5digest.empty()) { +			for (int i = 0; i < ARRAYSIZE(toucheGameVersionsTable); ++i) { +				const GameVersion *gv = &toucheGameVersionsTable[i]; +				if (md5digest.equalsIgnoreCase(gv->md5digest)) { +					DetectedGame dg(toucheGameDescriptor.gameid, gv->description, gv->language, gv->platform); +					detectedGames.push_back(dg); +					break; +				} +			} +			if (detectedGames.empty()) { +				const uint32 filesize = Engine_TOUCHE_filesize(file); +				printf("Datafile size (%d) and MD5 (%s) are unknown !\n", filesize, md5digest.c_str()); +				printf("Please report the details (language, platform, etc.) of this game to the ScummVM team.\n"); +				detectedGames.push_back(toucheGameDescriptor); +			} +		} +	} +	return detectedGames; +} + +PluginError Engine_TOUCHE_create(OSystem *system, Engine **engine) { +	FSList fslist; +	FilesystemNode dir(ConfMan.get("path")); +	if (!dir.listDir(fslist, FilesystemNode::kListFilesOnly)) { +		warning("ToucheEngine: invalid game path '%s'", dir.path().c_str()); +		return kInvalidPathError; +	} +	DetectedGameList game = Engine_TOUCHE_detectGames(fslist); +	if (game.size() != 1) { +		warning("ToucheEngine: Unable to locate game data in '%s'", dir.path().c_str()); +		return kNoGameDataFoundError; +	} +	assert(engine); +	*engine = new Touche::ToucheEngine(system, game[0].language); +	return kNoError; +} + +REGISTER_PLUGIN(TOUCHE, "Touche: The Adventures of the 5th Musketeer", "Touche: The Adventures of the 5th Musketeer (C) US Gold"); diff --git a/engines/touche/resource.cpp b/engines/touche/resource.cpp new file mode 100644 index 0000000000..fd6441d8ca --- /dev/null +++ b/engines/touche/resource.cpp @@ -0,0 +1,645 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 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$ + * $Id: $ + * + */ + +#include "common/stdafx.h" + +#include "sound/flac.h" +#include "sound/mixer.h" +#include "sound/mp3.h" +#include "sound/voc.h" +#include "sound/vorbis.h" + +#include "touche/touche.h" +#include "touche/graphics.h" + +namespace Touche { + +enum { +	kCurrentSpeechDataVersion = 1, +	kSpeechDataFileHeaderSize = 4 +}; + +struct CompressedSpeechFile { +	const char *filename; +	Audio::AudioStream *(*makeStream)(Common::File *file, uint32 size); +}; + +static const CompressedSpeechFile compressedSpeechFilesTable[] = { +#ifdef USE_MAD +	{ "TOUCHE.SO3", Audio::makeMP3Stream }, +#endif +#ifdef USE_VORBIS +	{ "TOUCHE.SOG", Audio::makeVorbisStream }, +#endif +#ifdef USE_FLAC +	{ "TOUCHE.SOF", Audio::makeFlacStream }, +#endif +	{ 0, 0 } +}; + +void ToucheEngine::res_openDataFile() { +	if (!_fData.open("TOUCHE.DAT")) { +		error("Unable to open 'TOUCHE.DAT' for reading"); +	} +	for (int i = 0; compressedSpeechFilesTable[i].filename; ++i) { +		if (_fSpeech[0].open(compressedSpeechFilesTable[i].filename)) { +			_compressedSpeechData = i; +			return; +		} +	} +	// _fSpeech[0] opening/closing is driven by the scripts +	_fSpeech[1].open("OBJ"); +	_compressedSpeechData = -1; +} + +void ToucheEngine::res_closeDataFile() { +	_fData.close(); +	_fSpeech[0].close(); +	_fSpeech[1].close(); +} + +void ToucheEngine::res_allocateTables() { +	_fData.seek(64); +	uint32 textDataOffs = _fData.readUint32LE(); +	uint32 textDataSize = _fData.readUint32LE(); +	_textData = (uint8 *)malloc(textDataSize); +	if (!_textData) { +		error("Unable to allocate memory for text data"); +	} +	_fData.seek(textDataOffs); +	_fData.read(_textData, textDataSize); + +	_fData.seek(2); +	const int bw = _fData.readUint16LE(); +	const int bh = _fData.readUint16LE(); +	uint32 size = bw * bh; +	_backdropBuffer = (uint8 *)malloc(size); +	if (!_backdropBuffer) { +		error("Unable to allocate memory for backdrop buffer"); +	} + +	_menuKitData = (uint8 *)malloc(5040); +	if (!_menuKitData) { +		error("Unable to allocate memory for menu kit data"); +	} + +	_convKitData = (uint8 *)malloc(12160); +	if (!_convKitData) { +		error("Unable to allocate memory for conv kit data"); +	} + +	for (int i = 0; i < 5; ++i) { +		_sequenceDataTable[i] = (uint8 *)malloc(16384); +		if (!_sequenceDataTable[i]) { +			error("Unable to allocate memory for sequence data %d", i); +		} +	} + +	_programData = (uint8 *)malloc(61440); +	if (!_programData) { +		error("Unable to allocate memory for program data"); +	} + +	_mouseData = (uint8 *)malloc(58 * 42); +	if (!_mouseData) { +		error("Unable to allocate memory for mouse data"); +	} + +	_iconData = (uint8 *)malloc(58 * 42); +	if (!_iconData) { +		error("Unable to allocate memory for object data"); +	} +	for (int i = 0; i < NUM_SPRITES; ++i) { +		_spritesTable[i].ptr = (uint8 *)malloc(_spritesTable[i].size); +		if (!_spritesTable[i].ptr) { +			error("Unable to allocate memory for sprite %d", i); +		} +	} + +	_offscreenBuffer = (uint8 *)malloc(640 * 400); +	if (!_offscreenBuffer) { +		error("Unable to allocate memory for offscreen buffer"); +	} +} + +void ToucheEngine::res_deallocateTables() { +	free(_textData); +	_textData = 0; + +	free(_backdropBuffer); +	_backdropBuffer = 0; + +	free(_menuKitData); +	_menuKitData = 0; + +	free(_convKitData); +	_convKitData = 0; + +	for (int i = 0; i < 5; ++i) { +		free(_sequenceDataTable[i]); +		_sequenceDataTable[i] = 0; +	} + +	free(_programData); +	_programData = 0; + +	free(_mouseData); +	_mouseData = 0; + +	free(_iconData); +	_iconData = 0; + +	for (int i = 0; i < NUM_SPRITES; ++i) { +		free(_spritesTable[i].ptr); +	} + +	free(_offscreenBuffer); +	_offscreenBuffer = 0; +} + +uint32 ToucheEngine::res_getDataOffset(ResourceType type, int num, uint32 *size) { +	debugC(9, kDebugResource, "ToucheEngine::res_getDataOffset() type=%d num=%d", type, num); +	static const struct ResourceData { +		int offs; +		int count; +		int type; +	} dataTypesTable[] = { +		{ 0x048, 100, kResourceTypeRoomImage   }, +		{ 0x228,  30, kResourceTypeSequence    }, +		{ 0x2A0,  50, kResourceTypeSpriteImage }, +		{ 0x390, 100, kResourceTypeIconImage   }, +		{ 0x6B0,  80, kResourceTypeRoomInfo    }, +		{ 0x908, 150, kResourceTypeProgram     }, +		{ 0xB60,  50, kResourceTypeMusic       }, +		{ 0xC28, 120, kResourceTypeSound       } +	}; + +	const ResourceData *rd = NULL; +	for (unsigned int i = 0; i < ARRAYSIZE(dataTypesTable); ++i) { +		rd = &dataTypesTable[i]; +		if (rd->type == type) { +			break; +		} +	} +	if (rd == NULL) { +		error("Invalid resource type %d", type); +	} +	if (num < 0 || num > rd->count) { +		error("Invalid resource number %d (type %d)", num, type); +	} +	_fData.seek(rd->offs + num * 4); +	uint32 offs = _fData.readUint32LE(); +	assert(offs != 0); +	if (size) { +		uint32 nextOffs = _fData.readUint32LE(); +		*size = nextOffs - offs; +	} +	return offs; +} + +void ToucheEngine::res_loadSpriteImage(int num, uint8 *dst) { +	debugC(9, kDebugResource, "ToucheEngine::res_loadSpriteImage() num=%d", num); +	const uint32 offs = res_getDataOffset(kResourceTypeSpriteImage, num); +	_fData.seek(offs); +	_currentImageWidth = _fData.readUint16LE(); +	_currentImageHeight = _fData.readUint16LE(); +	for (int i = 0; i < _currentImageHeight; ++i) { +		res_decodeScanLineImageRLE(dst + _currentImageWidth * i, _currentImageWidth); +	} +} + +void ToucheEngine::res_loadProgram(int num) { +	debugC(9, kDebugResource, "ToucheEngine::res_loadProgram() num=%d", num); +	const uint32 offs = res_getDataOffset(kResourceTypeProgram, num, &_programDataSize); +	_fData.seek(offs); +	_fData.read(_programData, _programDataSize); +} + +void ToucheEngine::res_decodeProgramData() { +	debugC(9, kDebugResource, "ToucheEngine::res_decodeProgramData()"); + +	uint8 *p; +	uint8 *programDataEnd = _programData + _programDataSize; + +	p = _programData + READ_LE_UINT32(_programData + 32); +	_script.init(p); + +	p = _programData + READ_LE_UINT32(_programData + 4); +	_programTextDataPtr = p; + +	_programRectsTable.clear(); +	p = _programData + READ_LE_UINT32(_programData + 20); +	while (p < programDataEnd) { +		int16 x = READ_LE_UINT16(p); p += 2; +		int16 y = READ_LE_UINT16(p); p += 2; +		int16 w = READ_LE_UINT16(p); p += 2; +		int16 h = READ_LE_UINT16(p); p += 2; +		_programRectsTable.push_back(Common::Rect(x, y, x + w, y + h)); +		if (x == -1) { +			break; +		} +	} + +	_programPointsTable.clear(); +	p = _programData + READ_LE_UINT32(_programData + 24); +	while (p < programDataEnd) { +		ProgramPointData ppd; +		ppd.x = READ_LE_UINT16(p); p += 2; +		ppd.y = READ_LE_UINT16(p); p += 2; +		ppd.z = READ_LE_UINT16(p); p += 2; +		ppd.priority = READ_LE_UINT16(p); p += 2; +		_programPointsTable.push_back(ppd); +		if (ppd.x == -1) { +			break; +		} +	} + +	_programWalkTable.clear(); +	p = _programData + READ_LE_UINT32(_programData + 28); +	while (p < programDataEnd) { +		ProgramWalkData pwd; +		pwd.point1 = READ_LE_UINT16(p); p += 2; +		if (pwd.point1 == -1) { +			break; +		} +		assert((uint16)pwd.point1 < _programPointsTable.size()); +		pwd.point2 = READ_LE_UINT16(p); p += 2; +		assert((uint16)pwd.point2 < _programPointsTable.size()); +		pwd.clippingRect = READ_LE_UINT16(p); p += 2; +		pwd.area1 = READ_LE_UINT16(p); p += 2; +		pwd.area2 = READ_LE_UINT16(p); p += 2; +		p += 12; // unused +		_programWalkTable.push_back(pwd); +	} + +	_programAreaTable.clear(); +	p = _programData + READ_LE_UINT32(_programData + 8); +	while (p < programDataEnd) { +		ProgramAreaData pad; +		int16 x = READ_LE_UINT16(p); p += 2; +		if (x == -1) { +			break; +		} +		int16 y = READ_LE_UINT16(p); p += 2; +		int16 w = READ_LE_UINT16(p); p += 2; +		int16 h = READ_LE_UINT16(p); p += 2; +		pad.area.r = Common::Rect(x, y, x + w, y + h); +		pad.area.srcX = READ_LE_UINT16(p); p += 2; +		pad.area.srcY = READ_LE_UINT16(p); p += 2; +		pad.id = READ_LE_UINT16(p); p += 2; +		pad.state = READ_LE_UINT16(p); p += 2; +		pad.animCount = READ_LE_UINT16(p); p += 2; +		pad.animNext = READ_LE_UINT16(p); p += 2; +		_programAreaTable.push_back(pad); +	} + +	_programBackgroundTable.clear(); +	p = _programData + READ_LE_UINT32(_programData + 12); +	while (p < programDataEnd) { +		ProgramBackgroundData pbd; +		int16 x = READ_LE_UINT16(p); p += 2; +		if (x == -1) { +			break; +		} +		int16 y = READ_LE_UINT16(p); p += 2; +		int16 w = READ_LE_UINT16(p); p += 2; +		int16 h = READ_LE_UINT16(p); p += 2; +		pbd.area.r = Common::Rect(x, y, x + w, y + h); +		pbd.area.srcX = READ_LE_UINT16(p); p += 2; +		pbd.area.srcY = READ_LE_UINT16(p); p += 2; +		pbd.type = READ_LE_UINT16(p); p += 2; +		pbd.offset = READ_LE_UINT16(p); p += 2; +		pbd.scaleMul = READ_LE_UINT16(p); p += 2; +		pbd.scaleDiv = READ_LE_UINT16(p); p += 2; +		_programBackgroundTable.push_back(pbd); +	} + +	_programHitBoxTable.clear(); +	p = _programData + READ_LE_UINT32(_programData + 16); +	while (p < programDataEnd) { +		ProgramHitBoxData phbd; +		phbd.item = READ_LE_UINT16(p); p += 2; +		if (phbd.item == 0) { +			break; +		} +		phbd.talk = READ_LE_UINT16(p); p += 2; +		phbd.state = READ_LE_UINT16(p); p += 2; +		phbd.str = READ_LE_UINT16(p); p += 2; +		phbd.defaultStr = READ_LE_UINT16(p); p += 2; +		for (int i = 0; i < 8; ++i) { +			phbd.actions[i] = READ_LE_UINT16(p); p += 2;; +		} +		for (int i = 0; i < 2; ++i) { +			int16 x = READ_LE_UINT16(p); p += 2; +			int16 y = READ_LE_UINT16(p); p += 2; +			int16 w = READ_LE_UINT16(p); p += 2; +			int16 h = READ_LE_UINT16(p); p += 2; +			phbd.hitBoxes[i].left = x; +			phbd.hitBoxes[i].top = y; +			phbd.hitBoxes[i].right = x + w; +			phbd.hitBoxes[i].bottom = y + h; +		} +		p += 8; // unused +		_programHitBoxTable.push_back(phbd); +	} + +	_programActionScriptOffsetTable.clear(); +	p = _programData + READ_LE_UINT32(_programData + 36); +	while (p < programDataEnd) { +		ProgramActionScriptOffsetData pasod; +		pasod.object1 = READ_LE_UINT16(p); p += 2; +		if (pasod.object1 == 0) { +			break; +		} +		pasod.action = READ_LE_UINT16(p); p += 2; +		pasod.object2 = READ_LE_UINT16(p); p += 2; +		pasod.offset = READ_LE_UINT16(p); p += 2; +		_programActionScriptOffsetTable.push_back(pasod); +	} + +	_programConversationTable.clear(); +	int count = (READ_LE_UINT32(_programData + 44) - READ_LE_UINT32(_programData + 40)) / 6; +	assert(count >= 0); +	p = _programData + READ_LE_UINT32(_programData + 40); +	while (p < programDataEnd && count != 0) { +		ProgramConversationData pcd; +		pcd.num = READ_LE_UINT16(p); p += 2; +		pcd.offset = READ_LE_UINT16(p); p += 2; +		pcd.msg = READ_LE_UINT16(p); p += 2; +		_programConversationTable.push_back(pcd); +		--count; +	} + +	_programKeyCharScriptOffsetTable.clear(); +	p = _programData + READ_LE_UINT32(_programData + 44); +	while (p < programDataEnd) { +		ProgramKeyCharScriptOffsetData pksod; +		pksod.keyChar = READ_LE_UINT16(p); p += 2; +		if (pksod.keyChar == 0) { +			break; +		} +		pksod.offset = READ_LE_UINT16(p); p += 2; +		_programKeyCharScriptOffsetTable.push_back(pksod); +	} +} + +void ToucheEngine::res_loadRoom(int num) { +	debugC(9, kDebugResource, "ToucheEngine::res_loadRoom() num=%d flag115=%d", num, _flagsTable[115]); + +	_currentRoomNum = num; +	_updatedRoomAreasTable[0] = 1; + +	const uint32 offsInfo = res_getDataOffset(kResourceTypeRoomInfo, num); +	_fData.seek(offsInfo); +	_fData.readUint16LE(); +	const int roomImageNum = _fData.readUint16LE(); +	_fData.readUint16LE(); +	for (int i = 0; i < 256; ++i) { +		_fData.read(&_paletteBuffer[i * 4], 3); +		_paletteBuffer[i * 4 + 3] = 0; +	} + +	const uint32 offsImage = res_getDataOffset(kResourceTypeRoomImage, roomImageNum); +	_fData.seek(offsImage); +	res_loadBackdrop(); + +	if (_flagsTable[115] == 0) { +		updatePalette(); +	} +	_fullRedrawCounter = 1; +	_roomNeedRedraw = true; + +	_sequenceEntryTable[5].sprNum = -1; +	_sequenceEntryTable[5].seqNum = -1; +	_sequenceEntryTable[6].sprNum = -1; +	_sequenceEntryTable[6].seqNum = -1; +} + +void ToucheEngine::res_loadSprite(int num, int index) { +	debugC(9, kDebugResource, "ToucheEngine::res_loadSprite() num=%d index=%d", num, index); +	assert(index >= 0 && index < NUM_SEQUENCES); +	_sequenceEntryTable[index].sprNum = num; +	SpriteData *spr = &_spritesTable[index]; +	const uint32 offs = res_getDataOffset(kResourceTypeSpriteImage, num); +	_fData.seek(offs); +	_currentImageWidth = _fData.readUint16LE(); +	_currentImageHeight = _fData.readUint16LE(); +	for (int i = 0; i < _currentImageHeight; ++i) { +		res_decodeScanLineImageRLE(spr->ptr + _currentImageWidth * i, _currentImageWidth); +	} +	spr->bitmapWidth = _currentImageWidth; +	spr->bitmapHeight = _currentImageHeight; +	if (_flagsTable[268] == 0) { +		res_loadImageHelper(spr->ptr, _currentImageWidth, _currentImageHeight); +	} +	spr->w = _currentImageWidth; +	spr->h = _currentImageHeight; +	Graphics::copyRect(_offscreenBuffer, 640, 0, 0, +	  _backdropBuffer, _currentImageWidth, _flagsTable[614], _flagsTable[615], +	  640, 100); +} + +void ToucheEngine::res_loadSequence(int num, int index) { +	debugC(9, kDebugResource, "ToucheEngine::res_loadSequence() num=%d index=%d", num, index); +	assert(index < NUM_SEQUENCES); +	_sequenceEntryTable[index].seqNum = num; +	const uint32 offs = res_getDataOffset(kResourceTypeSequence, num); +	_fData.seek(offs); +	_fData.read(_sequenceDataTable[index], 16000); +} + +void ToucheEngine::res_decodeScanLineImageRLE(uint8 *dst, int lineWidth) { +	int w = 0; +	while (w < lineWidth) { +		uint8 code = _fData.readByte(); +		if ((code & 0xC0) == 0xC0) { +			int len = code & 0x3F; +			uint8 color = _fData.readByte(); +			memset(dst, color, len); +			dst += len; +			w += len; +		} else { +			*dst = code; +			++dst; +			++w; +		} +	} +} + +void ToucheEngine::res_loadBackdrop() { +	debugC(9, kDebugResource, "ToucheEngine::res_loadBackdrop()"); +	_currentBitmapWidth = _fData.readUint16LE(); +	_currentBitmapHeight = _fData.readUint16LE(); +	uint8 *dst = _backdropBuffer; +	for (int i = 0; i < _currentBitmapHeight; ++i) { +		res_decodeScanLineImageRLE(dst + _currentBitmapWidth * i, _currentBitmapWidth); +	} +	_roomWidth = _currentBitmapWidth; +	dst = _backdropBuffer; +	for (int i = 0; i < _currentBitmapWidth; ++i) { +		if (*dst == 255) { +			_roomWidth = i; +			*dst = 0; +			break; +		} +		++dst; +	} +} + +void ToucheEngine::res_loadImage(int num, uint8 *dst) { +	debugC(9, kDebugResource, "ToucheEngine::res_loadImage() num=%d", num); +	const uint32 offsInfo = res_getDataOffset(kResourceTypeIconImage, num); +	_fData.seek(offsInfo); +	_currentImageWidth = _fData.readUint16LE(); +	_currentImageHeight = _fData.readUint16LE(); +	for (int i = 0; i < _currentImageHeight; ++i) { +		res_decodeScanLineImageRLE(dst + _currentImageWidth * i, _currentImageWidth); +	} +	res_loadImageHelper(dst, _currentImageWidth, _currentImageHeight); +} + +void ToucheEngine::res_loadImageHelper(uint8 *imgData, int imgWidth, int imgHeight) { +	uint8 *p = imgData; +	for (_currentImageHeight = 0; _currentImageHeight < imgHeight; ++_currentImageHeight, p += imgWidth) { +		if (*p == 64 || *p == 255) { +			break; +		} +	} +	p = imgData; +	for (_currentImageWidth = 0; _currentImageWidth < imgWidth; ++_currentImageWidth, ++p) { +		if (*p == 64 || *p == 255) { +			break; +		} +	} +	if (_flagsTable[267] == 0) { +		for (int i = 0; i < imgWidth * imgHeight; ++i) { +			uint8 color = imgData[i]; +			if (color != 0) { +				if (color < 64) { +					color += 192; +				} else { +					color = 0; +				} +			} +			imgData[i] = color; +		} +	} +} + +void ToucheEngine::res_loadSound(int priority, int num) { +	debugC(9, kDebugResource, "ToucheEngine::res_loadSound() num=%d", num); +	if (priority >= _defaultSoundPriority) { +		uint32 size; +		const uint32 offs = res_getDataOffset(kResourceTypeSound, num, &size); +		_fData.seek(offs); +		Audio::AudioStream *stream = Audio::makeVOCStream(_fData); +		if (stream) { +			_mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_sfxHandle, stream); +		} +	} +} + +void ToucheEngine::res_loadMusic(int num) { +	debugC(9, kDebugResource, "ToucheEngine::res_loadMusic() num=%d", num); +	warning("UNIMPLEMENTED ToucheEngine::res_loadMusic() num=%d", num); +	uint32 size; +	const uint32 offs = res_getDataOffset(kResourceTypeMusic, num, &size); +	_fData.seek(offs); +	// XXX start MIDI data playback +} + +void ToucheEngine::res_loadSpeech(int num) { +	debugC(9, kDebugResource, "ToucheEngine::res_loadSpeech() num=%d", num); +	if (num == -1) { +		// XXX stop all sounds currently playing +	} else { +		if (_compressedSpeechData < 0) { // uncompressed speech data +			if (_fSpeech[0].isOpen()) { +				_fSpeech[0].close(); +			} +			char filename[10]; +			sprintf(filename, "V%d", num); +			_fSpeech[0].open(filename); +		} +		if (_fSpeech[0].isOpen()) { +			_flagsTable[617] = num; +		} +	} +} + +void ToucheEngine::res_loadSpeechSegment(int num) { +	debugC(9, kDebugResource, "ToucheEngine::res_loadSpeechSegment() num=%d", num); +	if (_talkTextMode != kTalkModeTextOnly) { +		Audio::AudioStream *stream = 0; +		if (_compressedSpeechData < 0) { // uncompressed speech data +			int i = 0; +			if (num >= 750) { +				num -= 750; +				i = 1; +			} +			if (!_fSpeech[i].isOpen()) { +				return; +			} +			_fSpeech[i].seek(num * 8); +			uint32 offs = _fSpeech[i].readUint32LE(); +			uint32 size = _fSpeech[i].readUint32LE(); +			if (size == 0) { +				return; +			} +			_fSpeech[i].seek(offs); +			stream = Audio::makeVOCStream(_fSpeech[i]); +		} else { +			if (num >= 750) { +				num -= 750; +				_fSpeech[0].seek(kSpeechDataFileHeaderSize); +			} else { +				assert(_flagsTable[617] > 0 && _flagsTable[617] < 140); +				_fSpeech[0].seek(kSpeechDataFileHeaderSize + _flagsTable[617] * 4); +			} +			uint32 dataOffs = _fSpeech[0].readUint32LE(); +			if (dataOffs == 0) { +				return; +			} +			_fSpeech[0].seek(dataOffs + num * 8); +			uint32 offs = _fSpeech[0].readUint32LE(); +			uint32 size = _fSpeech[0].readUint32LE(); +			if (size == 0) { +				return; +			} +			_fSpeech[0].seek(offs); +			stream = (compressedSpeechFilesTable[_compressedSpeechData].makeStream)(&_fSpeech[0], size); +		} +		if (stream) { +			_speechPlaying = true; +			_mixer->playInputStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream); +		} +	} +} + +void ToucheEngine::res_stopSpeech() { +	debugC(9, kDebugResource, "ToucheEngine::res_stopSpeech()"); +	_mixer->stopHandle(_speechHandle); +	_speechPlaying = false; +	_defaultSoundPriority = 0; +} + +} // namespace Touche diff --git a/engines/touche/saveload.cpp b/engines/touche/saveload.cpp new file mode 100644 index 0000000000..e46ec24a30 --- /dev/null +++ b/engines/touche/saveload.cpp @@ -0,0 +1,418 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 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$ + * $Id: $ + * + */ + +#include "common/stdafx.h" +#include "common/savefile.h" + +#include "touche/graphics.h" +#include "touche/touche.h" + +namespace Touche { + +enum { +	kCurrentGameStateVersion = 5, +	kGameStateDescriptionLen = 32 +}; + +template <class S, class T> +static void saveOrLoad(S &s, T &t); + +template <> +static void saveOrLoad(Common::WriteStream &stream, uint16 &i) { +	stream.writeUint16LE(i); +} + +template <> +static void saveOrLoad(Common::ReadStream &stream, uint16 &i) { +	i = stream.readUint16LE(); +} + +template <> +static void saveOrLoad(Common::WriteStream &stream, int16 &i) { +	stream.writeSint16LE(i); +} + +template <> +static void saveOrLoad(Common::ReadStream &stream, int16 &i) { +	i = stream.readSint16LE(); +} + +template <class S, class T> +static void saveOrLoadPtr(S &s, T *&t, T *base); + +template <> +static void saveOrLoadPtr(Common::WriteStream &stream, int16 *&p, int16 *base) { +	int32 offset = (int32)(p - base); +	stream.writeSint32LE(offset); +} + +template <> +static void saveOrLoadPtr(Common::ReadStream &stream, int16 *&p, int16 *base) { +	int32 offset = stream.readSint32LE(); +	p = base + offset; +} + +template <class S> +static void saveOrLoad(S &s, Common::Rect &r) { +	saveOrLoad(s, r.left); +	saveOrLoad(s, r.top); +	saveOrLoad(s, r.right); +	saveOrLoad(s, r.bottom); +} + +template <class S> +static void saveOrLoad(S &s, SequenceEntry &seq) { +	saveOrLoad(s, seq.sprNum); +	saveOrLoad(s, seq.seqNum); +} + +template <class S> +static void saveOrLoad(S &s, KeyChar &key) { +	saveOrLoad(s, key.num); +	saveOrLoad(s, key.flags); +	saveOrLoad(s, key.currentAnimCounter); +	saveOrLoad(s, key.strNum); +	saveOrLoad(s, key.walkDataNum); +	saveOrLoad(s, key.spriteNum); +	saveOrLoad(s, key.prevBoundingRect); +	saveOrLoad(s, key.boundingRect); +	saveOrLoad(s, key.xPos); +	saveOrLoad(s, key.yPos); +	saveOrLoad(s, key.zPos); +	saveOrLoad(s, key.xPosPrev); +	saveOrLoad(s, key.yPosPrev); +	saveOrLoad(s, key.zPosPrev); +	saveOrLoad(s, key.prevWalkDataNum); +	saveOrLoad(s, key.textColor); +	for (uint i = 0; i < 4; ++i) { +		saveOrLoad(s, key.inventoryItems[i]); +	} +	saveOrLoad(s, key.money); +	saveOrLoad(s, key.pointsDataNum); +	saveOrLoad(s, key.currentWalkBox); +	saveOrLoad(s, key.prevPointsDataNum); +	saveOrLoad(s, key.currentAnim); +	saveOrLoad(s, key.facingDirection); +	saveOrLoad(s, key.currentAnimSpeed); +	for (uint i = 0; i < 16; ++i) { +		saveOrLoad(s, key.framesList[i]); +	} +	saveOrLoad(s, key.framesListCount); +	saveOrLoad(s, key.currentFrame); +	saveOrLoad(s, key.anim1Start); +	saveOrLoad(s, key.anim1Count); +	saveOrLoad(s, key.anim2Start); +	saveOrLoad(s, key.anim2Count); +	saveOrLoad(s, key.anim3Start); +	saveOrLoad(s, key.anim3Count); +	saveOrLoad(s, key.followingKeyCharNum); +	saveOrLoad(s, key.followingKeyCharPos); +	saveOrLoad(s, key.sequenceDataIndex); +	saveOrLoad(s, key.sequenceDataOffset); +	saveOrLoad(s, key.walkPointsListCount); +	for (uint i = 0; i < 40; ++i) { +		saveOrLoad(s, key.walkPointsList[i]); +	} +	saveOrLoad(s, key.scriptDataStartOffset); +	saveOrLoad(s, key.scriptDataOffset); +	saveOrLoadPtr(s, key.scriptStackPtr, &key.scriptStackTable[39]); +	saveOrLoad(s, key.delay); +	saveOrLoad(s, key.waitingKeyChar); +	for (uint i = 0; i < 3; ++i) { +		saveOrLoad(s, key.waitingKeyCharPosTable[i]); +	} +	for (uint i = 0; i < 40; ++i) { +		saveOrLoad(s, key.scriptStackTable[i]); +	} +} + +template <class S> +static void saveOrLoad(S &s, TalkEntry &entry) { +	saveOrLoad(s, entry.otherKeyChar); +	saveOrLoad(s, entry.talkingKeyChar); +	saveOrLoad(s, entry.num); +} + +template <class S> +static void saveOrLoad(S &s, ProgramHitBoxData &data) { +	saveOrLoad(s, data.item); +	saveOrLoad(s, data.talk); +	saveOrLoad(s, data.state); +	saveOrLoad(s, data.str); +	saveOrLoad(s, data.defaultStr); +	for (uint i = 0; i < 8; ++i) { +		saveOrLoad(s, data.actions[i]); +	} +	for (uint i = 0; i < 2; ++i) { +		saveOrLoad(s, data.hitBoxes[i]); +	} +} + +template <class S> +static void saveOrLoad(S &s, Area &area) { +	saveOrLoad(s, area.r); +	saveOrLoad(s, area.srcX); +	saveOrLoad(s, area.srcY); +} + +template <class S> +static void saveOrLoad(S &s, ProgramBackgroundData &data) { +	saveOrLoad(s, data.area); +	saveOrLoad(s, data.type); +	saveOrLoad(s, data.offset); +	saveOrLoad(s, data.scaleMul); +	saveOrLoad(s, data.scaleDiv); +} + +template <class S> +static void saveOrLoad(S &s, ProgramAreaData &data) { +	saveOrLoad(s, data.area); +	saveOrLoad(s, data.id); +	saveOrLoad(s, data.state); +	saveOrLoad(s, data.animCount); +	saveOrLoad(s, data.animNext); +} + +template <class S> +static void saveOrLoad(S &s, ProgramWalkData &data) { +	saveOrLoad(s, data.point1); +	saveOrLoad(s, data.point2); +	saveOrLoad(s, data.clippingRect); +	saveOrLoad(s, data.area1); +	saveOrLoad(s, data.area2); +} + +template <class S> +static void saveOrLoad(S &s, ProgramPointData &data) { +	saveOrLoad(s, data.x); +	saveOrLoad(s, data.y); +	saveOrLoad(s, data.z); +	saveOrLoad(s, data.priority); +} + +void ToucheEngine::saveGameStateData(Common::WriteStream *stream) { +	setKeyCharMoney(); +	stream->writeUint16LE(_currentEpisodeNum); +	stream->writeUint16LE(_currentMusicNum); +	stream->writeUint16LE(_currentRoomNum); +	stream->writeUint16LE(_flagsTable[614]); +	stream->writeUint16LE(_flagsTable[615]); +	stream->writeUint16LE(_disabledInputCounter); +	for (uint i = 0; i < _programHitBoxTable.size(); ++i) { +		saveOrLoad(*stream, _programHitBoxTable[i]); +	} +	for (uint i = 0; i < _programBackgroundTable.size(); ++i) { +		saveOrLoad(*stream, _programBackgroundTable[i]); +	} +	for (uint i = 0; i < _programAreaTable.size(); ++i) { +		saveOrLoad(*stream, _programAreaTable[i]); +	} +	for (uint i = 0; i < _programWalkTable.size(); ++i) { +		saveOrLoad(*stream, _programWalkTable[i]); +	} +	for (uint i = 0; i < _programPointsTable.size(); ++i) { +		saveOrLoad(*stream, _programPointsTable[i]); +	} +	stream->write(_updatedRoomAreasTable, 200); +	for (uint i = 0; i < 6; ++i) { +		saveOrLoad(*stream, _sequenceEntryTable[i]); +	} +	for (uint i = 0; i < 1024; ++i) { +		saveOrLoad(*stream, _flagsTable[i]); +	} +	for (uint i = 0; i < 100; ++i) { +		saveOrLoad(*stream, _inventoryList1[i]); +	} +	for (uint i = 0; i < 100; ++i) { +		saveOrLoad(*stream, _inventoryList2[i]); +	} +	for (uint i = 0; i < 6; ++i) { +		saveOrLoad(*stream, _inventoryList3[i]); +	} +	for (uint i = 0; i < NUM_KEYCHARS; ++i) { +		saveOrLoad(*stream, _keyCharsTable[i]); +	} +	for (uint i = 0; i < NUM_INVENTORY_ITEMS; ++i) { +		saveOrLoad(*stream, _inventoryItemsInfoTable[i]); +	} +	for (uint i = 0; i < NUM_TALK_ENTRIES; ++i) { +		saveOrLoad(*stream, _talkTable[i]); +	} +	stream->writeUint16LE(_talkListEnd); +	stream->writeUint16LE(_talkListCurrent); +} + +void ToucheEngine::loadGameStateData(Common::ReadStream *stream) { +	setKeyCharMoney(); +	clearDirtyRects(); +	clearAreaTable(); +	_flagsTable[115] = 0; +	clearRoomArea(); +	int16 room_offs_x, room_offs_y; +	_currentEpisodeNum = stream->readUint16LE(); +	_currentMusicNum = stream->readUint16LE(); +	_currentRoomNum = stream->readUint16LE(); +	res_loadRoom(_currentRoomNum); +	room_offs_x = stream->readUint16LE(); +	room_offs_y = stream->readUint16LE(); +	_disabledInputCounter = stream->readUint16LE(); +	res_loadProgram(_currentEpisodeNum); +	setupEpisode(-1); +	for (uint i = 0; i < _programHitBoxTable.size(); ++i) { +		saveOrLoad(*stream, _programHitBoxTable[i]); +	} +	for (uint i = 0; i < _programBackgroundTable.size(); ++i) { +		saveOrLoad(*stream, _programBackgroundTable[i]); +	} +	for (uint i = 0; i < _programAreaTable.size(); ++i) { +		saveOrLoad(*stream, _programAreaTable[i]); +	} +	for (uint i = 0; i < _programWalkTable.size(); ++i) { +		saveOrLoad(*stream, _programWalkTable[i]); +	} +	for (uint i = 0; i < _programPointsTable.size(); ++i) { +		saveOrLoad(*stream, _programPointsTable[i]); +	} +	stream->read(_updatedRoomAreasTable, 200); +	for (uint i = 1; i <= _updatedRoomAreasTable[0]; ++i) { +		updateRoomAreas(_updatedRoomAreasTable[i], -1); +	} +	for (uint i = 0; i < 6; ++i) { +		saveOrLoad(*stream, _sequenceEntryTable[i]); +	} +	for (uint i = 0; i < 1024; ++i) { +		saveOrLoad(*stream, _flagsTable[i]); +	} +	for (uint i = 0; i < 100; ++i) { +		saveOrLoad(*stream, _inventoryList1[i]); +	} +	for (uint i = 0; i < 100; ++i) { +		saveOrLoad(*stream, _inventoryList2[i]); +	} +	for (uint i = 0; i < 6; ++i) { +		saveOrLoad(*stream, _inventoryList3[i]); +	} +	for (uint i = 0; i < NUM_KEYCHARS; ++i) { +		saveOrLoad(*stream, _keyCharsTable[i]); +	} +	for (uint i = 0; i < NUM_INVENTORY_ITEMS; ++i) { +		saveOrLoad(*stream, _inventoryItemsInfoTable[i]); +	} +	for (uint i = 0; i < NUM_TALK_ENTRIES; ++i) { +		saveOrLoad(*stream, _talkTable[i]); +	} +	_talkListEnd = stream->readUint16LE(); +	_talkListCurrent = stream->readUint16LE(); +	_flagsTable[614] = room_offs_x; +	_flagsTable[615] = room_offs_y; +	for (uint i = 0; i < 6; ++i) { +		if (_sequenceEntryTable[i].seqNum != -1) { +			res_loadSequence(_sequenceEntryTable[i].seqNum, i); +		} +		if (_sequenceEntryTable[i].sprNum != -1) { +			res_loadSprite(_sequenceEntryTable[i].sprNum, i); +		} +	} +	_currentKeyCharNum = _flagsTable[104]; +	_inventoryListCount[0] = 0; +	_inventoryListCount[3] = 0; +	_inventoryListCount[6] = 0; +	drawInventory(_currentKeyCharNum, 1); +	Graphics::copyRect(_offscreenBuffer, 640, 0, 0, _backdropBuffer, _currentBitmapWidth, _flagsTable[614], _flagsTable[615], 640, 352); +	updateEntireScreen(); +	if (_flagsTable[617] != 0) { +		res_loadSpeech(_flagsTable[617]); +	} +} + +bool ToucheEngine::saveGameState(int num, const char *description) { +	bool saveOk = false; +	char gameStateFileName[16]; +	generateGameStateFileName(num, gameStateFileName, 15); +	Common::OutSaveFile *f = _saveFileMan->openForSaving(gameStateFileName); +	if (f) { +		f->writeUint16LE(kCurrentGameStateVersion); +		f->writeUint16LE(0); +		char headerDescription[kGameStateDescriptionLen]; +		memset(headerDescription, 0, kGameStateDescriptionLen); +		strncpy(headerDescription, description, kGameStateDescriptionLen - 1); +		f->write(headerDescription, kGameStateDescriptionLen); +		saveGameStateData(f); +		f->flush(); +		if (!f->ioFailed()) { +			saveOk = true; +		} else { +			warning("Can't write file '%s'", gameStateFileName); +		} +		delete f; +	} +	return saveOk; +} + +bool ToucheEngine::loadGameState(int num, const char *description) { +	bool loadOk = false; +	char gameStateFileName[16]; +	generateGameStateFileName(num, gameStateFileName, 15); +	Common::InSaveFile *f = _saveFileMan->openForLoading(gameStateFileName); +	if (f) { +		uint16 version = f->readUint16LE(); +		if (version < kCurrentGameStateVersion) { +			warning("Unsupported gamestate version %d\n", version); +		} else { +			f->skip(2 + kGameStateDescriptionLen); +			loadGameStateData(f); +			if (!f->ioFailed()) { +				loadOk = true; +			} else { +				warning("Can't read file '%s'", gameStateFileName); +			} +		} +		delete f; +	} +	return loadOk; +} + +void ToucheEngine::readGameStateDescription(int num, char *description, int len) { +	char gameStateFileName[16]; +	generateGameStateFileName(num, gameStateFileName, 15); +	Common::InSaveFile *f = _saveFileMan->openForLoading(gameStateFileName); +	if (f) { +		uint16 version = f->readUint16LE(); +		if (version >= kCurrentGameStateVersion) { +			f->readUint16LE(); +			f->read(description, MIN<int>(len, kGameStateDescriptionLen)); +			description[len] = 0; +		} +		delete f; +	} +} + +void ToucheEngine::generateGameStateFileName(int num, char *dst, int len, bool prefixOnly) const { +	if (prefixOnly) { +		snprintf(dst, len, "%s.", _targetName.c_str()); +	} else { +		snprintf(dst, len, "%s.%d", _targetName.c_str(), num); +	} +	dst[len] = 0; +} + +} // namespace Touche diff --git a/engines/touche/staticres.cpp b/engines/touche/staticres.cpp new file mode 100644 index 0000000000..db3b256c00 --- /dev/null +++ b/engines/touche/staticres.cpp @@ -0,0 +1,659 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 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$ + * $Id: $ + * + */ + +#include "common/stdafx.h" + +#include "touche/graphics.h" +#include "touche/touche.h" + +namespace Touche { + +ToucheEngine::OpcodeProc ToucheEngine::_opcodesTable[NUM_OPCODES] = { +	/* 0x00 */ +	&ToucheEngine::op_nop, +	&ToucheEngine::op_jnz, +	&ToucheEngine::op_jz, +	&ToucheEngine::op_jmp, +	/* 0x04 */ +	&ToucheEngine::op_true, +	&ToucheEngine::op_false, +	&ToucheEngine::op_push, +	&ToucheEngine::op_testFalse, +	/* 0x08 */ +	&ToucheEngine::op_add, +	&ToucheEngine::op_sub, +	&ToucheEngine::op_mul, +	&ToucheEngine::op_div, +	/* 0x0C */ +	&ToucheEngine::op_mod, +	&ToucheEngine::op_and, +	&ToucheEngine::op_or, +	&ToucheEngine::op_not, +	/* 0x10 */ +	&ToucheEngine::op_testGreater, +	&ToucheEngine::op_testEquals, +	&ToucheEngine::op_testLower, +	&ToucheEngine::op_fetchScriptWord, +	/* 0x14 */ +	0, +	0, +	0, +	0, +	/* 0x18 */ +	&ToucheEngine::op_testGreaterOrEquals, +	&ToucheEngine::op_testLowerOrEquals, +	&ToucheEngine::op_testNotEquals, +	&ToucheEngine::op_endConversation, +	/* 0x1C */ +	&ToucheEngine::op_stopScript, +	&ToucheEngine::op_getFlag, +	&ToucheEngine::op_setFlag, +	0, +	/* 0x20 */ +	0, +	0, +	0, +	&ToucheEngine::op_fetchScriptByte, +	/* 0x24 */ +	0, +	0, +	0, +	0, +	/* 0x28 */ +	&ToucheEngine::op_getScriptValue, +	&ToucheEngine::op_setScriptValue, +	0, +	0, +	/* 0x2C */ +	0, +	0, +	&ToucheEngine::op_getKeyCharWalkBox, +	&ToucheEngine::op_startSound, +	/* 0x30 */ +	&ToucheEngine::op_initKeyCharTalk, +	0, +	0, +	0, +	/* 0x34 */ +	&ToucheEngine::op_loadRoom, +	&ToucheEngine::op_updateRoom, +	&ToucheEngine::op_startTalk, +	&ToucheEngine::op_setKeyCharBox, +	/* 0x38 */ +	&ToucheEngine::op_initKeyCharScript, +	&ToucheEngine::op_loadSprite, +	&ToucheEngine::op_loadSequence, +	&ToucheEngine::op_setKeyCharFrame, +	/* 0x3C */ +	&ToucheEngine::op_setKeyCharDirection, +	&ToucheEngine::op_clearConversationChoices, +	&ToucheEngine::op_addConversationChoice, +	&ToucheEngine::op_removeConversationChoice, +	/* 0x40 */ +	&ToucheEngine::op_getInventoryItem, +	&ToucheEngine::op_setInventoryItem, +	&ToucheEngine::op_startEpisode, +	&ToucheEngine::op_setConversationNum, +	/* 0x44 */ +	&ToucheEngine::op_enableInventoryItem, +	&ToucheEngine::op_enableInput, +	&ToucheEngine::op_disableInput, +	&ToucheEngine::op_faceKeyChar, +	/* 0x48 */ +	&ToucheEngine::op_getKeyCharCurrentAnim, +	&ToucheEngine::op_getCurrentKeyChar, +	&ToucheEngine::op_isKeyCharActive, +	&ToucheEngine::op_setPalette, +	/* 0x4C */ +	&ToucheEngine::op_changeWalkPath, +	&ToucheEngine::op_lockWalkPath, +	&ToucheEngine::op_initializeKeyChar, +	&ToucheEngine::op_setupWaitingKeyChars, +	/* 0x50 */ +	&ToucheEngine::op_updateRoomAreas, +	&ToucheEngine::op_unlockWalkPath, +	0, +	&ToucheEngine::op_addItemToInventoryAndRedraw, +	/* 0x54 */ +	&ToucheEngine::op_giveItemTo, +	&ToucheEngine::op_resetHitBoxes, +	&ToucheEngine::op_fadePalette, +	0, +	/* 0x58 */ +	&ToucheEngine::op_disableInventoryItem, +	0, +	0, +	0, +	/* 0x5C */ +	0, +	0, +	0, +	0, +	/* 0x60 */ +	0, +	&ToucheEngine::op_getInventoryItemFlags, +	&ToucheEngine::op_drawInventory, +	&ToucheEngine::op_stopKeyCharScript, +	/* 0x64 */ +	&ToucheEngine::op_restartKeyCharScript, +	&ToucheEngine::op_getKeyCharCurrentWalkBox, +	&ToucheEngine::op_getKeyCharPointsDataNum, +	&ToucheEngine::op_setupFollowingKeyChar, +	/* 0x68 */ +	&ToucheEngine::op_startAnimation, +	&ToucheEngine::op_setKeyCharTextColor, +	0, +	0, +	/* 0x6C */ +	0, +	0, +	0, +	0, +	/* 0x70 */ +	&ToucheEngine::op_startMusic, +	0, +	0, +	&ToucheEngine::op_copyPaletteColor, +	/* 0x74 */ +	&ToucheEngine::op_delay, +	&ToucheEngine::op_lockHitBox, +	&ToucheEngine::op_removeItemFromInventory, +	&ToucheEngine::op_unlockHitBox, +	/* 0x78 */ +	&ToucheEngine::op_addRoomArea, +	&ToucheEngine::op_setKeyCharFlags, +	0, +	0, +	/* 0x7C */ +	0, +	0, +	0, +	0, +	/* 0x80 */ +	&ToucheEngine::op_unsetKeyCharFlags, +	&ToucheEngine::op_drawSpriteOnBackdrop, +	&ToucheEngine::op_loadVoice, +	0, +	/* 0x84 */ +	&ToucheEngine::op_startPaletteFadeIn, +	&ToucheEngine::op_startPaletteFadeOut, +	&ToucheEngine::op_setRoomAreaState +}; + +SpriteData ToucheEngine::_spritesTable[NUM_SPRITES] = { +	{ 0x34BC0, 0, 0, 0, 0, 0 }, +	{ 0x1E848, 0, 0, 0, 0, 0 }, +	{ 0x1E848, 0, 0, 0, 0, 0 }, +	{ 0x23A50, 0, 0, 0, 0, 0 }, +	{ 0x1E848, 0, 0, 0, 0, 0 } +}; + +const uint8 ToucheEngine::_directionsTable[] = { +	0x7F, 0x7F, 0x7F, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7F, +	0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, +	0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, +	0x7F, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, +	0x7F, 0x7F, 0x7F, 0x7F, 0x02, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, +	0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, +	0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x02, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x02, +	0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, +	0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x00, 0x00 +}; + +char ToucheEngine::_saveLoadDescriptionsTable[10][33] = { +	"[Empty.1.......................]", +	"[Empty.2.......................]", +	"[Empty.3.......................]", +	"[Empty.4.......................]", +	"[Empty.5.......................]", +	"[Empty.6.......................]", +	"[Empty.7.......................]", +	"[Empty.8.......................]", +	"[Empty.9.......................]" +}; + +const Common::Rect ToucheEngine::_inventoryAreasTable[13] = { +	Common::Rect(  0, 354,  50, 400), +	Common::Rect( 66, 354, 124, 380), +	Common::Rect( 74, 380, 116, 398), +	Common::Rect(116, 380, 158, 398), +	Common::Rect(144, 354, 198, 380), +	Common::Rect(202, 354, 238, 396), +	Common::Rect(242, 354, 300, 396), +	Common::Rect(300, 354, 358, 396), +	Common::Rect(358, 354, 416, 396), +	Common::Rect(416, 354, 474, 396), +	Common::Rect(474, 354, 532, 396), +	Common::Rect(532, 354, 590, 396), +	Common::Rect(594, 354, 640, 395) +}; + +const uint16 Graphics::_fontOffs[] = { +	0x0000, 0x0007, 0x0024, 0x0043, 0x0072, 0x00AD, 0x00E0, 0x0113, 0x0124, 0x0141, +	0x015E, 0x0191, 0x01C4, 0x01E3, 0x01F8, 0x0215, 0x0232, 0x0269, 0x0286, 0x02BD, +	0x02F4, 0x032B, 0x0362, 0x0399, 0x03D0, 0x0407, 0x043E, 0x045B, 0x047C, 0x0495, +	0x04C0, 0x04D9, 0x0510, 0x054B, 0x0582, 0x05B9, 0x05F0, 0x0627, 0x065E, 0x0695, +	0x06CC, 0x0703, 0x0720, 0x0757, 0x078E, 0x07C5, 0x07FC, 0x0833, 0x086A, 0x08A1, +	0x08D8, 0x090F, 0x0946, 0x097D, 0x09B4, 0x09EB, 0x0A3C, 0x0A73, 0x0AAA, 0x0AE1, +	0x0B00, 0x0B1D, 0x0B3C, 0x0B77, 0x0BAA, 0x0BBB, 0x0BF2, 0x0C29, 0x0C60, 0x0C97, +	0x0CCE, 0x0CEB, 0x0D2E, 0x0D65, 0x0D82, 0x0DA5, 0x0DDC, 0x0DF9, 0x0E30, 0x0E67, +	0x0E9E, 0x0EE1, 0x0F24, 0x0F41, 0x0F78, 0x0F95, 0x0FCC, 0x1003, 0x103A, 0x1071, +	0x10B4, 0x10EB, 0x110A, 0x112B, 0x114A, 0x116D, 0x1178, 0x11BB, 0x11F2, 0x1229, +	0x1260, 0x1297, 0x12CE, 0x1305, 0x1348, 0x137F, 0x13B6, 0x13ED, 0x1424, 0x1441, +	0x145E, 0x149D, 0x14A8, 0x14B3, 0x14EA, 0x14F5, 0x152C, 0x1563, 0x159A, 0x15D1, +	0x1608, 0x1613, 0x161E, 0x1629, 0x1634, 0x163F, 0x164A, 0x1655, 0x1660, 0x1697, +	0x16B4, 0x16EB, 0x1722, 0x1759, 0x1764, 0x176F, 0x177A, 0x17B1, 0x17BC, 0x17C7, +	0x17D2, 0x17DD +}; + +const uint8 Graphics::_fontData[] = { +	0x01, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0D, 0x05, 0x3C, 0x03, 0xD7, 0x0D, 0xD7, 0xCD, +	0xD7, 0xCD, 0xD7, 0xCD, 0xD7, 0xC3, 0xD7, 0xC0, 0xD7, 0xC0, 0x3F, 0xC0, 0xD7, 0x00, 0xD7, 0xC0, +	0x3F, 0xC0, 0x0F, 0x00, 0x02, 0x07, 0x09, 0x3C, 0x3C, 0x00, 0x0C, 0xD7, 0xD7, 0x00, 0x37, 0xD7, +	0xD7, 0xC0, 0xF7, 0xD7, 0xD7, 0xC3, 0x55, 0xD7, 0xD7, 0xC0, 0xDF, 0x3F, 0xFF, 0xC3, 0xDF, 0x0F, +	0x0F, 0x0D, 0x55, 0x02, 0x0B, 0x0B, 0x00, 0xC3, 0x00, 0x03, 0x03, 0x7D, 0xC0, 0x0D, 0x0F, 0x7D, +	0xF0, 0x0D, 0x35, 0x55, 0x70, 0x35, 0x0D, 0xF7, 0xFC, 0xDD, 0x3D, 0xF7, 0xF0, 0xDD, 0xD5, 0x55, +	0xC0, 0x35, 0x37, 0xDF, 0xF0, 0x3D, 0x37, 0xDF, 0xC0, 0xDD, 0x0F, 0xFF, 0x00, 0x35, 0x03, 0x0C, +	0x00, 0x0D, 0x02, 0x0E, 0x09, 0x03, 0x00, 0x00, 0xF0, 0x0D, 0xC0, 0x03, 0x5C, 0x0D, 0xF0, 0x0D, +	0xF7, 0x35, 0x5C, 0x0D, 0xF7, 0xDD, 0xF7, 0x03, 0x5F, 0xDD, 0xFF, 0xC0, 0xFD, 0x35, 0x5F, 0x00, +	0x37, 0x3D, 0xF7, 0x00, 0xDF, 0xDD, 0xF7, 0xC3, 0x7F, 0x35, 0x5F, 0xC0, 0xFC, 0x0D, 0xFF, 0x00, +	0x30, 0x0D, 0xFC, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x02, 0x0C, 0x0C, +	0x0F, 0x00, 0x00, 0x00, 0x35, 0xC0, 0xC0, 0x00, 0xDF, 0x73, 0x70, 0x03, 0xDF, 0x7D, 0xFC, 0x0D, +	0x35, 0xF7, 0xF0, 0x37, 0x0F, 0xDF, 0xC0, 0x37, 0x03, 0x7D, 0x70, 0x0D, 0x0D, 0xF7, 0xDC, 0x37, +	0x37, 0xF7, 0xDF, 0x37, 0x0F, 0xCD, 0x7F, 0x0D, 0x03, 0x03, 0xFC, 0x03, 0x00, 0x00, 0xF0, 0x00, +	0x02, 0x0C, 0x0B, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xD7, 0x0F, 0xC0, 0x00, 0xD7, 0x35, +	0x70, 0x00, 0xD7, 0xDF, 0xDC, 0x00, 0xD7, 0xDF, 0xFF, 0xC0, 0xFF, 0x35, 0x7D, 0x70, 0x3F, 0xDF, +	0xF7, 0xFC, 0x00, 0xDF, 0xDF, 0xF0, 0x00, 0x35, 0x7F, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x03, +	0xF0, 0x00, 0x00, 0x01, 0x07, 0x05, 0x3C, 0x00, 0xD7, 0x00, 0xD7, 0xC3, 0xD7, 0xC3, 0xD7, 0xCD, +	0xFF, 0xCD, 0x3F, 0x0D, 0x01, 0x0D, 0x06, 0x03, 0x00, 0x0D, 0xC3, 0x37, 0xF0, 0x37, 0xC0, 0xDF, +	0xC0, 0xDF, 0x00, 0xDF, 0x00, 0xDF, 0x00, 0x37, 0x00, 0x37, 0xC0, 0x0D, 0xC3, 0x03, 0xF0, 0x00, +	0xC0, 0x01, 0x0D, 0x06, 0x30, 0x00, 0xDC, 0x00, 0x37, 0x00, 0x37, 0xC0, 0x0D, 0xC0, 0x0D, 0xF0, +	0x0D, 0xF3, 0x0D, 0xF0, 0x37, 0xF0, 0x37, 0xC0, 0xDF, 0xC0, 0x3F, 0x00, 0x0C, 0x00, 0x02, 0x0C, +	0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x03, 0x0F, 0x7C, 0x00, +	0x0D, 0x37, 0x77, 0x00, 0x0D, 0x3D, 0x5F, 0xC0, 0xFD, 0xD5, 0xD5, 0xC3, 0x55, 0x3D, 0x5F, 0xF0, +	0xFD, 0x37, 0x77, 0xC0, 0x3D, 0x0F, 0x7F, 0xC0, 0x0D, 0x03, 0xFF, 0x00, 0x03, 0x00, 0x30, 0x00, +	0x00, 0x02, 0x0C, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, +	0x03, 0x70, 0x00, 0x00, 0x03, 0x7C, 0x00, 0x00, 0x3F, 0x7F, 0x00, 0x00, 0xD5, 0x55, 0xC0, 0x00, +	0x3F, 0x7F, 0xF0, 0x00, 0x0F, 0x7F, 0xC0, 0x3C, 0x03, 0x7C, 0x00, 0xD7, 0x00, 0xFC, 0x00, 0xD7, +	0x00, 0x30, 0x03, 0x5F, 0x01, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0F, 0x00, 0x35, 0xC0, 0x35, 0xF0, 0xD7, 0xF0, 0x3F, +	0xC0, 0x0F, 0x00, 0x01, 0x09, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x3F, 0xC0, 0xD5, 0x70, 0x3F, 0xFC, 0x0F, 0xF0, 0x01, 0x0D, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3C, 0x03, 0xD7, 0x0D, 0xD7, +	0xCD, 0x3F, 0xC3, 0x0F, 0x00, 0x01, 0x0D, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0xDC, 0x03, 0x7F, +	0x03, 0x7C, 0x0D, 0xFC, 0x0D, 0xF0, 0x37, 0xF0, 0x37, 0xC0, 0xDF, 0xC0, 0xDF, 0x00, 0x3F, 0x00, +	0x0C, 0x00, 0x02, 0x0D, 0x09, 0x0F, 0xF0, 0x00, 0x0F, 0x35, 0x5C, 0x00, 0xF5, 0xD7, 0xD7, 0x03, +	0x55, 0xD7, 0xD7, 0xC0, 0xF5, 0xD7, 0xD7, 0xC0, 0x35, 0xD7, 0xD7, 0xC0, 0x35, 0xD7, 0xD7, 0xC0, +	0x35, 0xD7, 0xD7, 0xC0, 0x35, 0xD7, 0xD7, 0xC0, 0x35, 0xD7, 0xD7, 0xC0, 0x35, 0x35, 0x5F, 0xC0, +	0x35, 0x0F, 0xFF, 0x00, 0x0F, 0x03, 0xFC, 0x00, 0x03, 0x01, 0x0D, 0x07, 0x03, 0xC0, 0x3D, 0x70, +	0xD5, 0x7C, 0x3D, 0x7C, 0x0D, 0x7C, 0x0D, 0x7C, 0x0D, 0x7C, 0x0D, 0x7C, 0x0D, 0x7C, 0x0D, 0x7C, +	0x0D, 0x7C, 0x03, 0xFC, 0x00, 0xF0, 0x02, 0x0D, 0x09, 0x0F, 0xF0, 0x00, 0x0F, 0x35, 0x5C, 0x00, +	0x35, 0xD7, 0xD7, 0x00, 0xD7, 0xD7, 0xD7, 0xC0, 0x3F, 0x3F, 0xD7, 0xC0, 0x0F, 0x0F, 0x5F, 0xC0, +	0x0D, 0x0D, 0x7F, 0x00, 0x03, 0x35, 0xFC, 0x00, 0x00, 0xD7, 0xF0, 0x00, 0x3C, 0xD7, 0xFC, 0x00, +	0xD7, 0xD5, 0x57, 0x00, 0x35, 0x3F, 0xFF, 0xC0, 0x0F, 0x0F, 0xFF, 0x00, 0x03, 0x02, 0x0D, 0x09, +	0x0F, 0xF0, 0x00, 0x00, 0x35, 0x5C, 0x00, 0x00, 0xD7, 0xD7, 0x00, 0x03, 0x3F, 0xD7, 0xC0, 0x0D, +	0x0F, 0xD7, 0xC0, 0x0D, 0x0D, 0x5F, 0xC0, 0x35, 0x03, 0xD7, 0x00, 0x35, 0x00, 0xD7, 0xC0, 0xD7, +	0x3C, 0xD7, 0xC0, 0xD5, 0xD7, 0xD7, 0xC0, 0x3F, 0x35, 0x5F, 0xC0, 0x0F, 0x0F, 0xFF, 0x00, 0x00, +	0x03, 0xFC, 0x00, 0x00, 0x02, 0x0D, 0x09, 0x00, 0x3C, 0x00, 0x3F, 0x00, 0xD7, 0x00, 0xD5, 0x03, +	0x57, 0xC0, 0xD7, 0x0D, 0x57, 0xC0, 0xD7, 0x0D, 0x57, 0xC0, 0xD7, 0x35, 0xD7, 0xC0, 0xD5, 0x35, +	0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0x3F, 0xD5, 0x57, 0xC0, 0x3F, 0x3F, 0xD7, 0xC0, 0xD7, 0x0F, +	0xD7, 0xC0, 0x35, 0x00, 0x3F, 0xC0, 0x0F, 0x00, 0x0F, 0x00, 0x03, 0x02, 0x0D, 0x09, 0x3F, 0xFC, +	0x00, 0x0F, 0xD5, 0x57, 0x00, 0x35, 0xD7, 0xFF, 0xC0, 0xD7, 0xD7, 0xFF, 0x00, 0xD7, 0xD7, 0xF0, +	0x00, 0xD7, 0xD5, 0x5C, 0x00, 0xD5, 0xD7, 0xD7, 0x00, 0xD7, 0x3F, 0xD7, 0xC0, 0xD7, 0x3F, 0xD7, +	0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0x35, 0x5F, 0xC0, 0x35, 0x0F, 0xFF, 0x00, 0x0F, 0x03, 0xFC, +	0x00, 0x03, 0x02, 0x0D, 0x09, 0x0F, 0xF0, 0x00, 0x3F, 0x35, 0x5C, 0x00, 0xD5, 0xD7, 0xD7, 0x00, +	0x3F, 0xD7, 0xFF, 0xC0, 0x0F, 0xD7, 0xFF, 0x00, 0x03, 0xD5, 0x5C, 0x00, 0x0D, 0xD7, 0xD7, 0x00, +	0x0D, 0xD7, 0xD7, 0xC0, 0x0D, 0xD7, 0xD7, 0xC0, 0x35, 0xD7, 0xD7, 0xC0, 0x35, 0x35, 0x5F, 0xC0, +	0x35, 0x0F, 0xFF, 0x00, 0x0F, 0x03, 0xFC, 0x00, 0x03, 0x02, 0x0D, 0x09, 0x3F, 0xFC, 0x00, 0x0F, +	0xD5, 0x57, 0x00, 0x35, 0x3F, 0xD7, 0xC0, 0xD7, 0x0F, 0x5F, 0xC0, 0xD7, 0x03, 0x5F, 0x00, 0xD7, +	0x0D, 0x7F, 0x00, 0x35, 0x0D, 0x7C, 0x00, 0xD7, 0x0D, 0x7C, 0x00, 0xD7, 0x35, 0xFC, 0x00, 0xD7, +	0x35, 0xF0, 0x00, 0xD7, 0x35, 0xF0, 0x00, 0x35, 0x0F, 0xF0, 0x00, 0x0F, 0x03, 0xC0, 0x00, 0x03, +	0x02, 0x0D, 0x09, 0x0F, 0xF0, 0x00, 0x0F, 0x35, 0x5C, 0x00, 0x35, 0xD7, 0xD7, 0x00, 0xD7, 0xD7, +	0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0x35, 0x5F, 0xC0, 0xD7, 0xD7, 0xD7, 0x00, 0x35, 0xD7, +	0xD7, 0xC0, 0x0F, 0xD7, 0xD7, 0xC0, 0x3F, 0xD7, 0xD7, 0xC0, 0xD7, 0x35, 0x5F, 0xC0, 0x35, 0x0F, +	0xFF, 0x00, 0x0F, 0x03, 0xFC, 0x00, 0x03, 0x02, 0x0D, 0x09, 0x0F, 0xF0, 0x00, 0x00, 0x35, 0x5C, +	0x00, 0x00, 0xD7, 0xD7, 0x00, 0x00, 0xD7, 0xD7, 0xC0, 0x00, 0xD7, 0xD7, 0xC0, 0xF0, 0xD7, 0xD7, +	0xC3, 0x5C, 0x35, 0x57, 0xC3, 0x5F, 0x0F, 0xD7, 0xC0, 0xFF, 0x3F, 0xD7, 0xC0, 0xFC, 0xD7, 0xD7, +	0xC3, 0x5C, 0x35, 0x5F, 0xC3, 0x5F, 0x0F, 0xFF, 0x00, 0xFF, 0x03, 0xFC, 0x00, 0x3C, 0x01, 0x0D, +	0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0xD7, 0x03, 0xD7, 0xC3, 0x3F, +	0xC0, 0x3F, 0x00, 0xD7, 0x03, 0xD7, 0xC3, 0x3F, 0xC0, 0x0F, 0x03, 0x01, 0x0F, 0x05, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0xD7, 0x00, 0xD7, 0xC0, 0x3F, 0xC0, 0x3F, 0x00, +	0xD7, 0x00, 0xD7, 0xC0, 0x37, 0xC0, 0xDF, 0xC0, 0x3F, 0x00, 0x0C, 0x00, 0x01, 0x0B, 0x07, 0x00, +	0x00, 0x00, 0xC0, 0x03, 0x70, 0x0D, 0xFC, 0x37, 0xF0, 0xDF, 0xC0, 0x37, 0x00, 0x0D, 0xC0, 0x03, +	0x70, 0x00, 0xFC, 0x00, 0x30, 0x02, 0x0A, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, +	0x00, 0x00, 0x03, 0x70, 0x3F, 0xFC, 0x00, 0xDC, 0xD5, 0x57, 0x00, 0x37, 0x3F, 0xFF, 0xC0, 0x0D, +	0x3F, 0xFF, 0x00, 0x37, 0xD5, 0x57, 0x00, 0xDF, 0x3F, 0xFF, 0xC3, 0x7F, 0x0F, 0xFF, 0x00, 0xFC, +	0x01, 0x0B, 0x07, 0x00, 0x00, 0x30, 0x00, 0xDC, 0x00, 0x37, 0x00, 0x0D, 0xC0, 0x03, 0x70, 0x0D, +	0xFC, 0x37, 0xF0, 0xDF, 0xC0, 0x3F, 0x00, 0x0C, 0x00, 0x02, 0x0D, 0x09, 0x0F, 0xF0, 0x00, 0x00, +	0x35, 0x5C, 0x00, 0x00, 0xD7, 0xD7, 0x00, 0x00, 0xD7, 0xD7, 0xC0, 0x00, 0x3F, 0xD7, 0xC0, 0x00, +	0x0F, 0x5F, 0xC0, 0x00, 0x0D, 0x7F, 0x00, 0x00, 0x0D, 0x7C, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, +	0x0D, 0x70, 0x00, 0x00, 0x0D, 0x7C, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, +	0x02, 0x0E, 0x0D, 0x03, 0xFF, 0xC0, 0x00, 0x0D, 0x55, 0x70, 0x00, 0x37, 0xFF, 0xDC, 0x00, 0xDF, +	0xFF, 0xF7, 0x00, 0xDF, 0xD5, 0xF7, 0xC0, 0xDF, 0x7D, 0xF7, 0xC0, 0xDF, 0x7D, 0xF7, 0xC0, 0xDF, +	0x7D, 0xF7, 0xC0, 0xDF, 0x75, 0xDF, 0xC0, 0xDF, 0xDD, 0x7F, 0x00, 0x37, 0xFF, 0xFC, 0x00, 0x0D, +	0x5C, 0xF0, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x02, 0x0D, 0x0B, 0x00, 0xF0, +	0x00, 0x3F, 0x03, 0x5C, 0x00, 0xD5, 0x03, 0x5F, 0x00, 0xD7, 0x0D, 0x57, 0x00, 0xD7, 0x0D, 0x57, +	0xC0, 0xD7, 0x0D, 0xF7, 0xC0, 0xD5, 0x35, 0xF5, 0xC0, 0xD7, 0x35, 0xF5, 0xF0, 0xD7, 0x35, 0x55, +	0xF0, 0xD7, 0xD7, 0xFD, 0x70, 0xD7, 0xD7, 0xFD, 0x7C, 0xD5, 0x3F, 0xC3, 0xFC, 0x3F, 0x0F, 0x00, +	0xF0, 0x0F, 0x02, 0x0D, 0x0B, 0x3F, 0xFF, 0x00, 0x03, 0xD5, 0x55, 0xC0, 0x0D, 0xD7, 0xFD, 0x70, +	0x35, 0xD7, 0xFD, 0x7C, 0xD7, 0xD7, 0xFD, 0x7C, 0xD7, 0xD5, 0x55, 0xFC, 0xD7, 0xD7, 0xFD, 0x70, +	0xD7, 0xD7, 0xFD, 0x7C, 0xD7, 0xD7, 0xCD, 0x7C, 0xD7, 0xD7, 0xFD, 0x7C, 0x35, 0xD5, 0x55, 0xFC, +	0x0D, 0x3F, 0xFF, 0xF0, 0x03, 0x0F, 0xFF, 0xC0, 0x00, 0x02, 0x0D, 0x0A, 0x03, 0xFC, 0x00, 0x0F, +	0x0D, 0x57, 0x00, 0x35, 0x35, 0xF5, 0xC0, 0x35, 0xD7, 0xFD, 0xF0, 0x35, 0xD7, 0xC3, 0xF0, 0x35, +	0xD7, 0xC0, 0xC0, 0x35, 0xD7, 0xC0, 0x00, 0x35, 0xD7, 0xC3, 0x00, 0x35, 0xD7, 0xCD, 0xC0, 0x35, +	0x35, 0xF5, 0xF0, 0x35, 0x0D, 0x57, 0xF0, 0x35, 0x03, 0xFF, 0xC0, 0x0F, 0x00, 0xFF, 0x00, 0x03, +	0x02, 0x0D, 0x0B, 0x3F, 0xFC, 0x00, 0x03, 0xD5, 0x57, 0x00, 0x0D, 0xD7, 0xF5, 0xC0, 0x0D, 0xD7, +	0xFD, 0x70, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0xD7, +	0xCD, 0x7C, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0xD7, 0xF5, 0xFC, 0x0D, 0xD5, 0x57, 0xF0, 0x0D, 0x3F, +	0xFF, 0xC0, 0x03, 0x0F, 0xFF, 0x00, 0x00, 0x02, 0x0D, 0x0A, 0x3F, 0xFF, 0x00, 0x0F, 0xD5, 0x55, +	0xC0, 0x35, 0xD7, 0xFF, 0xF0, 0x35, 0xD7, 0xFF, 0xC0, 0x35, 0xD7, 0xFC, 0x00, 0x35, 0xD5, 0x57, +	0x00, 0x35, 0xD7, 0xFF, 0xC0, 0x35, 0xD7, 0xFF, 0x00, 0x35, 0xD7, 0xC0, 0x00, 0x35, 0xD7, 0xFF, +	0x00, 0x35, 0xD5, 0x55, 0xC0, 0x35, 0x3F, 0xFF, 0xF0, 0x0F, 0x0F, 0xFF, 0xC0, 0x03, 0x02, 0x0D, +	0x0A, 0x3F, 0xFF, 0x00, 0x03, 0xD5, 0x55, 0xC0, 0x0D, 0xD7, 0xFF, 0xF0, 0x35, 0xD7, 0xFF, 0xC0, +	0xD7, 0xD7, 0xFC, 0x00, 0xD7, 0xD5, 0x57, 0x00, 0xD7, 0xD7, 0xFF, 0xC0, 0xD7, 0xD7, 0xFF, 0x00, +	0xD7, 0xD7, 0xC0, 0x00, 0xD7, 0xD7, 0xC0, 0x00, 0x35, 0xD7, 0xC0, 0x00, 0x0D, 0x3F, 0xC0, 0x00, +	0x03, 0x0F, 0x00, 0x00, 0x00, 0x02, 0x0D, 0x0B, 0x03, 0xFF, 0x00, 0x03, 0x0D, 0x55, 0xC0, 0x0D, +	0x35, 0xFD, 0x70, 0x0D, 0xD7, 0xFF, 0x7C, 0x0D, 0xD7, 0xC0, 0xFC, 0x0D, 0xD7, 0xFF, 0xF0, 0x0D, +	0xD7, 0xD5, 0x70, 0x0D, 0xD7, 0xFD, 0x7C, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0x35, 0xFD, 0x7C, 0x0D, +	0x0D, 0x57, 0x7C, 0x0D, 0x03, 0xFF, 0xFC, 0x03, 0x00, 0xFF, 0x30, 0x00, 0x02, 0x0D, 0x0B, 0x3C, +	0x03, 0xC0, 0x03, 0xD7, 0x0D, 0x70, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0xD7, +	0xFD, 0x7C, 0x0D, 0xD5, 0x55, 0x7C, 0x0D, 0xD7, 0xFD, 0x7C, 0x0D, 0xD7, 0xFD, 0x7C, 0x0D, 0xD7, +	0xCD, 0x7C, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0x3F, 0xC3, 0xFC, 0x03, 0x0F, +	0x00, 0xF0, 0x00, 0x01, 0x0D, 0x05, 0x3C, 0x00, 0xD7, 0x00, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, +	0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC3, 0xD7, 0xC3, 0xD7, 0xC0, 0x3F, 0xC0, 0x0F, 0x00, +	0x02, 0x0D, 0x09, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0xD7, 0x00, 0xD7, 0x00, 0xD7, 0xC0, 0xD7, 0x00, +	0xD7, 0xC0, 0xD7, 0x00, 0xD7, 0xC0, 0xD5, 0x00, 0xD7, 0xC0, 0xD5, 0x00, 0xD7, 0xC0, 0xD5, 0x3C, +	0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0x35, 0x5F, 0xC0, 0xD7, 0x0F, +	0xFF, 0x00, 0x3F, 0x03, 0xFC, 0x00, 0x0F, 0x02, 0x0D, 0x0B, 0x3C, 0x0F, 0x00, 0x0F, 0xD7, 0x35, +	0xC0, 0x35, 0xD7, 0xD7, 0xF0, 0x35, 0xD7, 0x5F, 0xC0, 0x35, 0xD5, 0x7F, 0x00, 0x35, 0xD5, 0xFC, +	0x00, 0x35, 0xD5, 0x70, 0x00, 0x35, 0xD7, 0x5C, 0x00, 0x35, 0xD7, 0xD7, 0x00, 0x35, 0xD7, 0xF5, +	0xC0, 0x35, 0xD7, 0xCD, 0x70, 0x35, 0x3F, 0xC3, 0xFC, 0x0F, 0x0F, 0x00, 0xF0, 0x03, 0x02, 0x0D, +	0x0A, 0x3C, 0x00, 0x00, 0x3C, 0xD7, 0x00, 0x00, 0xD7, 0xD7, 0xC0, 0x00, 0xD7, 0xD7, 0xC0, 0x00, +	0xD5, 0xD7, 0xC0, 0x00, 0xD5, 0xD7, 0xC0, 0x00, 0xD5, 0xD7, 0xC0, 0x00, 0xD5, 0xD7, 0xC0, 0x00, +	0xD7, 0xD7, 0xC0, 0x00, 0xD7, 0xD7, 0xFF, 0x00, 0xD7, 0xD5, 0x55, 0xC0, 0xD7, 0x3F, 0xFF, 0xF0, +	0x3F, 0x0F, 0xFF, 0xC0, 0x0F, 0x02, 0x0D, 0x0D, 0x3C, 0x00, 0x3C, 0x00, 0xD7, 0x00, 0xD7, 0x00, +	0xD7, 0xC0, 0xD7, 0xC0, 0xD5, 0xC3, 0x57, 0xC0, 0xD5, 0xF3, 0x57, 0xC0, 0xD5, 0x7D, 0x57, 0xC0, +	0xD5, 0x7D, 0x57, 0xC0, 0xD7, 0x55, 0xD7, 0xC0, 0xD7, 0x55, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, +	0xD7, 0xD7, 0xD7, 0xC0, 0x3F, 0xFF, 0xFF, 0xC0, 0x0F, 0x0F, 0x0F, 0x00, 0x02, 0x0D, 0x0B, 0x3C, +	0x03, 0xC0, 0x00, 0xD7, 0x0D, 0x70, 0x00, 0xD5, 0xCD, 0x7C, 0x03, 0xD5, 0x7D, 0x7C, 0x0D, 0xD5, +	0x7D, 0x7C, 0x0D, 0xD7, 0x5D, 0x7C, 0x0D, 0xD7, 0x5D, 0x7C, 0x0D, 0xD7, 0xD5, 0x7C, 0x0D, 0xD7, +	0xD5, 0x7C, 0x0D, 0xD7, 0xF5, 0x7C, 0x03, 0xD7, 0xCD, 0x7C, 0x00, 0x3F, 0xC3, 0xFC, 0x00, 0x0F, +	0x00, 0xF0, 0x00, 0x02, 0x0D, 0x0B, 0x03, 0xFC, 0x00, 0x03, 0x0D, 0x57, 0x00, 0x0D, 0x35, 0xF5, +	0xC0, 0x0D, 0xD7, 0xFD, 0x70, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0xD7, 0xCD, +	0x7C, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0x35, 0xF5, 0xFC, 0x0D, 0x0D, 0x57, +	0xF0, 0x0D, 0x03, 0xFF, 0xC0, 0x03, 0x00, 0xFF, 0x00, 0x00, 0x02, 0x0D, 0x0B, 0x3F, 0xFF, 0x00, +	0x00, 0xD5, 0x55, 0xC0, 0x03, 0xD7, 0xFD, 0x70, 0x0D, 0xD7, 0xFD, 0x7C, 0x35, 0xD7, 0xCD, 0x7C, +	0x35, 0xD7, 0xFD, 0x7C, 0x35, 0xD5, 0x55, 0xFC, 0x35, 0xD7, 0xFF, 0xF0, 0x35, 0xD7, 0xFF, 0xC0, +	0x35, 0xD7, 0xC0, 0x00, 0x0D, 0xD7, 0xC0, 0x00, 0x03, 0x3F, 0xC0, 0x00, 0x00, 0x0F, 0x00, 0x00, +	0x00, 0x02, 0x0D, 0x0B, 0x03, 0xFC, 0x00, 0x00, 0x0D, 0x57, 0x00, 0x00, 0x35, 0xF5, 0xC0, 0x00, +	0xD7, 0xFD, 0x70, 0x00, 0xD7, 0xCD, 0x7C, 0x00, 0xD7, 0xCD, 0x7C, 0x00, 0xD7, 0xCD, 0x7C, 0x00, +	0xD7, 0xFD, 0x7C, 0x00, 0xD7, 0xD5, 0x7C, 0x00, 0x35, 0xF5, 0xFC, 0x00, 0x0D, 0x55, 0x70, 0x00, +	0x03, 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x02, 0x0D, 0x0C, 0x3F, 0xFF, 0x00, 0x00, 0xD5, +	0x55, 0xC0, 0x03, 0xD7, 0xFD, 0x70, 0x0D, 0xD7, 0xFD, 0x7C, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0xD7, +	0xFD, 0x7C, 0x03, 0xD5, 0x55, 0xFC, 0x00, 0xD7, 0xFD, 0x70, 0x03, 0xD7, 0xFD, 0x7C, 0x0D, 0xD7, +	0xCD, 0x7C, 0x0D, 0xD7, 0xC3, 0x5C, 0x03, 0x3F, 0xC0, 0xFF, 0x00, 0x0F, 0x00, 0x3C, 0x00, 0x02, +	0x0D, 0x0A, 0x0F, 0xFC, 0x00, 0x3F, 0x35, 0x57, 0x00, 0xD5, 0xD7, 0xF5, 0xC0, 0x3F, 0xD7, 0xF5, +	0xF0, 0x0F, 0xD7, 0xCF, 0xF0, 0x03, 0x35, 0x7F, 0xC0, 0x03, 0x0F, 0x57, 0x00, 0x03, 0x3F, 0xF5, +	0xC0, 0x03, 0xD7, 0x35, 0xF0, 0x03, 0xD7, 0xF5, 0xF0, 0x03, 0x35, 0x57, 0xF0, 0x03, 0x0F, 0xFF, +	0xC0, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x02, 0x0D, 0x0B, 0x3F, 0xFF, 0xC0, 0x0F, 0xD5, 0x55, 0x70, +	0x35, 0x3F, 0x5F, 0xFC, 0x35, 0x0F, 0x5F, 0xF0, 0x35, 0x03, 0x5F, 0x00, 0x35, 0x03, 0x5F, 0x00, +	0x35, 0x03, 0x5F, 0x00, 0x35, 0x03, 0x5F, 0x00, 0x35, 0x03, 0x5F, 0x00, 0x35, 0x03, 0x5F, 0x00, +	0x0D, 0x03, 0x5F, 0x00, 0x03, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x02, 0x0D, 0x0B, +	0x3C, 0x03, 0xC0, 0x0F, 0xD7, 0x0D, 0x70, 0x35, 0xD7, 0xCD, 0x7C, 0x35, 0xD7, 0xCD, 0x7C, 0x0D, +	0xD7, 0xCD, 0x7C, 0x0D, 0xD7, 0xCD, 0x7C, 0x0D, 0xD7, 0xCD, 0x7C, 0x03, 0xD7, 0xCD, 0x7C, 0x03, +	0xD7, 0xCD, 0x7C, 0x03, 0x35, 0xF5, 0xFC, 0x00, 0x0D, 0x57, 0xF0, 0x00, 0x03, 0xFF, 0xC0, 0x00, +	0x00, 0xFF, 0x00, 0x00, 0x02, 0x0D, 0x0B, 0x3C, 0x03, 0xC0, 0x3C, 0xD7, 0x0D, 0x70, 0xD7, 0xD7, +	0xCD, 0x7C, 0xD7, 0x35, 0xF5, 0xFC, 0xD7, 0x35, 0xF5, 0xF0, 0x35, 0x35, 0xF5, 0xF0, 0x35, 0x0D, +	0xF7, 0xF0, 0x0D, 0x0D, 0x57, 0xC0, 0x0D, 0x0D, 0x57, 0xC0, 0x03, 0x03, 0x5F, 0xC0, 0x03, 0x03, +	0x5F, 0x00, 0x03, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x03, 0x0D, 0x11, 0x3C, 0x03, +	0xC0, 0x3C, 0x03, 0xC0, 0xD7, 0x0D, 0x70, 0xD7, 0x0D, 0x70, 0xD7, 0xCD, 0x7C, 0xD7, 0xCD, 0x7C, +	0xD7, 0xCD, 0x7C, 0xD7, 0xC3, 0x5C, 0x35, 0xF5, 0x5F, 0x5F, 0xC0, 0xD7, 0x35, 0xF5, 0x5F, 0x5F, +	0x00, 0x35, 0x0D, 0x77, 0xDD, 0x7F, 0x00, 0x35, 0x0D, 0x57, 0xD5, 0x7C, 0x00, 0xD7, 0x03, 0x5F, +	0xF5, 0xFC, 0x03, 0x5F, 0x03, 0x5F, 0x35, 0xF0, 0x0D, 0x7F, 0x03, 0x5F, 0x35, 0xF0, 0x0D, 0x7C, +	0x00, 0xFF, 0x0F, 0xF0, 0x03, 0xFC, 0x00, 0x3C, 0x03, 0xC0, 0x00, 0xF0, 0x02, 0x0D, 0x0C, 0x3C, +	0x00, 0xF0, 0x0F, 0xD7, 0x03, 0x5C, 0x35, 0xD7, 0xC3, 0x5F, 0x35, 0x35, 0xCD, 0x7F, 0x0D, 0x0D, +	0x75, 0xFC, 0x03, 0x03, 0x57, 0xF0, 0x00, 0x03, 0x57, 0xC0, 0x00, 0x0D, 0x75, 0xC0, 0x00, 0x35, +	0xFD, 0x70, 0x00, 0xD7, 0xF3, 0x5C, 0x00, 0xD7, 0xC3, 0x5F, 0x00, 0x3F, 0xC0, 0xFF, 0x00, 0x0F, +	0x00, 0x3C, 0x00, 0x02, 0x0D, 0x0D, 0x3C, 0x00, 0x3C, 0x03, 0xD7, 0x00, 0xD7, 0x0D, 0xD7, 0xC0, +	0xD7, 0xC3, 0x35, 0xC3, 0x5F, 0xC0, 0x0D, 0x7D, 0x7F, 0x00, 0x03, 0x55, 0xFC, 0x00, 0x00, 0xD7, +	0xF0, 0x00, 0x00, 0xD7, 0xC0, 0x00, 0x00, 0xD7, 0xC0, 0x03, 0x00, 0xD7, 0xC0, 0x0D, 0x00, 0xD7, +	0xC0, 0x0D, 0x00, 0x3F, 0xC0, 0x03, 0x00, 0x0F, 0x00, 0x00, 0x02, 0x0D, 0x0C, 0x3F, 0xFF, 0xF0, +	0x00, 0xD5, 0x55, 0x5C, 0x00, 0x3F, 0xFF, 0x5F, 0x00, 0x0F, 0xFD, 0x7F, 0x00, 0x00, 0x35, 0xFC, +	0x00, 0x00, 0xD7, 0xF0, 0x00, 0x03, 0x5F, 0xC0, 0x00, 0x0D, 0x7F, 0x00, 0x00, 0x35, 0xFC, 0x00, +	0x00, 0xD7, 0xFF, 0xF0, 0x00, 0xD5, 0x55, 0x5C, 0x00, 0x3F, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFC, +	0x00, 0x01, 0x0E, 0x06, 0x3F, 0x00, 0xD5, 0xC3, 0xDF, 0xF3, 0xDF, 0xC0, 0xDF, 0x00, 0xDF, 0x00, +	0xDF, 0x00, 0xDF, 0x00, 0xDF, 0x00, 0xDF, 0x00, 0xDF, 0x00, 0xD5, 0xC0, 0x3F, 0xF0, 0x0F, 0xC0, +	0x01, 0x0D, 0x08, 0x30, 0x00, 0xDC, 0x00, 0xDF, 0x00, 0x37, 0x00, 0x37, 0xC0, 0x0D, 0xC0, 0x0D, +	0xF0, 0x03, 0x70, 0x03, 0x7C, 0x00, 0xDC, 0x00, 0xDF, 0x00, 0x3F, 0x00, 0x0C, 0x01, 0x0E, 0x06, +	0x3F, 0x00, 0xD5, 0xC0, 0x3D, 0xF0, 0x0D, 0xF0, 0x0D, 0xF0, 0x0D, 0xF0, 0x0D, 0xF0, 0x0D, 0xF0, +	0x0D, 0xF0, 0x0D, 0xF0, 0x3D, 0xF0, 0xD5, 0xF0, 0x3F, 0xF0, 0x0F, 0xC0, 0x02, 0x0E, 0x0A, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x03, 0x70, 0x00, 0x00, 0x0D, +	0x5C, 0x00, 0x03, 0x37, 0x77, 0x00, 0x0D, 0xDF, 0x7D, 0xC0, 0x35, 0x3F, 0x7F, 0xF0, 0x0D, 0x0F, +	0x7C, 0xC0, 0x03, 0x03, 0x7C, 0x00, 0x00, 0x03, 0x7C, 0x00, 0x00, 0x03, 0x7C, 0x00, 0x00, 0x00, +	0xFC, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x02, 0x0C, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x03, 0x70, 0x00, 0x00, 0x0D, 0xFC, 0x00, 0x00, 0x37, 0xFF, +	0xC0, 0x00, 0xD5, 0x55, 0x70, 0x00, 0x37, 0xFF, 0xFC, 0x00, 0x0D, 0xFF, 0xF0, 0x00, 0x03, 0x70, +	0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x01, 0x07, 0x04, 0x0C, 0x00, 0x37, +	0x00, 0xD7, 0x00, 0xD7, 0x00, 0xD7, 0x03, 0xDC, 0x0D, 0x30, 0x03, 0x02, 0x0D, 0x09, 0x00, 0x00, +	0x00, 0x3C, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0xD7, 0x0F, 0xF0, 0x00, 0xD7, 0x35, 0x5C, +	0x00, 0xD5, 0xD7, 0xD7, 0x00, 0xD7, 0x3D, 0x57, 0xC0, 0xD7, 0x35, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, +	0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0x35, 0x57, 0xC0, 0xD5, 0x0F, 0xFF, 0xC0, 0x3F, 0x03, 0xFF, +	0x00, 0x0F, 0x02, 0x0D, 0x09, 0x3C, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0xD7, 0xC0, 0x00, +	0x00, 0xD7, 0xF0, 0x00, 0x0F, 0xD5, 0x5C, 0x00, 0x35, 0xD7, 0xD7, 0x00, 0xD7, 0xD7, 0xD7, 0xC0, +	0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD5, 0x5F, 0xC0, +	0x35, 0x3F, 0xFF, 0x00, 0x0F, 0x0F, 0xFC, 0x00, 0x03, 0x02, 0x0D, 0x09, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x0F, 0xF0, 0x00, 0x3F, 0x35, 0x5C, 0x00, 0xD5, +	0xD7, 0xD7, 0x03, 0x5F, 0xD7, 0xFF, 0xC3, 0x5F, 0xD7, 0xCF, 0x03, 0x5F, 0xD7, 0xFC, 0x03, 0x5F, +	0xD7, 0xD7, 0x03, 0x5F, 0x35, 0x5F, 0xC0, 0xD5, 0x0F, 0xFF, 0x00, 0x3F, 0x03, 0xFC, 0x00, 0x0F, +	0x02, 0x0D, 0x09, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0xD7, 0xC0, 0x00, 0x0F, +	0xD7, 0xC0, 0x0F, 0x35, 0x57, 0xC0, 0x35, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD5, 0xD7, +	0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0x35, 0x57, 0xC0, 0x35, 0x0F, +	0xFF, 0xC0, 0x0F, 0x03, 0xFF, 0x00, 0x03, 0x02, 0x0D, 0x09, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, +	0x00, 0x35, 0x00, 0x00, 0x00, 0xD7, 0x0F, 0xF0, 0x00, 0xD7, 0x35, 0x5C, 0x03, 0x55, 0xD7, 0xD7, +	0x00, 0xD7, 0xD5, 0x57, 0xC0, 0xD7, 0xD7, 0xFF, 0xC0, 0xD7, 0xD7, 0xFF, 0x00, 0xD7, 0xD7, 0xD7, +	0x00, 0xD7, 0x35, 0x5F, 0xC0, 0xD7, 0x0F, 0xFF, 0x00, 0x3F, 0x03, 0xFC, 0x00, 0x0F, 0x01, 0x0D, +	0x07, 0x03, 0xC0, 0x0D, 0x70, 0x35, 0xFC, 0x35, 0xF0, 0xD5, 0x70, 0x35, 0xFC, 0x35, 0xF0, 0x35, +	0xF0, 0x35, 0xF0, 0x35, 0xF0, 0x35, 0xF0, 0x0F, 0xF0, 0x03, 0xC0, 0x02, 0x10, 0x09, 0x00, 0x00, +	0x00, 0x3C, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0xD7, 0x0F, 0xFC, 0x00, 0xD7, 0x35, 0x57, +	0x00, 0xD5, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, +	0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0x35, 0x57, 0xC0, 0xD7, 0x3F, 0xD7, 0xC0, 0x3F, 0xD7, 0xD7, +	0xC0, 0x0F, 0x35, 0x5F, 0xC0, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x02, 0x0D, +	0x09, 0x3C, 0x00, 0x00, 0x3C, 0xD7, 0x00, 0x00, 0xD7, 0xD7, 0xC0, 0x00, 0xD7, 0xD7, 0xF0, 0x00, +	0x3F, 0xD5, 0x5C, 0x00, 0xD7, 0xD7, 0xD7, 0x00, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, +	0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0x3F, 0xFF, 0xC0, +	0x3F, 0x0F, 0x0F, 0x00, 0x0F, 0x01, 0x0D, 0x05, 0x3C, 0x00, 0xD7, 0x00, 0xD7, 0xC0, 0x3F, 0xC0, +	0xD7, 0x00, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0x3F, 0xC0, +	0x0F, 0x00, 0x01, 0x10, 0x06, 0x0F, 0x00, 0x35, 0xC0, 0x35, 0xF0, 0x0F, 0xF0, 0x35, 0xC0, 0x35, +	0xF0, 0x35, 0xF0, 0x35, 0xF0, 0x35, 0xF0, 0x35, 0xF0, 0x35, 0xF0, 0x35, 0xF0, 0x35, 0xF0, 0xD7, +	0xF0, 0x3F, 0xC0, 0x0F, 0x00, 0x02, 0x0D, 0x09, 0x3C, 0x00, 0x00, 0xF0, 0xD7, 0x00, 0x03, 0x5C, +	0xD7, 0xC0, 0x03, 0x5F, 0xD7, 0xFC, 0x03, 0x5F, 0xD7, 0xD7, 0x03, 0x5F, 0xD7, 0x5F, 0xC3, 0x5F, +	0xD5, 0x7F, 0x03, 0x5F, 0xD5, 0xFC, 0x03, 0x5F, 0xD5, 0x70, 0x03, 0x5F, 0xD7, 0x5C, 0x03, 0x5F, +	0xD7, 0xD7, 0x03, 0x5F, 0x3F, 0xFF, 0xC0, 0xFF, 0x0F, 0x0F, 0x00, 0x3C, 0x01, 0x0D, 0x05, 0x3C, +	0x00, 0xD7, 0x00, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, +	0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0x3F, 0xC0, 0x0F, 0x00, 0x02, 0x0D, 0x0D, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0xD5, 0x55, 0x5C, 0x00, +	0xD7, 0xD7, 0xD7, 0x00, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, +	0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0x3F, 0xFF, 0xFF, 0xC0, 0x0F, 0x0F, 0x0F, 0x00, +	0x02, 0x0D, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, +	0xF0, 0x00, 0x0F, 0xD5, 0x5C, 0x00, 0x35, 0xD7, 0xD7, 0x00, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, +	0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0x35, 0x3F, +	0xFF, 0xC0, 0x0F, 0x0F, 0x0F, 0x00, 0x03, 0x02, 0x0D, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x3F, 0x35, 0x5C, 0x00, 0xD5, 0xD7, 0xD7, +	0x00, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, +	0xC0, 0xD7, 0x35, 0x5F, 0xC0, 0xD5, 0x0F, 0xFF, 0x00, 0xD7, 0x03, 0xFC, 0x00, 0xD7, 0x02, 0x10, +	0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, +	0x0F, 0xD5, 0x5C, 0x00, 0x35, 0xD7, 0xD7, 0x00, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, +	0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD5, 0x5F, 0xC0, 0x35, 0xD7, 0xFF, 0x00, +	0x0F, 0xD7, 0xFC, 0x00, 0x03, 0xD7, 0xC0, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x0F, 0x00, 0x00, +	0x00, 0x02, 0x10, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0xFC, 0x00, 0x3F, 0x35, 0x57, 0x00, 0xD5, 0xD7, 0xD7, 0xC0, 0xD5, 0xD7, 0xD7, 0xC0, 0xD7, +	0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0x35, 0x57, 0xC0, 0xD7, +	0x0F, 0xD7, 0xC0, 0x3F, 0x03, 0xD7, 0xC0, 0x0F, 0x00, 0xD7, 0xC0, 0x00, 0x00, 0x3F, 0xC0, 0x00, +	0x00, 0x0F, 0x00, 0x00, 0x01, 0x0D, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xC0, 0xD5, +	0x70, 0xD5, 0xFC, 0xD7, 0xF0, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0x3F, 0xC0, 0x0F, +	0x00, 0x02, 0x0D, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xD7, +	0x0F, 0xF0, 0x00, 0xD7, 0x35, 0x5C, 0x03, 0x55, 0xD7, 0xD7, 0x00, 0xD7, 0xD7, 0xFF, 0xC0, 0xD7, +	0x35, 0x5F, 0x00, 0xD7, 0x3F, 0xD7, 0x00, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0x35, 0x5F, 0xC0, 0x35, +	0x0F, 0xFF, 0x00, 0x0F, 0x03, 0xFC, 0x00, 0x03, 0x01, 0x0D, 0x07, 0x00, 0x00, 0x0F, 0x00, 0x35, +	0xC0, 0x35, 0xF0, 0xD5, 0x70, 0x35, 0xFC, 0x35, 0xF0, 0x35, 0xF0, 0x35, 0xF0, 0x35, 0xF0, 0x0D, +	0x70, 0x03, 0xFC, 0x00, 0xF0, 0x02, 0x0D, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x00, 0xF0, 0xD7, 0xD7, 0x03, 0x5C, 0xD7, 0xD7, 0xC3, 0x5F, +	0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0x35, 0xD7, 0xD7, 0xC0, 0x0D, +	0x35, 0x57, 0xC0, 0x0D, 0x0F, 0xFF, 0xC0, 0x03, 0x03, 0xFF, 0x00, 0x00, 0x02, 0x0D, 0x0B, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x03, 0xC0, 0x3C, 0xD7, +	0x0D, 0x70, 0xD7, 0xD7, 0xCD, 0x7C, 0xD7, 0x35, 0xF5, 0xFC, 0x35, 0x35, 0xF5, 0xF0, 0x35, 0x0D, +	0x57, 0xF0, 0x35, 0x03, 0x5F, 0xC0, 0x0D, 0x03, 0x5F, 0x00, 0x0D, 0x00, 0xFF, 0x00, 0x03, 0x00, +	0x3C, 0x00, 0x00, 0x02, 0x0D, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x3C, 0x00, 0x3C, 0x03, 0xD7, 0x3C, 0xD7, 0x0D, 0xD7, 0xD7, 0xD7, 0xC3, 0x35, 0xD7, +	0x5F, 0xC0, 0x35, 0xD7, 0x5F, 0x00, 0x35, 0x55, 0x5F, 0x00, 0x0D, 0x7D, 0x7F, 0x03, 0x0D, 0x7D, +	0x7C, 0x0D, 0x03, 0xFF, 0xFC, 0x03, 0x00, 0xF0, 0xF0, 0x00, 0x02, 0x0D, 0x0B, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x03, 0xC0, 0x3C, 0xD7, 0x0D, 0x70, +	0xD7, 0x35, 0xF5, 0xFC, 0xD7, 0x0D, 0x57, 0xF0, 0x35, 0x03, 0x5F, 0xC0, 0x35, 0x0D, 0x57, 0x00, +	0x0D, 0x35, 0xF5, 0xC0, 0x0D, 0xD7, 0xFD, 0x70, 0x03, 0x3F, 0xC3, 0xFC, 0x03, 0x0F, 0x00, 0xF0, +	0x0D, 0x02, 0x10, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x3C, 0x03, 0xC0, 0x0F, 0xD7, 0x0D, 0x70, 0x35, 0xD7, 0xCD, 0x7C, 0x0F, 0x35, 0xF5, 0xFC, 0x03, +	0x35, 0xF5, 0xF0, 0x03, 0x0D, 0x57, 0xF0, 0x0D, 0x0D, 0x57, 0xC0, 0x35, 0x03, 0x5F, 0xC0, 0x35, +	0x03, 0x5F, 0x00, 0x0F, 0x0D, 0x7F, 0x00, 0x03, 0x35, 0xFC, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, +	0x03, 0xC0, 0x00, 0x00, 0x02, 0x0D, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0xD5, 0x57, 0x00, 0x00, 0x3F, 0xD7, 0xC0, 0x00, 0x0F, +	0x5F, 0xC0, 0x00, 0x0D, 0x7F, 0x00, 0x00, 0x35, 0xFC, 0x00, 0x00, 0xD7, 0xFC, 0x00, 0x00, 0xD5, +	0x57, 0x00, 0x00, 0x3F, 0xFF, 0xC0, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x01, 0x0E, 0x07, 0x00, 0x00, +	0x03, 0xC0, 0x0D, 0x70, 0x37, 0xFC, 0x37, 0xF0, 0x37, 0xC0, 0xDF, 0xC0, 0xDF, 0x00, 0x37, 0x00, +	0x37, 0xC0, 0x37, 0xC0, 0x0D, 0x70, 0x03, 0xFC, 0x00, 0xF0, 0x01, 0x0F, 0x04, 0x30, 0x00, 0xDC, +	0x0F, 0xDF, 0x35, 0xDF, 0x0F, 0xDF, 0x03, 0xDF, 0x03, 0xDF, 0x00, 0xDF, 0x00, 0xDF, 0x03, 0xDF, +	0x03, 0xDF, 0x0F, 0xDF, 0x35, 0xDF, 0x0F, 0x3F, 0x03, 0x0C, 0x00, 0x01, 0x0E, 0x07, 0x00, 0x00, +	0x3C, 0x00, 0xD7, 0x00, 0x3D, 0xC0, 0x0D, 0xF0, 0x0D, 0xF0, 0x03, 0x70, 0x03, 0x7C, 0x0D, 0xFC, +	0x0D, 0xF0, 0x3D, 0xF0, 0xD7, 0xF0, 0x3F, 0xC0, 0x0F, 0x00, 0x02, 0x08, 0x0A, 0x00, 0x00, 0x00, +	0xC0, 0x00, 0x00, 0x03, 0x70, 0x0F, 0x00, 0x03, 0x70, 0x35, 0xC3, 0x00, 0xC0, 0xDF, 0x7D, 0xC0, +	0x00, 0x3F, 0xD7, 0xF0, 0x00, 0x0C, 0x3F, 0xC0, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x01, 0x04, 0x03, +	0x30, 0x00, 0xDC, 0x00, 0xDC, 0x00, 0x30, 0x00, 0x02, 0x10, 0x0A, 0x03, 0xFC, 0x00, 0x00, 0x0D, +	0x57, 0x00, 0x00, 0x35, 0xF5, 0xC0, 0x00, 0xD7, 0xFD, 0xF0, 0x00, 0xD7, 0xC3, 0xF0, 0x00, 0xD7, +	0xC0, 0xC0, 0x00, 0xD7, 0xC0, 0x00, 0x00, 0xD7, 0xC3, 0x00, 0x00, 0xD7, 0xCD, 0xC0, 0x00, 0x35, +	0xF5, 0xF0, 0x00, 0x0D, 0x57, 0xF0, 0x00, 0x03, 0x7F, 0xC0, 0x00, 0x03, 0xDF, 0x00, 0x00, 0x01, +	0xDF, 0x00, 0x00, 0x03, 0x7C, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x02, 0x0D, 0x09, 0x0C, 0x0C, +	0x00, 0x00, 0x37, 0x37, 0x00, 0x00, 0xD7, 0xD7, 0xC0, 0x03, 0x3F, 0xFF, 0xC0, 0x00, 0x3F, 0x3F, +	0x00, 0x03, 0xD7, 0xD7, 0x00, 0x0D, 0xD7, 0xD7, 0xC0, 0x35, 0xD7, 0xD7, 0xC0, 0x35, 0xD7, 0xD7, +	0xC0, 0x35, 0xD7, 0xD7, 0xC0, 0x35, 0x35, 0x57, 0xC0, 0x0D, 0x0F, 0xFF, 0xC0, 0x03, 0x03, 0xFF, +	0x00, 0x00, 0x02, 0x0D, 0x09, 0x00, 0x30, 0x00, 0x03, 0x03, 0xDC, 0x00, 0x0D, 0x0D, 0x7F, 0x00, +	0x37, 0x03, 0xFC, 0x00, 0x0F, 0x0F, 0xF0, 0x00, 0x0F, 0x35, 0x5C, 0x00, 0x35, 0xD7, 0xD7, 0x00, +	0xD7, 0xD5, 0x57, 0xC0, 0x3D, 0xD7, 0xFF, 0xC0, 0x35, 0xD7, 0xD7, 0x00, 0xD7, 0x35, 0x5F, 0xC0, +	0x35, 0x0F, 0xFF, 0x00, 0x0F, 0x03, 0xFC, 0x00, 0x03, 0x02, 0x0D, 0x09, 0x03, 0xC0, 0x00, 0x03, +	0x0D, 0x70, 0x00, 0x0D, 0x37, 0xDC, 0x00, 0x35, 0x0F, 0xFF, 0x00, 0x0F, 0x0F, 0xFC, 0x00, 0x03, +	0x35, 0x5C, 0x00, 0x0D, 0xD7, 0xD7, 0x00, 0x35, 0x3D, 0x57, 0xC0, 0x0F, 0x35, 0xD7, 0xC0, 0x0D, +	0xD7, 0xD7, 0xC0, 0x35, 0x35, 0x57, 0xC0, 0x0D, 0x0F, 0xFF, 0xC0, 0x03, 0x03, 0xFF, 0x00, 0x00, +	0x02, 0x0D, 0x09, 0x0C, 0x0C, 0x00, 0x0C, 0x37, 0x37, 0x00, 0x37, 0xD7, 0xD7, 0xC0, 0x0D, 0x3F, +	0xFF, 0xC0, 0x03, 0x0F, 0xFF, 0x00, 0x3F, 0x35, 0x5C, 0x00, 0xD5, 0xD7, 0xD7, 0x03, 0x5F, 0x3D, +	0x57, 0xC0, 0xF5, 0x35, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC3, 0x5F, 0x35, 0x57, 0xC0, 0xD5, 0x0F, +	0xFF, 0xC0, 0x3F, 0x03, 0xFF, 0x00, 0x0F, 0x02, 0x0D, 0x09, 0x03, 0x00, 0x00, 0x00, 0x0D, 0xF0, +	0x00, 0x03, 0x03, 0x5C, 0x00, 0x0D, 0x00, 0xFF, 0x00, 0x03, 0x0F, 0xF0, 0x00, 0x0F, 0x35, 0x5C, +	0x00, 0x35, 0xD7, 0xD7, 0x00, 0xD7, 0x3D, 0x57, 0xC0, 0x3D, 0x35, 0xD7, 0xC0, 0x35, 0xD7, 0xD7, +	0xC0, 0xD7, 0x35, 0x57, 0xC0, 0x35, 0x0F, 0xFF, 0xC0, 0x0F, 0x03, 0xFF, 0x00, 0x03, 0x02, 0x0D, +	0x09, 0x00, 0xC0, 0x00, 0x00, 0x03, 0x70, 0x00, 0x00, 0x0D, 0xDC, 0x00, 0x00, 0x03, 0x7F, 0x00, +	0x03, 0x0F, 0xFC, 0x00, 0x0D, 0x35, 0x5C, 0x00, 0x35, 0xD7, 0xD7, 0x00, 0x35, 0x3D, 0x57, 0xC0, +	0x35, 0x35, 0xD7, 0xC0, 0x35, 0xD7, 0xD7, 0xC0, 0x35, 0x35, 0x57, 0xC0, 0x0D, 0x0F, 0xFF, 0xC0, +	0x03, 0x03, 0xFF, 0x00, 0x03, 0x02, 0x10, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, +	0x00, 0x00, 0x00, 0x0D, 0x0F, 0xF0, 0x00, 0x03, 0x35, 0x5C, 0x00, 0x03, 0xD7, 0xD7, 0x00, 0x0D, +	0xD7, 0xFF, 0xC0, 0x35, 0xD7, 0xCF, 0x00, 0x35, 0xD7, 0xFC, 0x00, 0x35, 0xD7, 0xD7, 0x00, 0x35, +	0x35, 0x5F, 0xC0, 0x0D, 0x0D, 0xFF, 0x00, 0x03, 0x0F, 0x7C, 0x00, 0x00, 0x07, 0x7C, 0x00, 0x00, +	0x0D, 0xF0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x02, 0x0D, 0x09, 0x03, 0xC0, 0x00, 0x0C, 0x0D, +	0x70, 0x00, 0x37, 0x37, 0xDC, 0x00, 0xD7, 0x0F, 0xFF, 0x00, 0x3F, 0x0F, 0xFC, 0x00, 0x0F, 0x35, +	0x5C, 0x00, 0x35, 0xD7, 0xD7, 0x00, 0xD7, 0xD5, 0x57, 0xC0, 0xD5, 0xD7, 0xFF, 0xC0, 0xD7, 0xD7, +	0xD7, 0x00, 0xD7, 0x35, 0x5F, 0xC0, 0x35, 0x0F, 0xFF, 0x00, 0x0F, 0x03, 0xFC, 0x00, 0x03, 0x02, +	0x0D, 0x09, 0x0C, 0x0C, 0x00, 0x03, 0x37, 0x37, 0x00, 0x0D, 0xD7, 0xD7, 0xC0, 0x03, 0x3F, 0xFF, +	0xC0, 0x00, 0x0F, 0xFF, 0x00, 0x03, 0x35, 0x5C, 0x00, 0x0D, 0xD7, 0xD7, 0x00, 0x35, 0xD5, 0x57, +	0xC0, 0x35, 0xD7, 0xFF, 0xC0, 0x35, 0xD7, 0xD7, 0x00, 0x35, 0x35, 0x5F, 0xC0, 0x0D, 0x0F, 0xFF, +	0x00, 0x03, 0x03, 0xFC, 0x00, 0x00, 0x02, 0x0D, 0x09, 0x0C, 0x00, 0x00, 0x03, 0x37, 0xC0, 0x00, +	0x0D, 0x0D, 0x70, 0x00, 0x35, 0x03, 0xFC, 0x00, 0x0F, 0x0F, 0xF0, 0x00, 0x03, 0x35, 0x5C, 0x00, +	0x03, 0xD7, 0xD7, 0x00, 0x03, 0xD5, 0x57, 0xC0, 0x03, 0xD7, 0xFF, 0xC0, 0x03, 0xD7, 0xD7, 0x00, +	0x03, 0x35, 0x5F, 0xC0, 0x03, 0x0F, 0xFF, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x02, 0x0D, 0x09, +	0x0C, 0x0C, 0x00, 0x03, 0x37, 0x37, 0x00, 0x0D, 0xD7, 0xD7, 0xC0, 0x37, 0x3F, 0xFF, 0xC0, 0x0F, +	0x0F, 0xCF, 0x00, 0x0F, 0x0D, 0x70, 0x00, 0x0D, 0x0D, 0x7C, 0x00, 0x0D, 0x0D, 0x7C, 0x00, 0x0D, +	0x0D, 0x7C, 0x00, 0x0D, 0x0D, 0x7C, 0x00, 0x0D, 0x0D, 0x7C, 0x00, 0x0D, 0x03, 0xFC, 0x00, 0x03, +	0x00, 0xF0, 0x00, 0x00, 0x01, 0x0D, 0x07, 0x0F, 0x00, 0x35, 0xC0, 0xDF, 0x70, 0x3F, 0xFC, 0x3F, +	0x30, 0x35, 0xC0, 0x35, 0xF0, 0x35, 0xF0, 0x35, 0xF0, 0x35, 0xF0, 0x35, 0xF0, 0x0F, 0xF0, 0x03, +	0xC0, 0x01, 0x0D, 0x06, 0x30, 0x00, 0xDF, 0x00, 0x35, 0xC0, 0x0F, 0xF0, 0x3C, 0x00, 0xD7, 0x00, +	0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0x3F, 0xC0, 0x0F, 0x00, 0x02, 0x0F, +	0x0F, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x37, 0x37, 0x00, 0x00, 0xD7, 0xD7, 0xC0, 0x00, 0x3F, 0xFF, +	0xC0, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0xD5, 0x57, 0xF0, 0x00, 0xDF, 0x57, 0xC0, 0x00, 0xDF, 0x77, +	0xC0, 0x00, 0x3D, 0x57, 0xC0, 0x30, 0x0D, 0xF7, 0xC0, 0xDC, 0xF7, 0xF7, 0x70, 0xD7, 0x5F, 0xF5, +	0x7C, 0x3D, 0xFF, 0x0F, 0xFC, 0x0F, 0xFC, 0x03, 0xF0, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x04, 0x03, +	0x30, 0x00, 0xDC, 0x00, 0xDC, 0x00, 0x30, 0x00, 0x01, 0x04, 0x03, 0x30, 0x00, 0xDC, 0x00, 0xDC, +	0x00, 0x30, 0x0F, 0x02, 0x0D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x00, 0x00, 0x0F, 0xF0, 0x3F, 0xC0, 0x35, 0x5C, 0xD5, 0x70, 0xD7, 0xD7, 0x5F, 0x5C, 0x3D, 0x57, +	0x55, 0x5F, 0x35, 0xD7, 0x5F, 0xFF, 0xD7, 0xD7, 0x5F, 0xFC, 0xD7, 0xD7, 0x5F, 0x5C, 0x35, 0x55, +	0x55, 0x7F, 0x0F, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xF0, 0x01, 0x04, 0x03, 0x30, 0x00, 0xDC, +	0x03, 0xDC, 0x0D, 0x30, 0x03, 0x02, 0x0D, 0x09, 0x03, 0xC0, 0x00, 0x30, 0x0D, 0x70, 0x00, 0xDC, +	0x37, 0xDC, 0x03, 0x5F, 0x0F, 0xFF, 0x00, 0xFF, 0x0F, 0xFC, 0x00, 0x3F, 0x35, 0x5C, 0x00, 0xD5, +	0xD7, 0xD7, 0x03, 0x5F, 0xD7, 0xD7, 0xC3, 0x5F, 0xD7, 0xD7, 0xC3, 0x5F, 0xD7, 0xD7, 0xC3, 0x5F, +	0x35, 0x5F, 0xC0, 0xD5, 0x0F, 0xFF, 0x00, 0x3F, 0x03, 0xFC, 0x00, 0x0F, 0x02, 0x0D, 0x09, 0x0C, +	0x0C, 0x00, 0x0C, 0x37, 0x37, 0x00, 0x37, 0xD7, 0xD7, 0xC0, 0x0D, 0x3F, 0xFF, 0xC0, 0x03, 0x0F, +	0xFF, 0x00, 0x0F, 0x35, 0x5C, 0x00, 0x35, 0xD7, 0xD7, 0x00, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, +	0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0x35, 0x5F, 0xC0, 0x35, 0x0F, 0xFF, 0x00, 0x0F, 0x03, +	0xFC, 0x00, 0x03, 0x02, 0x0D, 0x09, 0x0C, 0x00, 0x00, 0x03, 0x37, 0xC0, 0x00, 0x0D, 0x0D, 0x70, +	0x00, 0x37, 0x03, 0xFC, 0x00, 0x0F, 0x0F, 0xF0, 0x00, 0x3F, 0x35, 0x5C, 0x00, 0xD7, 0xD7, 0xD7, +	0x00, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0x35, 0x5F, +	0xC0, 0x35, 0x0F, 0xFF, 0x00, 0x0F, 0x03, 0xFC, 0x00, 0x03, 0x02, 0x0D, 0x09, 0x03, 0xC0, 0x00, +	0x0C, 0x0D, 0x70, 0x00, 0x37, 0x37, 0xDC, 0x00, 0x0D, 0x0F, 0xFF, 0x00, 0x03, 0x3F, 0x3C, 0x00, +	0x3C, 0xD7, 0xD7, 0x00, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, +	0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0x35, 0x57, 0xC0, 0x35, 0x0F, 0xFF, 0xC0, 0x0F, 0x03, 0xFF, 0x00, +	0x03, 0x02, 0x0D, 0x09, 0x0C, 0x00, 0x00, 0x03, 0x37, 0xC0, 0x00, 0x0D, 0x0D, 0x70, 0x00, 0x0D, +	0x03, 0xFC, 0x00, 0x03, 0x3C, 0x3C, 0x00, 0x00, 0xD7, 0xD7, 0x00, 0x00, 0xD7, 0xD7, 0xC0, 0x00, +	0xD7, 0xD7, 0xC0, 0x00, 0xD7, 0xD7, 0xC0, 0x00, 0xD7, 0xD7, 0xC0, 0x00, 0x35, 0x57, 0xC0, 0x00, +	0x0F, 0xFF, 0xC0, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x01, 0x04, 0x03, 0x30, 0x30, 0xDC, 0xDC, 0xDC, +	0xDC, 0x30, 0x30, 0x01, 0x04, 0x03, 0x30, 0x30, 0xDC, 0xDC, 0xDC, 0xDC, 0x30, 0x30, 0x01, 0x04, +	0x03, 0x30, 0x30, 0xDC, 0xDC, 0xDC, 0xDC, 0x30, 0x30, 0x01, 0x04, 0x03, 0x30, 0x00, 0xDC, 0x03, +	0xDC, 0x03, 0x30, 0x00, 0x01, 0x04, 0x03, 0x30, 0x30, 0xDC, 0xDC, 0xDC, 0xDC, 0x30, 0x30, 0x01, +	0x04, 0x03, 0x30, 0x30, 0xDC, 0xDC, 0xDC, 0xDC, 0x30, 0x30, 0x01, 0x04, 0x03, 0x30, 0x30, 0xDC, +	0xDC, 0xDC, 0xDC, 0x30, 0x30, 0x01, 0x04, 0x03, 0x30, 0x00, 0xDC, 0x00, 0xDC, 0x00, 0x30, 0x00, +	0x02, 0x0D, 0x09, 0x00, 0x30, 0x00, 0x03, 0x03, 0xDC, 0x00, 0x3D, 0x0D, 0x7F, 0x00, 0xD7, 0x03, +	0xFC, 0x00, 0x3F, 0x0F, 0xF0, 0x00, 0x3C, 0x35, 0x5C, 0x00, 0xD7, 0xD7, 0xD7, 0x00, 0xD7, 0x3D, +	0x57, 0xC0, 0xD7, 0x35, 0xD7, 0xC0, 0xD7, 0xD7, 0xD7, 0xC0, 0xD7, 0x35, 0x57, 0xC0, 0xD7, 0x0F, +	0xFF, 0xC0, 0x3F, 0x03, 0xFF, 0x00, 0x0F, 0x01, 0x0D, 0x06, 0x03, 0x00, 0x3D, 0xC0, 0xD7, 0xF0, +	0x3F, 0xC0, 0x3C, 0x00, 0xD7, 0x00, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, 0xD7, 0xC0, +	0x3F, 0xC0, 0x0F, 0x00, 0x02, 0x0D, 0x09, 0x00, 0x30, 0x00, 0x00, 0x03, 0xDC, 0x00, 0x0F, 0x0D, +	0x7F, 0x00, 0x35, 0x03, 0xFC, 0x00, 0x0F, 0x0F, 0xF0, 0x00, 0xF0, 0x35, 0x5C, 0x03, 0x5F, 0xD7, +	0xD7, 0x03, 0x5F, 0xD7, 0xD7, 0xC3, 0x5F, 0xD7, 0xD7, 0xC3, 0x5F, 0xD7, 0xD7, 0xC3, 0x5F, 0x35, +	0x5F, 0xC0, 0xD5, 0x0F, 0xFF, 0x00, 0x3F, 0x03, 0xFC, 0x00, 0x0F, 0x02, 0x0D, 0x09, 0x00, 0x30, +	0x00, 0x03, 0x03, 0xDC, 0x00, 0x0D, 0x0D, 0x7F, 0x00, 0x37, 0x03, 0xFC, 0x00, 0x0F, 0x3C, 0x3C, +	0x00, 0x0F, 0xD7, 0xD7, 0x00, 0x35, 0xD7, 0xD7, 0xC0, 0x35, 0xD7, 0xD7, 0xC0, 0x35, 0xD7, 0xD7, +	0xC0, 0x35, 0xD7, 0xD7, 0xC0, 0x35, 0x35, 0x57, 0xC0, 0x35, 0x0F, 0xFF, 0xC0, 0x0F, 0x03, 0xFF, +	0x00, 0x03, 0x02, 0x0D, 0x09, 0x0F, 0x0C, 0x00, 0x00, 0x35, 0xF7, 0x00, 0x00, 0xDF, 0x5F, 0xC0, +	0x00, 0x3F, 0xFF, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0xD5, 0x5C, 0x00, 0x00, 0xD7, 0xD7, 0x00, +	0x00, 0xD7, 0xD7, 0xC0, 0x00, 0xD7, 0xD7, 0xC0, 0x00, 0xD7, 0xD7, 0xC0, 0x00, 0xD7, 0xD7, 0xC0, +	0x00, 0x3F, 0xFF, 0xC0, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x01, 0x04, 0x03, 0x30, 0x30, 0xDC, 0xDC, +	0xDC, 0xDC, 0x30, 0x30, 0x01, 0x04, 0x03, 0x30, 0x30, 0xDC, 0xDC, 0xDC, 0xDC, 0x30, 0x30, 0x01, +	0x04, 0x03, 0x30, 0x00, 0xDC, 0x00, 0xDC, 0x00, 0x30, 0x00, 0x02, 0x0D, 0x09, 0x03, 0xC0, 0x00, +	0x00, 0x0D, 0x70, 0x00, 0x00, 0x0D, 0x7C, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x0D, 0x70, 0x00, +	0x00, 0x0D, 0x7C, 0x00, 0x00, 0x35, 0xFC, 0x00, 0x00, 0xD7, 0xFC, 0x00, 0x00, 0xD7, 0xD7, 0x00, +	0x00, 0xD7, 0xD7, 0xC0, 0x00, 0x35, 0x5F, 0xC0, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x03, 0xFC, 0x00, +	0x00, 0x01, 0x04, 0x03, 0x30, 0x30, 0xDC, 0xDC, 0xDC, 0xDC, 0x30, 0x30, 0x01, 0x04, 0x03, 0x30, +	0x30, 0xDC, 0xDC, 0xDC, 0xDC, 0x30, 0x30, 0x01, 0x04, 0x03, 0x30, 0x30, 0xDC, 0xDC, 0xDC, 0xDC, +	0x30, 0x30, 0x01, 0x04, 0x03, 0x30, 0x00, 0xDC, 0x00, 0xDC, 0x00, 0x30, 0x00, 0x01, 0x0F, 0x06, +	0x00, 0x00, 0x03, 0x00, 0x0D, 0xC0, 0x35, 0xF0, 0x37, 0xF0, 0x0F, 0xC0, 0x0D, 0xC0, 0x35, 0xF0, +	0x35, 0xF0, 0x35, 0xF0, 0x35, 0xF0, 0xD5, 0xF0, 0xD7, 0xF0, 0x3F, 0xC0, 0x0F, 0x00, 0x02, 0x0F, +	0x0C, 0x0F, 0xFF, 0xC0, 0x00, 0x35, 0x55, 0x70, 0x00, 0xD7, 0xDF, 0x5C, 0x00, 0xDF, 0x5F, 0x5F, +	0x00, 0x3F, 0x5F, 0x5F, 0x00, 0x0F, 0x5D, 0x7F, 0x00, 0x03, 0x5D, 0x5C, 0x00, 0x03, 0x5F, 0x5F, +	0x00, 0x0D, 0x57, 0x5F, 0x00, 0x0D, 0x75, 0x7F, 0x00, 0x0D, 0x7F, 0xFC, 0x00, 0x0D, 0xFF, 0xF0, +	0x00, 0x35, 0xF0, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00 +}; + +} // namespace Touche diff --git a/engines/touche/touche.cpp b/engines/touche/touche.cpp new file mode 100644 index 0000000000..0b418b5e6b --- /dev/null +++ b/engines/touche/touche.cpp @@ -0,0 +1,3271 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 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$ + * $Id: $ + * + */ + +#include "common/stdafx.h" +#include "common/config-manager.h" +#include "common/system.h" + +#include "touche/touche.h" +#include "touche/graphics.h" + +namespace Touche { + +ToucheEngine::ToucheEngine(OSystem *system, Common::Language language) +	: Engine(system), _language(language) { + +	_talkTextMode = kTalkModeVoiceAndText; + +	_saveLoadCurrentPage = 0; +	_saveLoadCurrentSlot = 0; +	_hideInventoryTexts = false; + +	_screenRect = Common::Rect(640, 400); +	_roomAreaRect = Common::Rect(640, 352); +	clearDirtyRects(); + +	_defaultSoundPriority = 0; +	_snd_midiContext.unk2 = 0; +	_snd_midiContext.unkA = 1; +	_snd_midiContext.unkB = 0; +	_snd_midiContext.volume = 0; +	_snd_midiContext.unkF = 0; +	_snd_midiContext.currentVolume = 175; +	_playSoundCounter = 0; + +	_processRandomPaletteCounter = 0; + +	_roomNeedRedraw = false; +	_fastWalkMode = false; + +	_currentObjectNum = -1; +	_objectDescriptionNum = 0; +	_speechPlaying = false; + +	_roomNeedRedraw	= false; +	_fullRedrawCounter = 0; +	_redrawScreenCounter1 = 0; +	memset(_paletteBuffer, 0, sizeof(_paletteBuffer)); + +	Common::addSpecialDebugLevel(kDebugEngine,   "Engine",   "Engine debug level"); +	Common::addSpecialDebugLevel(kDebugGraphics, "Graphics", "Graphics debug level"); +	Common::addSpecialDebugLevel(kDebugResource, "Resource", "Resource debug level"); +	Common::addSpecialDebugLevel(kDebugOpcodes,  "Opcodes",  "Opcodes debug level"); +	Common::addSpecialDebugLevel(kDebugUserIntf, "UserIntf", "UserInterface debug level"); +} + +ToucheEngine::~ToucheEngine() { +	Common::clearAllSpecialDebugLevels(); +} + +int ToucheEngine::init() { +	_system->beginGFXTransaction(); +		initCommonGFX(true); +		_system->initSize(640, 400); +	_system->endGFXTransaction(); + +	_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); +	_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume")); +	return 0; +} + +int ToucheEngine::go() { +	res_openDataFile(); +	res_allocateTables(); +	res_loadSpriteImage(18, _menuKitData); +	res_loadImageHelper(_menuKitData, _currentImageWidth, _currentImageHeight); +	res_loadSpriteImage(19, _convKitData); +	res_loadImageHelper(_convKitData, _currentImageWidth, _currentImageHeight); + +	mainLoop(); + +	res_deallocateTables(); +	res_closeDataFile(); + +	return 0; +} + +void ToucheEngine::restart() { +	_displayQuitDialog = false; + +	memset(_flagsTable, 0, sizeof(_flagsTable)); + +	_currentKeyCharNum = 0; +	initKeyChars(-1); + +	for (int i = 0; i < NUM_SEQUENCES; ++i) { +		_sequenceEntryTable[i].sprNum = -1; +		_sequenceEntryTable[i].seqNum = -1; +	} + +	_disabledInputCounter = 0; +	_currentCursorObject = 0; +	setCursor(0); + +	_waitingSetKeyCharNum1 = -1; +	_waitingSetKeyCharNum2 = -1; +	_waitingSetKeyCharNum3 = -1; + +	_currentEpisodeNum = 0; +	_newEpisodeNum = ConfMan.getInt("boot_param"); +	if (_newEpisodeNum == 0) { +		_newEpisodeNum = 90; +	} + +	_newMusicNum = 0; +	_currentMusicNum = 0; + +	_newSoundNum = 0; +	_newSoundDelay = 0; +	_newSoundPriority = 0; + +	_flagsTable[176] = 0; +	_keyCharsTable[0].money = 25; +	_currentAmountOfMoney = 0; + +	_giveItemToKeyCharNum = 0; +	_giveItemToObjectNum = 0; +	_giveItemToCounter = 0; + +	clearAreaTable(); +	clearAnimationTable(); + +	initInventoryObjectsTable(); +	initInventoryLists(); +	drawInventory(0, 1); + +	_talkListEnd = 0; +	_talkListCurrent = 0; +	_talkTextRectDefined = false; +	_talkTextDisplayed = false; +	_talkTextInitialized = false; +	_skipTalkText = false; +	_talkTextSpeed = 0; +	_keyCharTalkCounter = 0; +	_talkTableLastTalkingKeyChar = -1; +	_talkTableLastOtherKeyChar = -1; +	_talkTableLastStringNum = -1; +	_objectDescriptionNum = 0; +	memset(_talkTable, 0, sizeof(_talkTable)); + +	_conversationChoicesUpdated = false; +	_conversationReplyNum = -1; +	_conversationEnded = false; +	_conversationNum = 0; +	_drawCharacterConversionRepeatCounter = 0; +	_currentConversation = 0; +	_disableConversationScript = false; +	_conversationAreaCleared = false; +	memset(_conversationChoicesTable, 0, sizeof(_conversationChoicesTable)); + +	_flagsTable[901] = 1; +	if (_language == Common::FR_FRA) { +		_flagsTable[621] = 1; +	} +} + +void ToucheEngine::mainLoop() { +	restart(); +	_inp_mousePos.x = 640 / 2; +	_inp_mousePos.y = 352 / 2; +	_inp_mouseButtonClicked = false; +	_inp_mouseButtonPressed = false; +	_system->warpMouse(_inp_mousePos.x, _inp_mousePos.y); +	setPalette(0, 255, 0, 0, 0); +#ifdef NORMAL_GAME_SPEED +	const int cycleDelay = 1000 / (1193180 / 32768); +#else +	const int cycleDelay = 10; +#endif +	uint32 frameTimeStamp = _system->getMillis(); +	for (uint32 cycleCounter = 0; _flagsTable[611] == 0; ++cycleCounter) { +		if ((cycleCounter & 3) == 0) { +			runCycle(); +		} +		if ((cycleCounter & 2) == 0) { +			fadePaletteFromFlags(); + 		} +		int delay = _system->getMillis() - frameTimeStamp; +		delay = cycleDelay - delay; +		if (delay < 1) { +			delay = 1; +		} +		_system->delayMillis(delay); +		frameTimeStamp = _system->getMillis(); +	} +} + +void ToucheEngine::processEvents() { +	OSystem::Event event; +	while (_system->pollEvent(event)) { +		switch (event.type) { +		case OSystem::EVENT_QUIT: +			_flagsTable[611] = 1; +			break; +		case OSystem::EVENT_KEYDOWN: +			_flagsTable[600] = event.kbd.keycode; +			if (event.kbd.keycode == 27) { // ESC +				if (_displayQuitDialog) { +					_flagsTable[611] = ui_displayQuitDialog(); +				} +			} +			if (event.kbd.keycode == 286) { // F5 +				if (_flagsTable[618] == 0 && !_hideInventoryTexts) { +					ui_handleOptions(0); +				} +			} +			if (event.kbd.keycode == 290) { // F9 +				_fastWalkMode = true; +			} +			if (event.kbd.keycode == 291) { // F10 +				_fastWalkMode = false; +			} +			if (event.kbd.ascii == 't') { +				++_talkTextMode; +				if (_talkTextMode == kTalkModeCount) { +					_talkTextMode = 0; +				} +				ui_displayTextMode(-(92 + _talkTextMode)); +			} +			if (event.kbd.ascii == 'd') { +				// enable debugging stuff ? +				_flagsTable[777] = 1; +			} +			if (event.kbd.ascii == ' ') { +				updateKeyCharTalk(2); +			} +			break; +		case OSystem::EVENT_MOUSEMOVE: +			_inp_mousePos.x = event.mouse.x; +			_inp_mousePos.y = event.mouse.y; +			break; +		case OSystem::EVENT_LBUTTONDOWN: +			_inp_mousePos.x = event.mouse.x; +			_inp_mousePos.y = event.mouse.y; +			_inp_mouseButtonClicked = true; +			break; +		case OSystem::EVENT_LBUTTONUP: +			_inp_mousePos.x = event.mouse.x; +			_inp_mousePos.y = event.mouse.y; +			break; +		case OSystem::EVENT_RBUTTONDOWN: +			_inp_mousePos.x = event.mouse.x; +			_inp_mousePos.y = event.mouse.y; +			_inp_mouseButtonPressed = true; +			break; +		case OSystem::EVENT_RBUTTONUP: +			_inp_mousePos.x = event.mouse.x; +			_inp_mousePos.y = event.mouse.y; +			_inp_mouseButtonPressed = false; +			break; +		default: +			break; +		} +	} +} + +void ToucheEngine::runCycle() { +	debugC(9, kDebugEngine, "ToucheEngine::runCycle()"); +	if (_flagsTable[290]) { +		changePaletteRange(); +	} +	if (_flagsTable[270]) { +		playSoundInRange(); +	} +	if (_conversationEnded) { +		_disabledInputCounter = 0; +		_fullRedrawCounter = 1; +		_roomAreaRect.setHeight(352); +		_hideInventoryTexts = false; +		_conversationEnded = false; +		drawInventory(_currentKeyCharNum, 1); +	} +	if (_giveItemToCounter == 1) { +		_fullRedrawCounter = 1; +		drawInventory(_giveItemToObjectNum, 1); +		++_giveItemToCounter; +	} +	if (_giveItemToCounter == -1) { +		_giveItemToCounter = 0; +		_roomAreaRect.setHeight(320); +		_keyCharsTable[_giveItemToKeyCharNum].flags &= ~kScriptPaused; +	} +	setupNewEpisode(); +	startNewMusic(); +	startNewSound(); +	updateSpeech(); +	handleConversation(); +	if (scrollRoom(_currentKeyCharNum)) { +	 	_fullRedrawCounter |= 1; +	} +	redrawRoom(); +	clearDirtyRects(); +	processAreaTable(); +	clearAreaTable(); +	updateRoomRegions(); +	if (_flagsTable[612] != 0) { +		_flagsTable[613] = getRandomNumber(_flagsTable[612]); +	} +	processEvents(); +	sortKeyChars(); +	for (int i = 0; i < NUM_KEYCHARS; ++i) { +		runKeyCharScript(&_keyCharsTable[i]); +	} +	if (_roomNeedRedraw) { +		scrollRoom(_currentKeyCharNum); +		redrawRoom(); +		_roomNeedRedraw = false; +	} +	updateSpeech(); +	for (int i = 0; i < NUM_KEYCHARS; ++i) { +		waitForKeyCharPosition(i); +	} +	redrawBackground(); +	waitForKeyCharsSet(); +	handleMouseInput(0); +	for (int i = 0; i < NUM_KEYCHARS; ++i) { +		drawKeyChar(&_keyCharsTable[i]); +	} +	processAnimationTable(); +	updateKeyCharTalk(0); +	updateDirtyScreenAreas(); +	++_flagsTable[295]; +	++_flagsTable[296]; +	++_flagsTable[297]; +	if (_flagsTable[298]) { +		--_flagsTable[298]; +	} +	if (_flagsTable[299]) { +		--_flagsTable[299]; +	} +} + +int16 ToucheEngine::getRandomNumber(int max) { +	assert(max > 0); +	return _rnd.getRandomNumber(max - 1); +} + +void ToucheEngine::changePaletteRange() { +	if (_processRandomPaletteCounter) { +		--_processRandomPaletteCounter; +	} else { +		int scale = _flagsTable[291] + getRandomNumber(_flagsTable[292]); +		setPalette(0, 240, scale, scale, scale); +		_processRandomPaletteCounter = _flagsTable[293] + getRandomNumber(_flagsTable[294]); +	} +} + +void ToucheEngine::playSoundInRange() { +	if (_playSoundCounter != 0) { +		--_playSoundCounter; +	} else { +		int16 flag = getRandomNumber(_flagsTable[270]); +		int16 num = _flagsTable[273 + flag]; +		res_loadSound(0, num); +		_playSoundCounter = _flagsTable[271] + getRandomNumber(_flagsTable[272]); +	} +} + +void ToucheEngine::resetSortedKeyCharsTable() { +	for (int i = 0; i < NUM_KEYCHARS; ++i) { +		_sortedKeyCharsTable[i] = &_keyCharsTable[i]; +	} +} + +void ToucheEngine::setupEpisode(int num) { +	debugC(9, kDebugEngine, "ToucheEngine::setupEpisode() num=%d", num); +	res_stopSpeech(); +	resetTalkingVars(); +	res_loadSpeech(-1); +	_currentObjectNum = -1; +	if (num != -1) { +		_updatedRoomAreasTable[0] = 1; +		clearAreaTable(); +		initKeyChars(-1); +		for (int i = 200; i < 300; ++i) { +			_flagsTable[i] = 0; +		} +		_flagsTable[291] = 240; +		_flagsTable[292] = 16; +		_flagsTable[293] = 0; +		_flagsTable[294] = 1; +		_currentEpisodeNum = num; +		if (_flagsTable[911] != 0) { +			// load scripts from external data files +		} +		debug(0, "Setting up episode %d\n", num); +		res_loadProgram(num); +		_disabledInputCounter = 0; +	} +	res_decodeProgramData(); +	_roomAreaRect.setHeight(352); +	_disableConversationScript = false; +	_hideInventoryTexts = false; +	_conversationEnded = false; +	clearRoomArea(); +	drawInventory(_currentKeyCharNum, 1); +} + +void ToucheEngine::setupNewEpisode() { +	debugC(9, kDebugEngine, "ToucheEngine::setupNewEpisode() _newEpisodeNum=%d", _newEpisodeNum); +	if (_newEpisodeNum) { +		if (_newEpisodeNum == 91) { +			_displayQuitDialog = true; +		} +//		flushDigitalSounds(); +		setupEpisode(_newEpisodeNum); +		runCurrentKeyCharScript(1); +		_newEpisodeNum = 0; +		resetSortedKeyCharsTable(); +	} +} + +void ToucheEngine::drawKeyChar(KeyChar *key) { +	debugC(9, kDebugEngine, "ToucheEngine::drawKeyChar()"); +	if (key->num != 0) { +		Common::Rect r(key->prevBoundingRect); +		r.extend(key->boundingRect); +//		r.clip(_roomAreaRect); +//		addToDirtyRect(r); +	} +} + +void ToucheEngine::sortKeyChars() { +	debugC(9, kDebugEngine, "ToucheEngine::sortKeyChars()"); +	for (int i = 0; i < NUM_KEYCHARS; ++i) { +		bool hasSwapped = false; +		for (int j = 0; j < NUM_KEYCHARS - 1; ++j) { +			KeyChar *key1 = _sortedKeyCharsTable[j]; +			KeyChar *key2 = _sortedKeyCharsTable[j + 1]; +			if (key1->num != 0 && key2->num != 0) { +				if (key1->zPos > key2->zPos) { +					SWAP(_sortedKeyCharsTable[j], _sortedKeyCharsTable[j + 1]); +					hasSwapped = true; +				} else if (key1->zPos == key2->zPos && key1->yPos > key2->yPos) { +					SWAP(_sortedKeyCharsTable[j], _sortedKeyCharsTable[j + 1]); +					hasSwapped = true; +				} +			} else if (key1->num != 0) { +				SWAP(_sortedKeyCharsTable[j], _sortedKeyCharsTable[j + 1]); +				hasSwapped = true; +			} +		} +		if (!hasSwapped) { +			break; +		} +	} +} + +void ToucheEngine::runKeyCharScript(KeyChar *key) { +	debugC(9, kDebugEngine, "ToucheEngine::runKeyCharScript() keyChar=%d", key - _keyCharsTable); +	if (key->scriptDataOffset != 0 && (key->flags & (kScriptStopped | kScriptPaused)) == 0) { +		int16 scriptParam = key->num - 1; +		int16 *prevStackDataPtr = _script.stackDataPtr; +		_script.stackDataPtr = key->scriptStackPtr; +		uint16 prevDataOffset = _script.dataOffset; +		_script.dataOffset = key->scriptDataOffset; +		_script.quitFlag = 0; +		while (_script.quitFlag == 0) { +			executeScriptOpcode(scriptParam); +		} +		switch (_script.quitFlag) { +		case 1: // restart +			key->scriptDataOffset = key->scriptDataStartOffset; +			key->scriptStackPtr = &key->scriptStackTable[39]; +			break; +		case 3: // pause +			key->flags |= kScriptPaused; +			key->flags &= ~kScriptStopped; +			key->scriptDataOffset = _script.dataOffset; +			key->scriptStackPtr = _script.stackDataPtr; +			break; +		default: // stop +			key->flags |= kScriptStopped; +			key->flags &= ~kScriptPaused; +			key->scriptDataOffset = 0; +			break; +		} +		_script.dataOffset = prevDataOffset; +		_script.stackDataPtr = prevStackDataPtr; +	} +} + +void ToucheEngine::runCurrentKeyCharScript(int mode) { +	debugC(9, kDebugEngine, "ToucheEngine::runCurrentKeyCharScript() _currentKeyCharNum=%d mode=%d", _currentKeyCharNum, mode); +	KeyChar *key = &_keyCharsTable[_currentKeyCharNum]; +	if (mode == 1) { +		_script.dataOffset = 0; +		_script.stackDataPtr = key->scriptStackPtr; +	} +	if (mode != 0) { +		while (_script.quitFlag == 0) { +			executeScriptOpcode(0); +		} +		if (mode == 1) { +			centerScreenToKeyChar(_currentKeyCharNum); +		} +		if (_script.quitFlag == 3) { +			key->flags |= kScriptPaused; +			key->flags &= ~kScriptStopped; +			key->scriptDataOffset = _script.dataOffset; +			key->scriptStackPtr = _script.stackDataPtr; +		} +	} +	handleMouseInput(1); +} + +void ToucheEngine::executeScriptOpcode(int16 param) { +	debugC(9, kDebugEngine, "executeScriptOpcode(%d) offset=%04X", param, _script.dataOffset); +	_script.keyCharNum = param; +	_script.opcodeNum = _script.readNextByte(); +	if (_script.opcodeNum < NUM_OPCODES) { +		OpcodeProc op = _opcodesTable[_script.opcodeNum]; +		if (op) { +			(this->*op)(); +			return; +		} +	} +	error("Invalid opcode 0x%X", _script.opcodeNum); +} + +void ToucheEngine::initKeyChars(int keyChar) { +	debugC(9, kDebugEngine, "ToucheEngine::initKeyChars() keyChar=%d", keyChar); +	int indexStart, indexEnd; +	if (keyChar == -1) { +		indexStart = 0; +		indexEnd = NUM_KEYCHARS; +	} else { +		indexStart = keyChar; +		indexEnd = keyChar + 1; +	} +	Common::Rect defaultKeyCharRect(10, 10, 11, 11); +	for (int i = indexStart; i < indexEnd; ++i) { +		KeyChar *key = &_keyCharsTable[i]; +		if (keyChar != -1 && key->num != 0) { +			Area keyCharArea; +			keyCharArea.r = key->prevBoundingRect; +			keyCharArea.r.extend(key->boundingRect); +			keyCharArea.srcX = _flagsTable[614] + keyCharArea.r.left; +			keyCharArea.srcY = _flagsTable[615] + keyCharArea.r.top; +			addToAreaTable(&keyCharArea); +		} +		key->num = 0; +		key->strNum = 0; +		key->textColor = 253; +		key->currentAnimCounter = 0; +		key->currentAnimSpeed = 0; +		key->currentAnim = 0; +		key->framesListCount = 0; +		key->currentFrame = 0; +		key->anim1Start = 0; +		key->anim1Count = 1; +		key->anim2Start = 0; +		key->anim2Count = 1; +		key->anim3Start = 0; +		key->anim3Count = 1; +		key->facingDirection = 0; +		key->sequenceDataOffset = 0; +		key->walkDataNum = 0; +		key->walkPointsList[0] = -1; +		key->walkPointsListCount = 0; +		key->delay = 0; +		key->waitingKeyChar = -1; +		key->flags = 0; +		key->scriptDataOffset = 0; +		key->scriptStackPtr = &key->scriptStackTable[39]; +		key->xPos = 10; +		// like the original interpreter, don't reset yPos here. Doing so causes +		// glitches during the introduction for example (talk texts get displayed +		// at the wrong coordinates). +		key->boundingRect = defaultKeyCharRect; +		key->prevBoundingRect = defaultKeyCharRect; +	} +} + +void ToucheEngine::setKeyCharTextColor(int keyChar, uint16 color) { +	debugC(9, kDebugEngine, "ToucheEngine::setKeyCharTextColor(%d) color=%d", keyChar, color); +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	_keyCharsTable[keyChar].textColor = color; +} + +void ToucheEngine::waitForKeyCharPosition(int keyChar) { +	debugC(9, kDebugEngine, "ToucheEngine::waitForKeyCharPosition(%d)", keyChar); +	KeyChar *key = _sortedKeyCharsTable[keyChar]; +	if (key->num != 0) { +		key->prevBoundingRect = key->boundingRect; +		moveKeyChar(_offscreenBuffer, 640, key); +		key->boundingRect = _moveKeyCharRect; +		if (key->delay != 0) { +			--key->delay; +			if (key->delay == 0) { +				key->flags &= ~kScriptPaused; +			} +			return; +		} +		if (key->waitingKeyChar == -1) { +			return; +		} +		KeyChar *nextKey = &_keyCharsTable[key->waitingKeyChar]; +		if (nextKey->currentAnim != key->waitingKeyCharPosTable[0] && +		    nextKey->pointsDataNum != key->waitingKeyCharPosTable[1] && +		    nextKey->walkDataNum != key->waitingKeyCharPosTable[2]) { +			return; +		} +		key->flags &= ~kScriptPaused; +		key->waitingKeyChar = -1; +	} +} + +void ToucheEngine::setKeyCharBox(int keyChar, int value) { +	debugC(9, kDebugEngine, "ToucheEngine::setKeyCharBox(%d) value=%d", keyChar, value); +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	KeyChar *key = &_keyCharsTable[keyChar]; +	key->prevPointsDataNum = key->pointsDataNum = value; +	key->xPosPrev = key->xPos = _programPointsTable[value].x; +	key->yPosPrev = key->yPos = _programPointsTable[value].y; +	key->zPosPrev = key->zPos = _programPointsTable[value].z; +	key->prevWalkDataNum = key->walkDataNum = findWalkDataNum(value, 10000); +} + +void ToucheEngine::setKeyCharFrame(int keyChar, int16 type, int16 value1, int16 value2) { +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	KeyChar *key = &_keyCharsTable[keyChar]; +	switch (type) { +	case 0: +		key->anim2Start = value1; +		key->anim2Count = value2; +		key->anim3Start = value1; +		key->anim3Count = value2; +		break; +	case 1: +		if (value2 != 0) { +			value2 = getRandomNumber(value2); +		} +		key->framesList[key->framesListCount] = value1 + value2; +		++key->framesListCount; +		key->framesListCount &= 15; +		break; +	case 2: +		key->anim1Start = value1; +		key->anim1Count = value2; +		break; +	case 3: +		key->currentAnim = value1; +		key->currentAnimSpeed = 0; +		key->currentAnimCounter = 0; +		break; +	case 4: +		key->anim3Start = value1; +		key->anim3Count = value2; +		break; +	} +} + +void ToucheEngine::setKeyCharFacingDirection(int keyChar, int16 dir) { +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	_keyCharsTable[keyChar].facingDirection = dir; +} + +void ToucheEngine::initKeyCharScript(int keyChar, int16 spriteNum, int16 seqDataIndex, int16 seqDataOffs) { +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	KeyChar *key = &_keyCharsTable[keyChar]; +	key->num = keyChar + 1; +	key->spriteNum = spriteNum; +	key->sequenceDataIndex = seqDataIndex; +	key->sequenceDataOffset = seqDataOffs; +	key->scriptDataStartOffset = findProgramKeyCharScriptOffset(keyChar); +	key->scriptDataOffset = key->scriptDataStartOffset; +} + +uint16 ToucheEngine::findProgramKeyCharScriptOffset(int keyChar) const { +	for (uint i = 0; i < _programKeyCharScriptOffsetTable.size(); ++i) { +		if (_programKeyCharScriptOffsetTable[i].keyChar == keyChar) { +			return _programKeyCharScriptOffsetTable[i].offset; +		} +	} +	return 0; +} + +bool ToucheEngine::scrollRoom(int keyChar) { +	if (_flagsTable[616] != 0) { +		return 0; +	} +	KeyChar *key = &_keyCharsTable[keyChar]; +	bool needRedraw = false; + +	// vertical scrolling +	int prevRoomDy = _flagsTable[615]; +	_flagsTable[615] = key->yPos + 32 - 400 / 2; +	int roomHeight; +	if (_hideInventoryTexts) { +		roomHeight = 352; +	} else { +		roomHeight = (_flagsTable[606] != 0) ? 400 : 352; +		_roomAreaRect.setHeight(roomHeight); +	} +	_flagsTable[615] = CLIP<int16>(_flagsTable[615], 0, _currentBitmapHeight - roomHeight); +	if (_flagsTable[615] != prevRoomDy) { +		needRedraw = true; +	} + +	// horizontal scrolling +	int prevRoomDx = _flagsTable[614]; +	if (key->xPos > prevRoomDx + 480) { +		int dx = key->xPos - (prevRoomDx + 480); +		prevRoomDx += dx; +	} else if (key->xPos < prevRoomDx + 160) { +		int dx = prevRoomDx + 160 - key->xPos; +		prevRoomDx -= dx; +		if (prevRoomDx < 0) { +			prevRoomDx = 0; +		} +	} +	prevRoomDx = CLIP<int16>(prevRoomDx, 0, _roomWidth - 640); +	if (_flagsTable[614] != prevRoomDx) { +		_flagsTable[614] = prevRoomDx; +		return true; +	} +	if (_screenOffset.x == 0) { +		return needRedraw; +	} +	int scrollDx = _screenOffset.x - _flagsTable[614]; +	if (scrollDx < -4) { +		scrollDx = -4; +	} else if (scrollDx > 4) { +		scrollDx = 4; +	} +	_flagsTable[614] += scrollDx; + +	if (_screenOffset.x == _flagsTable[614]) { +		_screenOffset.x = 0; +	} +	return true; +} + +void ToucheEngine::drawIcon(int x, int y, int num) { +	res_loadImage(num, _iconData); +	Graphics::copyRect(_offscreenBuffer, 640, x, y, +	  _iconData, 58, 0, 0, +	  58, 42, +	  Graphics::kTransparent); +} + +void ToucheEngine::centerScreenToKeyChar(int keyChar) { +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	KeyChar *key = &_keyCharsTable[keyChar]; +	_flagsTable[614] = key->xPos - 640 / 2; +	_flagsTable[615] = key->yPos - 400 / 2; +	_flagsTable[615] = CLIP<int16>(_flagsTable[615], 0, _currentBitmapHeight - 352); +	scrollRoom(keyChar); +} + +void ToucheEngine::waitForKeyCharsSet() { +	if (_waitingSetKeyCharNum2 != -1) { +		KeyChar *key = &_keyCharsTable[_waitingSetKeyCharNum2]; +		if (key->framesListCount == key->currentFrame && key->currentAnim == key->anim2Start) { +			key = &_keyCharsTable[_waitingSetKeyCharNum1]; +			if (key->framesListCount == key->currentFrame && key->currentAnim == key->anim2Start) { +				key = &_keyCharsTable[_waitingSetKeyCharNum3]; +				_waitingSetKeyCharNum2 = -1; +				key->flags &= ~kScriptPaused; +			} +		} +	} +} + +void ToucheEngine::redrawRoom() { +	if (_currentBitmapWidth == 0 || _currentBitmapHeight == 0) { +		return; +	} +	int w = 640; +	if (_flagsTable[614] < 0 || _flagsTable[614] > _currentBitmapWidth - w) { +		error("Invalid room_x_offset = %d (w=%d, room_w=%d)", _flagsTable[614], w, _currentBitmapWidth); +	} +	int h = (_flagsTable[606] != 0) ? 400 : _roomAreaRect.height(); +	if (_flagsTable[615] < 0 || _flagsTable[615] > _currentBitmapHeight - h) { +		error("Invalid room_y_offset = %d (h=%d, room_h=%d)", _flagsTable[615], h, _currentBitmapHeight); +	} +	uint8 *dst = _offscreenBuffer; +	const uint8 *src = _backdropBuffer + _flagsTable[615] * _currentBitmapWidth + _flagsTable[614]; +	while (h--) { +		memcpy(dst, src, w); +		dst += w; +		src += _currentBitmapWidth; +	} +} + +void ToucheEngine::fadePalette(int firstColor, int lastColor, int scale, int scaleInc, int fadingStepsCount) { +	for (int i = 0; i < fadingStepsCount; ++i) { +		scale += scaleInc; +		if (scale > 255) { +			scale = 0; +		} else if (scale < 0) { +			scale = 0; +		} +		setPalette(firstColor, lastColor, scale, scale, scale); +	} +} + +void ToucheEngine::fadePaletteFromFlags() { +	if (_flagsTable[603]) { +		setPalette(_flagsTable[607], _flagsTable[608], _flagsTable[605], _flagsTable[605], _flagsTable[605]); +		if (_flagsTable[603] > 0) { +			if (_flagsTable[605] >= _flagsTable[609]) { +				_flagsTable[603] = 0; +			} +		} else { +			if (_flagsTable[605] <= _flagsTable[610]) { +				_flagsTable[603] = 0; +			} +		} +		_flagsTable[605] += _flagsTable[603]; +		if (_flagsTable[605] < 0) { +			_flagsTable[605] = 0; +		} else if (_flagsTable[605] > 255) { +			_flagsTable[605] = 255; +		} +	} +} + +static uint8 *getKeyCharFrameData(uint8 *p, uint16 dir1, uint16 dir2, uint16 dir3, uint8 **dst, int16 sequence_num) { +	uint8 *src; +	uint16 offs, num1; + +	// spriteData +	// LE16 offset to "sprite copy" data +	// LE16 offset to 4 * 2 * 10 offsets : "sprite info" offset +	// LE16 data offset +	// LE16 ? +	offs = READ_LE_UINT16(p + sequence_num * 8 + 2); +	offs = READ_LE_UINT16(p + offs + dir1 * 4); // facing +	offs = READ_LE_UINT16(p + offs + dir2 * 2); // top/bottom +	src = p + offs + dir3 * 10; // current frame anim ? +	*dst = src; +	// LE16 : if 0x8000 -> offset "sprite copy" data num +	// LE16 : dx +	// LE16 : dy +	// LE16 : dz + +	num1 = READ_LE_UINT16(src) & 0x7FFF; +	offs = READ_LE_UINT16(p + sequence_num * 8 + 0); +	offs = READ_LE_UINT16(p + offs + num1 * 2); +	return p + offs; +	// LE16 : srcX +	// LE16 : srcY +	// LE16 : flags (vflip, hflip) +} + +void ToucheEngine::moveKeyChar(uint8 *dst, int dstPitch, KeyChar *key) { +	int16 keyChar = key->num - 1; +	int16 walkDataNum = key->walkDataNum; +	int16 clippingRectNum = 0; +	if (walkDataNum != -1) { +		clippingRectNum = _programWalkTable[walkDataNum].clippingRect; +	} +	Common::Rect clippingRect(_programRectsTable[clippingRectNum]); +	clippingRect.translate(-_flagsTable[614], -_flagsTable[615]); +	if (key->flags & 0x8000) { +		clippingRect.moveTo(clippingRect.left, 352); +	} +	clippingRect.clip(_roomAreaRect); +	SpriteData *spr = &_spritesTable[key->spriteNum]; +	int x1 =  30000, y1 =  30000; +	int x2 = -30000, y2 = -30000; +	int16 keyCharDirection = _flagsTable[266]; +	if (keyCharDirection == 0) { +		keyCharDirection = key->facingDirection; +	} +	int16 facingDirection = keyCharDirection; +	uint8 *sequenceDataBase = _sequenceDataTable[key->sequenceDataIndex]; +	uint8 *sequenceData = sequenceDataBase; + +	uint16 frameDirFlag = READ_LE_UINT16(sequenceData + key->sequenceDataOffset * 8 + 4); +	if (frameDirFlag) { +		sequenceData += frameDirFlag & ~1; +	} + +	uint8 *frameData; +	uint8 *frameDataBase = getKeyCharFrameData(sequenceDataBase, key->currentAnim, facingDirection, key->currentAnimCounter, &frameData, key->sequenceDataOffset); +	uint16 frameFlag = READ_LE_UINT16(frameData); frameData += 2; +	uint16 walkDx = READ_LE_UINT16(frameData); frameData += 2; +	uint16 walkDy = READ_LE_UINT16(frameData); frameData += 2; +	uint16 walkDz = READ_LE_UINT16(frameData); frameData += 2; + +	if (key->currentAnimSpeed <= 0) { +		key->currentAnimSpeed = READ_LE_UINT16(frameData); +	} +	--key->currentAnimSpeed; +	if (key->currentAnimSpeed <= 0) { +		++key->currentAnimCounter; +	} +	if (_fastWalkMode) { +		walkDx *= 2; +		walkDy *= 2; +		walkDz *= 2; +	} +	updateKeyCharWalkPath(key, walkDx, walkDy, walkDz); +	int posX = key->xPos; +	int posY = key->yPos; +	int posZ = key->zPos; +	if (frameFlag & 0x8000) { +		changeKeyCharFrame(key, keyChar); +	} +	posX -= _flagsTable[614]; +	posY -= _flagsTable[615]; +	if (posZ == 160) { // draw sprite frames without rescaling +		while (1) { +			int dstX = (int16)READ_LE_UINT16(frameDataBase); frameDataBase += 2; +			if (dstX == 10000) { +				_moveKeyCharRect = Common::Rect(x1, y1, x2 + spr->w, y2 + spr->h); +				break; +			} +			int dstY = (int16)READ_LE_UINT16(frameDataBase); frameDataBase += 2; + +			if (facingDirection == 3) { +				dstX = -dstX - spr->w; +			} +			dstX += posX; +			dstY += posY; +			x1 = MIN(dstX, x1); +			x2 = MAX(dstX, x2); +			y1 = MIN(dstY, y1); +			y2 = MAX(dstY, y2); + +			int frameDir = READ_LE_UINT16(frameDataBase); frameDataBase += 2; +//			assert((frameDir & 0x4000) == 0); // hflipped +			bool vflipped = (frameDir & 0x8000) != 0; + +			frameDir &= 0xFFF; +			if (frameDirFlag) { +				frameDir = READ_LE_UINT16(sequenceData + frameDir * 2); +			} +			if (keyChar == 0) { +				if (_directionsTable[frameDir] <= _flagsTable[176]) { +					continue; +				} +			} +			if (frameDir == 0x800) { +				continue; +			} + +			assert(spr->w != 0); +			int framesPerLine = spr->bitmapWidth / spr->w; +			assert(framesPerLine != 0); +			const int srcOffsX = spr->w * (frameDir % framesPerLine); +			const int srcOffsY = spr->h * (frameDir / framesPerLine); + +			Area copyRegion(dstX, dstY, spr->w, spr->h); +			copyRegion.srcX = 0; +			copyRegion.srcY = 0; +			if (!copyRegion.clip(clippingRect)) { +				continue; +			} + +			if (facingDirection == 3) { +				vflipped = !vflipped; +			} + +			uint8 *dstCur = dst + copyRegion.r.top * dstPitch + copyRegion.r.left; +			const int spr_y1 = srcOffsY + copyRegion.srcY; +			const int spr_x1 = srcOffsX + copyRegion.srcX; +			const uint8 *srcSpr = spr->ptr + spr_y1 * spr->bitmapWidth + spr_x1; +			for (int h = 0; h < copyRegion.r.height(); ++h) { +				for (int w = 0; w < copyRegion.r.width(); ++w) { +					uint8 color = vflipped ? srcSpr[spr->w - 1 - w] : srcSpr[w]; +					if (color != 0) { +						dstCur[w] = color; +					} +				} +				srcSpr += spr->bitmapWidth; +				dstCur += dstPitch; +			} +		} +	} else { // draw sprite frames with rescaling +		y2 = posY; +		int clippingRect_x1 = clippingRect.left; +		int clippingRect_y1 = clippingRect.top; +		int clippingRect_x2 = clippingRect.right; +		int clippingRect_y2 = clippingRect.bottom; +		buildSpriteScalingTable(160, posZ); +		while (1) { +			int dstX = (int16)READ_LE_UINT16(frameDataBase); frameDataBase += 2; +			if (dstX == 10000) { +				_moveKeyCharRect = Common::Rect(x1, y1, x2 + 1, y2 + 1); +				break; +			} +			int dstY = (int16)READ_LE_UINT16(frameDataBase); frameDataBase += 2; + +			int frameDir = READ_LE_UINT16(frameDataBase); frameDataBase += 2; +//			assert((frameDir & 0x4000) == 0); // hflipped +			bool vflipped = (frameDir & 0x8000) != 0; + +			frameDir &= 0xFFF; +			if (frameDirFlag) { +				frameDir = READ_LE_UINT16(sequenceData + frameDir * 2); +			} +			if (keyChar == 0) { +				if (_directionsTable[frameDir] <= _flagsTable[176]) { +					continue; +				} +			} +			if (frameDir == 0x800) { +				continue; +			} + +			assert(spr->w != 0); +			int framesPerLine = spr->bitmapWidth / spr->w; +			assert(framesPerLine != 0); +			const int srcOffsX = spr->w * (frameDir % framesPerLine); +			const int srcOffsY = spr->h * (frameDir / framesPerLine); +			const uint8 *srcSpr = spr->ptr + srcOffsY * spr->bitmapWidth + srcOffsX; + +			assert(dstY >= -500 && dstY < 500); +			int scalingIndex = _spriteScalingIndex[500 + dstY]; +			int16 *yScaledTable = &_spriteScalingTable[scalingIndex]; +			int sprScaledY = posY + scalingIndex - 500; +			y1 = MIN(y1, sprScaledY); + +			if (facingDirection == 3) { +				dstX = -dstX; +			} +			assert(dstX >= -500 && dstX < 500); +			scalingIndex = _spriteScalingIndex[500 + dstX]; +			int sprScaledX = posX + scalingIndex - 500; +			int16 *xScaledTable = &_spriteScalingTable[scalingIndex]; +			x1 = MIN(x1, sprScaledX); +			x2 = MAX(x2, sprScaledX); + +			uint8 *dstCur = dst + sprScaledY * dstPitch + sprScaledX; + +			uint8 *dstStart = dstCur; +			int sprStartY = 0; +			while (1) { +				int sprCurY = *yScaledTable - dstY; ++yScaledTable; +				if (sprCurY >= spr->h) { +					break; +				} +				sprStartY = sprCurY - sprStartY; +				while (sprStartY != 0) { +					srcSpr += spr->bitmapWidth; +					--sprStartY; +				} +				sprStartY = sprCurY; + +				int16 *scalingTable = xScaledTable; +				int spr_x2 = sprScaledX; +				dstCur = dstStart; +				if (sprScaledY < clippingRect_y1 || sprScaledY >= clippingRect_y2) { +					continue; +				} +				if (facingDirection != 3) { +					while (1) { +						int spr_x1 = *scalingTable - dstX; ++scalingTable; +						if (spr_x1 >= spr->w || spr_x2 >= clippingRect_x2) { +							break; +						} +						if (spr_x2 >= clippingRect_x1) { +							uint8 color = vflipped ? srcSpr[spr->w - 1 - spr_x1] : srcSpr[spr_x1]; +							if (color != 0) { +								*dstCur = color; +							} +						} +						++spr_x2; +						++dstCur; +					} +					x2 = MAX(x2, spr_x2); +				} else { +					while (1) { +						int spr_x1 = dstX - *scalingTable; --scalingTable; +						if (spr_x1 >= spr->w || spr_x2 < clippingRect_x1) { +							break; +						} +						if (spr_x2 < clippingRect_x2) { +							uint8 color = vflipped ? srcSpr[spr->w - 1 - spr_x1] : srcSpr[spr_x1]; +							if (color != 0) { +								*dstCur = color; +							} +						} +						--spr_x2; +						--dstCur; +					} +					x1 = MIN(x1, spr_x2); +				} +				++sprScaledY; +				dstStart += dstPitch; +			} +		} +	} +	if (walkDataNum != -1) { +		if (_flagsTable[604] == 0) { +			int area1 = _programWalkTable[walkDataNum].area1; +			if (area1 != 0) { +				findAndRedrawRoomRegion(area1); +			} +			int area2 = _programWalkTable[walkDataNum].area2; +			if (area2 != 0) { +				findAndRedrawRoomRegion(area2); +			} +		} +	} +} + +void ToucheEngine::changeKeyCharFrame(KeyChar *key, int keyChar) { +	key->currentAnimSpeed = 0; +	key->currentAnimCounter = 0; +	if (key->currentAnim != 1) { +		int16 animStart, animCount; +		if (_currentObjectNum == keyChar && _flagsTable[901] == 1) { +			animStart = key->anim1Start; +			animCount = key->anim1Count; +		} else if (key->framesListCount != key->currentFrame) { +			animStart = key->framesList[key->currentFrame]; +			++key->currentFrame; +			key->currentFrame &= 15; +			animCount = 0; +		} else { +			animStart = key->anim2Start; +			animCount = key->anim2Count; +			if (key->currentAnim >= animStart && key->currentAnim < animStart + animCount) { +				int rnd = getRandomNumber(100); +				if (key->flags & 0x10) { +					if (rnd >= 50 && rnd <= 55) { +						KeyChar *followingKey = &_keyCharsTable[key->followingKeyCharNum]; +						int16 num = followingKey->pointsDataNum; +						if (num != 0 && followingKey->currentWalkBox != -1 && num != key->followingKeyCharPos) { +							key->followingKeyCharPos = num; +							sortPointsData(-1, num); +							buildWalkPointsList(key->num - 1); +						} +					} +				} else { +					if (rnd >= 50 && rnd <= 51) { +						animStart = key->anim3Start; +						animCount = key->anim3Count; +					} +				} +			} +		} +		if (animCount != 0) { +			animCount = getRandomNumber(animCount); +		} +		key->currentAnim = animStart + animCount; +	} +} + +void ToucheEngine::setKeyCharRandomFrame(KeyChar *key) { +	key->currentAnimSpeed = 0; +	key->currentAnim = key->anim2Start + getRandomNumber(key->anim2Count); +	key->currentAnimCounter = 0; +} + +void ToucheEngine::setKeyCharMoney() { +	_keyCharsTable[_currentKeyCharNum].money = _currentAmountOfMoney; +	drawAmountOfMoneyInInventory(); +} + +const char *ToucheEngine::getString(int num) const { +	if (num < 0) { +		return (const char *)_textData + READ_LE_UINT32(_textData - num * 4); +	} else { +		return (const char *)_programTextDataPtr + READ_LE_UINT32(_programTextDataPtr + num * 4); +	} +} + +int ToucheEngine::getStringWidth(int m, int num) const { +	int w = 0; +	const char *str = getString(num); +	switch (m) { +	case 16: +		w = Graphics::getStringWidth16(str); +		break; +	} +	return w; +} + +void ToucheEngine::drawString(uint8 *dst, int dstPitch, int m, uint16 color, int x, int y, int16 num) { +	if (num) { +		const char *str = getString(num); +		switch (m) { +		case 16: +			Graphics::drawString16(dst, dstPitch, color, x, y, str); +			break; +		} +	} +} + +void ToucheEngine::drawGameString(int m, uint16 color, int x1, int y, const char *str) { +	int x, w; +	switch (m) { +	case 16: +		w = Graphics::getStringWidth16(str); +		x = x1 - w / 2; +		if (x + w >= 640) { +			x = 640 - w - 1; +		} +		while (*str) { +			char chr = *str++; +			if (chr == '\\') { +				y += 16; +				w = Graphics::getStringWidth16(str); +				x = x1 - w / 2; +			} else { +				if (x < 0) { +					x = 0; +				} +				x += Graphics::drawChar16(_offscreenBuffer, 640, chr, x, y, color); +			} +		} +		break; +	} +} + +int ToucheEngine::restartKeyCharScriptOnAction(int action, int obj1, int obj2) { +	debugC(9, kDebugEngine, "ToucheEngine::restartKeyCharScriptOnAction(%d, %d, %d)", action, obj1, obj2); +	for (uint i = 0; i < _programActionScriptOffsetTable.size(); ++i) { +		const ProgramActionScriptOffsetData *pasod = &_programActionScriptOffsetTable[i]; +		if (pasod->object1 == obj1 && pasod->action == action && pasod->object2 == obj2) { +			debug(0, "Found matching action i=%d %d,%d,%d\n", i, pasod->action, pasod->object1, pasod->object2); +			KeyChar *key = &_keyCharsTable[_currentKeyCharNum]; +			key->scriptDataOffset = pasod->offset; +			key->scriptStackPtr = &key->scriptStackTable[39]; +			key->flags &= ~(kScriptStopped | kScriptPaused); +			return 1; +		} +	} +	return 0; +} + +void ToucheEngine::buildSpriteScalingTable(int z1, int z2) { +	debugC(9, kDebugEngine, "ToucheEngine::buildSpriteScalingTable(%d, %d)", z1, z2); +	if (z2 > 500) { +		z2 = 500; +	} +	if (z2 == 0) { +		z2 = 1; +	} + +	const int scaleInc = z1 * 256 / z2; +	int scaleSum = 0; +	for (int i = 0; i < z2; ++i) { +		int value = scaleSum >> 8; +		assert(i < 500); +		_spriteScalingTable[500 + i] =  value; +		_spriteScalingTable[500 - i] = -value; +		scaleSum += scaleInc; +	} + +	const int16 *p = &_spriteScalingTable[500]; +	int16 z1_s = *p++; +	int16 z2_s = *p++; +	for (int i = 0, j = 0; j < z1; ++i) { +		while (z2_s != z1_s) { +			++z1_s; +			assert(j < 500); +			_spriteScalingIndex[500 + j] = i + 500; +			_spriteScalingIndex[500 - j] = 500 - i; +			if (j++ >= z1) { +				break; +			} +		} +		z1_s = z2_s; +		z2_s = *p++; +	} +} + +void ToucheEngine::drawSpriteOnBackdrop(int num, int x, int y) { +	assert(num >= 0 && num < NUM_SPRITES); +	SpriteData *spr = &_spritesTable[num]; +	Graphics::copyRect(_backdropBuffer, _currentBitmapWidth, x, y, +	  spr->ptr, spr->bitmapWidth, 0, 0, +	  spr->bitmapWidth, spr->bitmapHeight); +} + +void ToucheEngine::updateTalkFrames(int keyChar) { +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	KeyChar *key = &_keyCharsTable[keyChar]; +	if (key->currentAnim >= key->anim1Start && key->currentAnim < key->anim1Start + key->anim1Count) { +		key->currentAnim = key->anim2Start; +		key->currentAnimCounter = 0; +		key->currentAnimSpeed = 0; +	} +} + +void ToucheEngine::setKeyCharTalkingFrame(int keyChar) { +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	KeyChar *key = &_keyCharsTable[keyChar]; +	if (key->currentAnim != 1) { +		key->currentAnim = key->anim1Start; +		key->currentAnimCounter = 0; +		key->currentAnimSpeed = 0; +	} +} + +void ToucheEngine::lockUnlockHitBox(int num, int lock) { +	for (uint i = 0; i < _programHitBoxTable.size(); ++i) { +		if (_programHitBoxTable[i].item == num) { +			if (lock) { +				_programHitBoxTable[i].hitBoxes[0].top |= 0x4000; +			} else { +				_programHitBoxTable[i].hitBoxes[0].top &= ~0x4000; +			} +		} +	} +} + +void ToucheEngine::drawHitBoxes() { +	for (uint i = 0; i < _programHitBoxTable.size(); ++i) { +		uint16 state = _programHitBoxTable[i].state; +		if (state & 0x8000) { +			_programHitBoxTable[i].state = state & 0x7FFF; +			addToDirtyRect(_programHitBoxTable[i].hitBoxes[1]); +		} +	} +} + +void ToucheEngine::setCursor(int num) { +	debugC(9, kDebugEngine, "ToucheEngine::setCursor(%d)", num); +	_currentCursorObject = num; +	const int cursorW = 58; +	const int cursorH = 42; +	res_loadImage(num, _mouseData); +	_system->setMouseCursor(_mouseData, cursorW, cursorH, cursorW / 2, cursorH / 2, 0); +	_system->showMouse(true); +} + +void ToucheEngine::updateCursor(int num) { +	debugC(9, kDebugEngine, "ToucheEngine::updateCursor(%d)", num); +	if (_currentCursorObject != 0) { +		if (_currentCursorObject != 1) { +			addItemToInventory(num, _currentCursorObject); +			drawInventory(num, 1); +		} +		setCursor(0); +	} +} + +void ToucheEngine::handleMouseButtonClicked() { +	for (int i = 0; i < 13; ++i) { +		if (_inventoryAreasTable[i].contains(_inp_mousePos)) { +			switch (i) { +			case 0: +				_keyCharsTable[_currentKeyCharNum].money += _currentAmountOfMoney; +				_currentAmountOfMoney = 0; +				ui_handleOptions(0); +				break; +			case 1: +				setKeyCharMoney(); +				if (_currentCursorObject == 1) { +					setCursor(0); +				} +				break; +			case 2: +				if (_keyCharsTable[_currentKeyCharNum].money >= 10) { +					_keyCharsTable[_currentKeyCharNum].money -= 10; +					_currentAmountOfMoney += 10; +				} +				break; +			case 3: +				if (_keyCharsTable[_currentKeyCharNum].money != 0) { +					--_keyCharsTable[_currentKeyCharNum].money; +					++_currentAmountOfMoney; +					drawAmountOfMoneyInInventory(); +				} +				break; +			case 4: +				if (_currentAmountOfMoney != 0) { +					updateCursor(_objectDescriptionNum); +					int money = _currentAmountOfMoney; +					_currentAmountOfMoney = 0; +					drawAmountOfMoneyInInventory(); +					setCursor(1); +					_currentAmountOfMoney = money; +				} +				break; +			case 5: +				if (*_inventoryVar2 != 0) { +					*_inventoryVar2 -= 6; +					drawInventory(_objectDescriptionNum, 1); +				} +				break; +			case 12: +				if (_inventoryVar1[12 + *_inventoryVar2] != 0) { +					*_inventoryVar2 += 6; +					drawInventory(_objectDescriptionNum, 1); +				} +				break; +			default: +				if (i >= 6 && i <= 11) { +					int item = _inventoryVar1[i - 6 + *_inventoryVar2]; +					_flagsTable[119] = _currentCursorObject; +					if (_currentCursorObject == 1) { +						setKeyCharMoney(); +						_flagsTable[118] = _currentAmountOfMoney; +						_currentAmountOfMoney = 0; +					} +					if (item != 0 && _currentCursorObject != 0) { +						if (restartKeyCharScriptOnAction(-53, item | 0x1000, 0)) { +							updateCursor(_objectDescriptionNum); +							drawInventory(_objectDescriptionNum, 1); +						} +					} else { +						_inventoryVar1[i - 6 + *_inventoryVar2] = 0; +						if (_currentCursorObject != 0) { +							updateCursor(_objectDescriptionNum); +						} +						if (item != 0) { +							setCursor(item); +							packInventoryItems(0); +							packInventoryItems(1); +						} +						drawInventory(_objectDescriptionNum, 1); +					} +				} +				break; +			} +			break; +		} +	} +} + +void ToucheEngine::handleMouseButtonPressed() { +	for (int pos = 0; pos < 13; ++pos) { +		const Common::Rect &r = _inventoryAreasTable[pos]; +		if (r.contains(_inp_mousePos)) { +			if (pos >= 6 && pos <= 11) { +				int item = _inventoryVar1[pos - 6 + *_inventoryVar2] | 0x1000; +				for (uint i = 0; i < _programHitBoxTable.size(); ++i) { +					const ProgramHitBoxData *hitBox = &_programHitBoxTable[i]; +					if (hitBox->item == item) { +						const int menuX = r.left + r.width() / 2; +						const int menuY = 352; +						int act = handleActionMenuUnderCursor(hitBox->actions, menuX, menuY, hitBox->str); +						if (act != 0) { +							restartKeyCharScriptOnAction(act, hitBox->item, 0); +						} +					} +				} +				break; +			} +		} +	} +} + +void ToucheEngine::handleMouseInput(int flag) { +	if (_disabledInputCounter != 0 || _flagsTable[618] != 0) { +		_inp_mouseButtonPressed = false; +	} +	if (_inp_mousePos.y < _roomAreaRect.height()) { +		handleMouseInputRoomArea(flag); +	} else { +		handleMouseInputInventoryArea(flag); +	} +} + +void ToucheEngine::handleMouseInputRoomArea(int flag) { +	if (_hideInventoryTexts && _conversationReplyNum != -1 && !_conversationAreaCleared) { +		drawConversationString(_conversationReplyNum, 0xD6); +	} +	if (_disabledInputCounter == 0 && !_hideInventoryTexts && _flagsTable[618] == 0) { +		bool itemSelected = false; +		bool stringDrawn = false; +		if (_conversationReplyNum != -1 && !_conversationAreaCleared && _giveItemToCounter == 0) { +			drawConversationString(_conversationReplyNum, 0xD6); +		} +		_conversationReplyNum = -1; +		int keyCharNewPosX = _flagsTable[614] + _inp_mousePos.x; +		int keyCharNewPosY = _flagsTable[615] + _inp_mousePos.y; +		for (uint i = 0; i < _programHitBoxTable.size(); ++i) { +			if (_programHitBoxTable[i].item & 0x1000) { +				break; +			} +			bool itemDisabled = false; +			Common::Rect *hitBox = &_programHitBoxTable[i].hitBoxes[0]; +			int hitPosX = keyCharNewPosX; +			int hitPosY = keyCharNewPosY; +			int16 str = _programHitBoxTable[i].str; +			KeyChar *keyChar; +			switch (_programHitBoxTable[i].item & 0xF000) { +			case 0x1000: +				if (_inventoryItemsInfoTable[_programHitBoxTable[i].item & ~0x1000] != 0x20) { +					hitPosY = 10000; +				} +				break; +			case 0x2000: +				itemDisabled = true; +				break; +			case 0x4000: +				keyChar = &_keyCharsTable[_programHitBoxTable[i].item & ~0x4000]; +				hitPosY = 10000; +				if (keyChar->num != 0) { +					if ((keyChar->flags & 0x4000) == 0) { +						if (keyChar->strNum != 0) { +							str = keyChar->strNum; +						} +						hitBox = &keyChar->prevBoundingRect; +						hitPosX = _inp_mousePos.x; +						hitPosY = _inp_mousePos.y; +					} +				} +				break; +			} +			if (_giveItemToCounter == 0 && !_hideInventoryTexts) { +				if (hitBox->contains(hitPosX, hitPosY)) { +					if (!itemDisabled) { +						if (_inp_mouseButtonClicked && _currentCursorObject != 0) { +							_inp_mouseButtonClicked = false; +							itemSelected = true; +							_flagsTable[119] = _currentCursorObject; +							if (_currentCursorObject == 1) { +								_flagsTable[118] = _currentAmountOfMoney; +								_currentAmountOfMoney = 0; +							} +							_inventoryItemsInfoTable[_currentCursorObject] = 0x20; +							setCursor(0); +							if (_giveItemToCounter == 0) { +								if (!restartKeyCharScriptOnAction(-53, _programHitBoxTable[i].item, 0)) { +									if (_flagsTable[119] == 1) { +										_currentAmountOfMoney = _flagsTable[118]; +									} else { +										addItemToInventory(_currentKeyCharNum, _flagsTable[119]); +										drawInventory(_currentKeyCharNum, 1); +									} +									drawAmountOfMoneyInInventory(); +								} +							} else { +								_flagsTable[117] = _programHitBoxTable[i].item - 1; +								_giveItemToCounter = -1; +							} +						} +						const char *strData = getString(str); +						int strPosY = _inp_mousePos.y - 22; +						if (_currentCursorObject != 0) { +							strPosY -= 8; +						} +						if (strPosY <= 0) { +							strPosY = 1; +						} +						int strWidth = getStringWidth(16, str); +						int strPosX = _inp_mousePos.x - strWidth / 2; +						strPosX = CLIP<int>(strPosX, 0, 640 - strWidth - 1); +						if (_talkTextSpeed != 0) { +							--_talkTextSpeed; +						} +						if (!stringDrawn && _talkTextSpeed == 0) { +							drawGameString(16, 0xFF, strPosX + strWidth / 2, strPosY, strData); +						} +						stringDrawn = true; +						Common::Rect redrawRect(strPosX, strPosY, strPosX + strWidth, strPosY + 16); +						if (_programHitBoxTable[i].state & 0x8000) { +							redrawRect.extend(_programHitBoxTable[i].hitBoxes[1]); +						} +						addToDirtyRect(redrawRect); +						_programHitBoxTable[i].hitBoxes[1] = Common::Rect(strPosX, strPosY, strPosX + strWidth, strPosY + 16); +						_programHitBoxTable[i].state |= 0x8000; +					} +					if (_inp_mouseButtonClicked) { +						_inp_mouseButtonClicked = false; +						if (_currentCursorObject != 0) { +							updateCursor(_currentKeyCharNum); +						} else { +							drawInventory(_currentKeyCharNum, 0); +							if (restartKeyCharScriptOnAction(-49, _programHitBoxTable[i].item, 0) == 0) { +								buildWalkPath(keyCharNewPosX, keyCharNewPosY, _currentKeyCharNum); +							} +						} +					} else { +						if (_inp_mouseButtonPressed && !itemDisabled && !itemSelected) { +							int act = handleActionMenuUnderCursor(_programHitBoxTable[i].actions, _inp_mousePos.x, _inp_mousePos.y, str); +							_inp_mouseButtonPressed = false; +							int16 facing = (keyCharNewPosX <= _keyCharsTable[_currentKeyCharNum].xPos) ? 3 : 0; +							_keyCharsTable[_currentKeyCharNum].facingDirection = facing; +							if (act != 0) { +								restartKeyCharScriptOnAction(act, _programHitBoxTable[i].item, 0); +							} else { +								act = _programHitBoxTable[i].talk; +								if (act != 0) { +									addToTalkTable(0, act, _currentKeyCharNum); +								} +							} +						} +					} +				} else if (_programHitBoxTable[i].state & 0x8000) { +					_programHitBoxTable[i].state &= 0x7FFF; +					addToDirtyRect(_programHitBoxTable[i].hitBoxes[1]); +				} +			} +		} +		if (_inp_mouseButtonClicked) { +			_inp_mouseButtonClicked = false; +			if (_currentCursorObject != 0) { +				if (_currentCursorObject != 1) { +					addItemToInventory(_currentKeyCharNum, _currentCursorObject); +					drawInventory(_objectDescriptionNum, 1); +				} +				setCursor(0); +			} else { +				drawInventory(_currentKeyCharNum, 0); +				buildWalkPath(keyCharNewPosX, keyCharNewPosY, _currentKeyCharNum); +			} +		} +	} else { +		if (flag) { +			drawHitBoxes(); +		} +	} +} + +void ToucheEngine::handleMouseInputInventoryArea(int flag) { +	if (flag) { +		drawHitBoxes(); +	} +	if (_hideInventoryTexts && _giveItemToCounter == 0) { +		if (!_conversationAreaCleared) { +			if (_inp_mousePos.x >= 40) { +				if (_inp_mousePos.y >= 328) { +					int replyNum = (_inp_mousePos.y - 328) / 16; +					if (replyNum >= 4) { +						replyNum = 3; +					} +					if (replyNum != _conversationReplyNum) { +						if (_conversationReplyNum != -1) { +							drawConversationString(_conversationReplyNum, 0xD6); +						} +						drawConversationString(replyNum, 0xFF); +						_conversationReplyNum = replyNum; +					} +					if (_inp_mouseButtonClicked) { +						_inp_mouseButtonClicked = false; +						setupConversationScript(replyNum); +						_conversationReplyNum = -1; +					} +				} +			} else { +				if (_conversationReplyNum != -1 && !_conversationAreaCleared) { +					drawConversationString(_conversationReplyNum, 0xD6); +				} +				_conversationReplyNum = -1; +				if (_inp_mouseButtonClicked) { +					int replyNum = _inp_mousePos.y - _roomAreaRect.height(); +					if (replyNum < 40) { +						drawCharacterConversationRepeat(); +					} else { +						drawCharacterConversationRepeat2(); +					} +					_inp_mouseButtonClicked = false; +				} +			} +		} +	} else if (_disabledInputCounter == 0 && !_hideInventoryTexts) { +		if (_inp_mouseButtonClicked) { +			handleMouseButtonClicked(); +			_inp_mouseButtonClicked = false; +		} +		if (_inp_mouseButtonPressed) { +			handleMouseButtonPressed(); +			_inp_mouseButtonPressed = false; +		} +	} +} + +void ToucheEngine::scrollScreenToPos(int num) { +	_screenOffset.x = _programPointsTable[num].x - 640 / 2; +	_screenOffset.y = _programPointsTable[num].y - 400 / 2; +} + +void ToucheEngine::clearRoomArea() { +	int h = (_flagsTable[606] != 0) ? 400 : _roomAreaRect.height(); +	Graphics::fillRect(_offscreenBuffer, 640, 0, 0, 640, h, 0); +	updateEntireScreen(); +} + +void ToucheEngine::startNewMusic() { +	_snd_midiContext.unkA = _flagsTable[619] & 0xFF; +	if (_newMusicNum != 0 && _newMusicNum != _currentMusicNum) { +		_snd_midiContext.unkB = 3; +		if (_snd_midiContext.unkF != 0 && _snd_midiContext.unk2 != 0) { +			return; +		} +		_snd_midiContext.unkB = 0; +		res_loadMusic(_newMusicNum); +		_snd_midiContext.unk2 = 0; +		_newMusicNum = 0; +	} +} + +void ToucheEngine::startNewSound() { +	if (_newSoundNum != 0) { +		if (_newSoundDelay == 0) { +			res_loadSound(_newSoundPriority, _newSoundNum); +			_newSoundNum = 0; +		} else { +			--_newSoundDelay; +		} +	} +} + +void ToucheEngine::updateSpeech() { +	if (_speechPlaying) { +		if (!_mixer->isSoundHandleActive(_speechHandle)) { +			_speechPlaying = false; +			_defaultSoundPriority = 0; +		} +	} +} + +int ToucheEngine::handleActionMenuUnderCursor(const int16 *actions, int offs, int y, int str) { +	if (*actions == 0 || _redrawScreenCounter1 != 0) { +		return -26; +	} +	int i; +	int16 actionsTable[10]; +	int16 *currentAction = actionsTable; +	int drawY = 0; +	for (i = 0; i < 8; ++i) { +		int act = *actions++; +		if (act == 0) { +			break; +		} +		if (act != -49 && act != -53) { +			*currentAction++ = act; +			drawY = 1; +		} +	} +	if (drawY == 0) { +		return -26; +	} +	*currentAction = 0; +	int strW = getStringWidth(16, str); +	int h = 0; +	for (i = 0; i < 10; ++i) { +		if (actionsTable[i] == 0) { +			break; +		} +		++h; +		drawY = getStringWidth(16, actionsTable[i]); +		if (drawY > strW) { +			strW = drawY; +		} +	} +	int cursorW = strW + 28; +	int cursorPosX = CLIP<int16>(offs - cursorW / 2, 0, 640 - cursorW); +	offs = cursorPosX + 14; +	h *= 16; +	int cursorH = h + 28; +	int cursorPosY = CLIP<int16>(y - 24, 0, 352 - cursorH); +	y = cursorPosY + 24; +	_cursorObjectRect = Common::Rect(cursorPosX, cursorPosY, cursorPosX + cursorW, cursorPosY + cursorH); +	addToDirtyRect(_cursorObjectRect); + +	Graphics::fillRect(_offscreenBuffer, 640, cursorPosX + 14, cursorPosY + 24, cursorW - 28, cursorH - 40, 0xF8); +	ui_drawActionsPanel(cursorPosX, cursorPosY, cursorW, cursorH); + +	const char *strData = getString(str); +	drawGameString(16, 0xF8FF, offs + strW / 2, cursorPosY + 4, strData); +	for (i = 0; i < 10; ++i) { +		if (actionsTable[i] == 0) { +			break; +		} +		drawString(_offscreenBuffer, 640, 16, 0xF8F9, offs, y + i * 16, actionsTable[i]); +	} +	updateScreenArea(_offscreenBuffer, 640, cursorPosX, cursorPosY, cursorPosX, cursorPosY, cursorW, cursorH); + +	_redrawScreenCounter1 = 2; +	Common::Rect rect(0, y, 640, y + h); +	i = -1; +	while (_inp_mouseButtonPressed) { +		if (rect.contains(_inp_mousePos)) { +			int c = (_inp_mousePos.y - y) / 16; +			if (c != i) { +				if (i >= 0) { +					drawY = y + i * 16; +					drawString(_offscreenBuffer, 640, 16, 0xF8F9, offs, drawY, actionsTable[i]); +					updateScreenArea(_offscreenBuffer, 640, offs, drawY, offs, drawY, strW, 16); +				} +				i = c; +				drawY = y + i * 16; +				drawString(_offscreenBuffer, 640, 16, 0xF8FF, offs, drawY, actionsTable[i]); +				updateScreenArea(_offscreenBuffer, 640, offs, drawY, offs, drawY, strW, 16); +			} +		} else if (i >= 0) { +			drawY = y + i * 16; +			drawString(_offscreenBuffer, 640, 16, 0xF8F9, offs, drawY, actionsTable[i]); +			updateScreenArea(_offscreenBuffer, 640, offs, drawY, offs, drawY, strW, 16); +			i = -1; +		} + +		OSystem::Event event; +		while (_system->pollEvent(event)) { +			switch (event.type) { +			case OSystem::EVENT_QUIT: +				_flagsTable[611] = 1; +				break; +			case OSystem::EVENT_MOUSEMOVE: +				_inp_mousePos.x = event.mouse.x; +				_inp_mousePos.y = event.mouse.y; +				break; +			case OSystem::EVENT_RBUTTONDOWN: +				_inp_mousePos.x = event.mouse.x; +				_inp_mousePos.y = event.mouse.y; +				_inp_mouseButtonPressed = true; +				break; +			case OSystem::EVENT_RBUTTONUP: +				_inp_mousePos.x = event.mouse.x; +				_inp_mousePos.y = event.mouse.y; +				_inp_mouseButtonPressed = false; +				break; +			default: +				break; +			} +		} +		_system->delayMillis(50); +	} + +	const int action = (i >= 0) ? actionsTable[i] : -26; +	return action; +} + +void ToucheEngine::redrawBackground() { +	for (uint i = 0; i < _programBackgroundTable.size(); ++i) { +		Area area = _programBackgroundTable[i].area; +		if (area.r.top != 20000) { +			area.r.translate(-_flagsTable[614], -_flagsTable[615]); +			if (_programBackgroundTable[i].type == 4) { +				int16 dx = _programBackgroundTable[i].offset - 640 / 2 - _flagsTable[614]; +				dx *= _programBackgroundTable[i].scaleMul; +				dx /= _programBackgroundTable[i].scaleDiv; +				area.r.translate(dx, 0); +			} +			if (area.clip(_roomAreaRect)) { +				Graphics::copyRect(_offscreenBuffer, 640, area.r.left, area.r.top, +				  _backdropBuffer, _currentBitmapWidth, area.srcX, area.srcY, +				  area.r.width(), area.r.height(), +				  Graphics::kTransparent); +				addToDirtyRect(area.r); +			} +		} +	} +} + +void ToucheEngine::processAreaTable() { +	debugC(9, kDebugEngine, "ToucheEngine::processAreaTable()"); +//	for (int i = 0; i < _areaTableCount; ++i) { +//		Rect r(_areaTable[i].r); +//		if (rectClip(&_roomAreaRect, &r)) { +//			addToDirtyRect(&r); +//		} +//	} +} + +void ToucheEngine::clearAreaTable() { +	debugC(9, kDebugEngine, "ToucheEngine::clearAreaTable()"); +	_areaTableCount = 0; +} + +void ToucheEngine::addToAreaTable(const Area *area) { +	debugC(9, kDebugEngine, "ToucheEngine::addToAreaTable()"); +	assert(_areaTableCount < NUM_AREAS); +	_areaTable[_areaTableCount] = *area; +	++_areaTableCount; +} + +void ToucheEngine::addRoomArea(int num, int flag) { +	debugC(9, kDebugEngine, "ToucheEngine::addRoomArea(%d, %d)", num, flag); +	if (_flagsTable[flag] == 20000) { +		Area area = _programBackgroundTable[num].area; +		area.r.translate(-_flagsTable[614], -_flagsTable[615]); +		if (area.clip(_roomAreaRect)) { +			addToAreaTable(&area); +		} +	} +	_programBackgroundTable[num].area.r.moveTo(_flagsTable[flag], _flagsTable[flag + 1]); +} + +void ToucheEngine::updateRoomAreas(int num, int flags) { +	debugC(9, kDebugEngine, "ToucheEngine::updateRoomAreas(%d, %d)", num, flags); +	if (flags != -1) { +		int16 count = _updatedRoomAreasTable[0]; +		++_updatedRoomAreasTable[0]; +		if (count == 199) { +			_updatedRoomAreasTable[0] = 2; +			count = 1; +		} +		_updatedRoomAreasTable[count] = (uint8)num; +	} +	for (uint i = 0; i < _programAreaTable.size(); ++i) { +		if (_programAreaTable[i].id == num) { +			Area area = _programAreaTable[i].area; +			Graphics::copyRect(_backdropBuffer, _currentBitmapWidth, area.r.left, area.r.top, +			  _backdropBuffer, _currentBitmapWidth, area.srcX, area.srcY, +			  area.r.width(), area.r.height(), +			  Graphics::kTransparent); +			if (flags != 0) { +				area.r.translate(-_flagsTable[614], -_flagsTable[615]); +				if (area.clip(_roomAreaRect)) { +					addToAreaTable(&area); +				} +			} +		} +	} +} + +void ToucheEngine::setRoomAreaState(int num, uint16 state) { +	debugC(9, kDebugEngine, "ToucheEngine::setRoomAreaState(%d, %d)", num, state); +	for (uint i = 0; i < _programAreaTable.size(); ++i) { +		if (_programAreaTable[i].id == num) { +			_programAreaTable[i].state = state; +		} +	} +} + +void ToucheEngine::findAndRedrawRoomRegion(int num) { +	debugC(9, kDebugEngine, "ToucheEngine::findAndRedrawRoomRegion(%d)", num); +	for (uint i = 0; i < _programAreaTable.size(); ++i) { +		if (_programAreaTable[i].id == num) { +			redrawRoomRegion(i, 0); +			break; +		} +	} +} + +void ToucheEngine::updateRoomRegions() { +	debugC(9, kDebugEngine, "ToucheEngine::updateRoomRegions()"); +	if (_flagsTable[269] == 0) { +		uint i = 0; +		while (i < _programAreaTable.size() && _programAreaTable[i].id != 0) { +			switch (_programAreaTable[i].state) { +			case 0: +				++i; +				break; +			case 1: +				redrawRoomRegion(i + _programAreaTable[i].animNext, true); +				++_programAreaTable[i].animNext; +				if (_programAreaTable[i].animNext >= _programAreaTable[i].animCount) { +					_programAreaTable[i].animNext = 0; +				} +				i += _programAreaTable[i].animCount; +				break; +			case 3: +				redrawRoomRegion(i + _programAreaTable[i].animNext, true); +				++_programAreaTable[i].animNext; +				if (_programAreaTable[i].animNext >= _programAreaTable[i].animCount) { +					_programAreaTable[i].animNext = 0; +				} +				i += _programAreaTable[i].animCount + 1; +				break; +			} +		} +	} +} + +void ToucheEngine::redrawRoomRegion(int num, bool markForRedraw) { +	debugC(9, kDebugEngine, "ToucheEngine::redrawRoomRegion(%d)", num); +	Area area = _programAreaTable[num].area; +	area.r.translate(-_flagsTable[614], -_flagsTable[615]); +	if (area.clip(_roomAreaRect)) { +		Graphics::copyRect(_offscreenBuffer, 640, area.r.left, area.r.top, +		  _backdropBuffer, _currentBitmapWidth, area.srcX, area.srcY, +		  area.r.width(), area.r.height(), +		  Graphics::kTransparent); +		if (markForRedraw) { +			addToDirtyRect(area.r); +		} +	} +} + +void ToucheEngine::initInventoryObjectsTable() { +	for (int i = 0; i < NUM_INVENTORY_ITEMS; ++i) { +		_inventoryItemsInfoTable[i] = 0x20; +	} +} + +void ToucheEngine::initInventoryLists() { +	memset(_inventoryList1, 0, sizeof(_inventoryList1)); +	_inventoryList1[100] = -1; +	_inventoryListPtrs[0] = _inventoryList1; +	_inventoryListCount[3 * 0 + 0] = 0; // start offset +	_inventoryListCount[3 * 0 + 1] = 100; // max number of items +	_inventoryListCount[3 * 0 + 2] = 6; // items per inventory line + +	memset(_inventoryList2, 0, sizeof(_inventoryList2)); +	_inventoryList2[100] = -1; +	_inventoryListPtrs[1] = _inventoryList2; +	_inventoryListCount[3 * 1 + 0] = 0; +	_inventoryListCount[3 * 1 + 1] = 100; +	_inventoryListCount[3 * 1 + 2] = 6; + +	memset(_inventoryList3, 0, sizeof(_inventoryList3)); +	_inventoryList3[6] = -1; +	_inventoryListPtrs[2] = _inventoryList3; +	_inventoryListCount[3 * 2 + 0] = 0; +	_inventoryListCount[3 * 2 + 1] = 6; +	_inventoryListCount[3 * 2 + 2] = 6; +} + +void ToucheEngine::drawInventory(int index, int flag) { +	if (_flagsTable[606] == 0) { +		if (index > 1) { +			index = 1; +		} +		if (_objectDescriptionNum == index && flag == 0) { +			return; +		} +		_inventoryVar1 = _inventoryListPtrs[index]; +		_inventoryVar2 = &_inventoryListCount[index * 3]; +		_objectDescriptionNum = index; +		uint8 *dst = _offscreenBuffer + 640 * 352; +		res_loadSpriteImage(index + 12, dst); +		res_loadImageHelper(dst, _currentImageWidth, _currentImageHeight); +		int firstObjNum = _inventoryVar2[0]; +		for (int i = 0, x = 242; i < 6; ++i, x += 58) { +			int num = _inventoryVar1[firstObjNum + i]; +			if (num == -1) { +				break; +			} +			if (num != 0) { +				drawIcon(x + 3, 353, num); +			} +		} +		drawAmountOfMoneyInInventory(); +		updateScreenArea(_offscreenBuffer, 640, 0, 352, 0, 352, 640, 48); +	} +} + +void ToucheEngine::drawAmountOfMoneyInInventory() { +	if (_flagsTable[606] == 0 && !_hideInventoryTexts) { +		char text[4]; +		itoa(_keyCharsTable[0].money, text, 10); +		Graphics::fillRect(_offscreenBuffer, 640, 74, 354, 40, 16, 0xD2); +		drawGameString(16, 217, 94, 355, text); +		updateScreenArea(_offscreenBuffer, 640, 74, 354, 74, 354, 40, 16); +		Graphics::fillRect(_offscreenBuffer, 640, 150, 353, 40, 41, 0xD2); +		if (_currentAmountOfMoney != 0) { +			drawIcon(141, 348, 1); +			itoa(_currentAmountOfMoney, text, 10); +			drawGameString(16, 217, 170, 378, text); +		} +		updateScreenArea(_offscreenBuffer, 640, 150, 353, 150, 353, 40, 41); +	} +} + +void ToucheEngine::packInventoryItems(int index) { +	int16 *p = _inventoryListPtrs[index]; +	for (int i = 0; *p != -1; ++i, ++p) { +		if (p[0] == 0 && p[1] != -1) { +			p[0] = p[1]; +			p[1] = 0; +		} +	} +} + +void ToucheEngine::appendItemToInventoryList(int index) { +	int last = _inventoryListCount[index * 3 + 1] - 1; +	int16 *p = _inventoryListPtrs[index]; +	if (p[last] != 0) { +		warning("Inventory %d Full", index); +	} else { +		for (int i = last; i > 0; --i) { +			p[i] = p[i - 1]; +		} +		*p = 0; +	} +} + +void ToucheEngine::addItemToInventory(int inventory, int16 item) { +	if (item == 0) { +		packInventoryItems(inventory); +	} else if (item == 1)  { +		_currentAmountOfMoney += _flagsTable[118]; +		drawAmountOfMoneyInInventory(); +	} else { +		appendItemToInventoryList(inventory); +		assert(inventory >= 0 && inventory < 3); +		int16 *p = _inventoryListPtrs[inventory]; +		for (int i = 0; *p != -1; ++i, ++p) { +			if (*p == 0) { +				*p = item; +				_inventoryItemsInfoTable[item] = inventory | 0x10; +				packInventoryItems(0); +				packInventoryItems(1); +				break; +			} +		} +	} +} + +void ToucheEngine::removeItemFromInventory(int inventory, int16 item) { +	if (item == 1) { +		_currentAmountOfMoney = 0; +		drawAmountOfMoneyInInventory(); +	} else { +		assert(inventory >= 0 && inventory < 3); +		int16 *p = _inventoryListPtrs[inventory]; +		for (int i = 0; *p != -1; ++i, ++p) { +			if (*p == item) { +				*p = 0; +				packInventoryItems(0); +				packInventoryItems(1); +				break; +			} +		} +	} +} + +void ToucheEngine::changeInventoryItemState(int flag, int itemNum, int itemRnd, int inventoryItem) { +	const int rnd = getRandomNumber(100) + 1; +	if (inventoryItem) { +		itemNum = _keyCharsTable[_currentKeyCharNum].inventoryItems[itemNum]; +	} +	if (_flagsTable[174]) { +		itemNum /= 2; +		_flagsTable[174] = 0; +	} +	int16 value; +	if (itemNum > itemRnd) { +		value = 1; +	} else if (rnd < itemNum / 6) { +		value = 0; +	} else if (rnd <= itemNum) { +		value = 1; +	} else if (rnd >= itemNum * 2) { +		value = 2; +	} else { +		value = 3; +	} +	_flagsTable[flag] = value; +} + +void ToucheEngine::resetTalkingVars() { +	_talkListCurrent = 0; +	_talkListEnd = 0; +	_keyCharTalkCounter = 0; +	_talkTextRectDefined = false; +	_talkTextDisplayed = false; +	_skipTalkText = false; +	_talkTextInitialized = false; +	if (_speechPlaying) { +		res_stopSpeech(); +	} +} + +int ToucheEngine::updateKeyCharTalk(int skipFlag) { +	if (skipFlag != 0) { +		if (_speechPlaying) { +			res_stopSpeech(); +		} +		if (_talkListEnd != _talkListCurrent) { +			_keyCharTalkCounter = 0; +			_talkTextInitialized = false; +			if (skipFlag == 2) { +				_skipTalkText = true; +			} else { +				_skipTalkText = false; +			} +		} +		return 0; +	} +	if (_talkListEnd == _talkListCurrent) { +		return 0; +	} +	int talkingKeyChar = _talkTable[_talkListCurrent].talkingKeyChar; +	int otherKeyChar = _talkTable[_talkListCurrent].otherKeyChar; +	KeyChar *key = &_keyCharsTable[talkingKeyChar]; +	int x = key->xPos - _flagsTable[614]; +	int y = key->yPos - _flagsTable[615] - (key->zPos / 2 + 16); +	int stringNum = _talkTable[_talkListCurrent].num; +	const char *stringData = getString(stringNum); +	int textWidth = getStringWidth(16, stringNum); +	if (!_talkTextInitialized && !_skipTalkText) { +		_keyCharTalkCounter = textWidth / 32 + 20; +		setKeyCharTalkingFrame(talkingKeyChar); +		res_loadSpeechSegment(stringNum); +		_talkTextInitialized = true; +	} +	if (_keyCharTalkCounter) { +		--_keyCharTalkCounter; +	} +	if (_speechPlaying) { +		_flagsTable[297] = 0; +		if (_talkTextMode == kTalkModeVoiceOnly) { +			_keyCharTalkCounter = 0; +		} +		_currentObjectNum = talkingKeyChar; +		if (_keyCharTalkCounter == 0) { +			_keyCharTalkCounter = 1; +		} +	} +	if (_talkTextMode == kTalkModeVoiceOnly) { +		if (_speechPlaying) { +			return 1; +		} +	} +	if (_keyCharTalkCounter != 0) { +		_currentObjectNum = talkingKeyChar; +		_talkTextDisplayed = true; +		int textHeight = 16; +		y -= 16; +		if (y < 0) { +			y = 1; +		} else if (y > 352) { +			y = 336; +		} +		if (textWidth > 200) { +			textWidth = 200; +			stringData = formatTalkText(16, &y, &textHeight, stringData); +		} +		x -= textWidth / 2; +		if (x < 0) { +			x = 0; +		} +		if (x + textWidth >= 640) { +			x = 640 - textWidth - 1; +		} +		drawGameString(16, key->textColor, x + textWidth / 2, y, stringData); +		_talkTextSpeed = 6; +		_talkTextRect = Common::Rect(x, y, x + textWidth, y + textHeight); +		if (_talkTextRectDefined) { +			_talkTextRect.extend(_talkTextRect2); +		} +		addToDirtyRect(_talkTextRect); +		_talkTextRect2 = Common::Rect(x, y, x + textWidth, y + textHeight); +		_talkTextRectDefined = true; +		_flagsTable[297] = 0; +	} else { +		updateTalkFrames(_currentObjectNum); +		_currentObjectNum = -1; +		if (_talkTextDisplayed) { +			addToDirtyRect(_talkTextRect2); +		} +		_talkTextInitialized = false; +		_skipTalkText = false; +		_talkTextRectDefined = false; +		++_talkListCurrent; +		if (_talkListCurrent == 16) { +			_talkListCurrent = 0; +		} +		if (otherKeyChar != -1) { +			_keyCharsTable[otherKeyChar].flags &= ~kScriptPaused; +		} +	} +	return 1; +} + +const char *ToucheEngine::formatTalkText(int mode, int *y, int *h, const char *text) { +	int newLineWidth = 0; +	int lineWidth = 0; +	char *textBuffer = _talkTextBuffer; +	char *textLine = textBuffer; +	if (mode != 16) { +		return text; +	} else { +		while (*text) { +			char chr = *text++; +			int chrWidth = Graphics::getCharWidth16(chr); +			lineWidth += chrWidth; +			if (chr == ' ') { +				if (lineWidth + newLineWidth >= 200) { +					*textLine = '\\'; +					newLineWidth = lineWidth - chrWidth; +					*y -= mode; +					*h += mode; +					lineWidth = chrWidth; +				} else { +					newLineWidth += lineWidth; +					lineWidth = chrWidth; +				} +				*textBuffer = ' '; +				textLine = textBuffer; +				textBuffer++; +			} else { +				*textBuffer++ = chr; +			} +		} +		if (newLineWidth + lineWidth >= 200) { +			*textLine = '\\'; +			*y -= mode; +			*h += mode; +		} +		*textBuffer = '\0'; +		if (*y < 0) { +			*y = 1; +		} +		return _talkTextBuffer; +	} +} + +void ToucheEngine::addToTalkTable(int talkingKeyChar, int num, int otherKeyChar) { +	if (_talkListEnd != _talkListCurrent) { +		if (_talkTableLastTalkingKeyChar == talkingKeyChar && +			_talkTableLastOtherKeyChar == otherKeyChar && +			_talkTableLastStringNum == num) { +			return; +		} +	} +	_talkTableLastTalkingKeyChar = talkingKeyChar; +	_talkTableLastOtherKeyChar = otherKeyChar; +	_talkTableLastStringNum = num; + +	removeFromTalkTable(otherKeyChar); + +	assert(_talkListEnd < NUM_TALK_ENTRIES); +	TalkEntry *talkEntry = &_talkTable[_talkListEnd]; +	talkEntry->talkingKeyChar = talkingKeyChar; +	talkEntry->otherKeyChar = otherKeyChar; +	talkEntry->num = num; + +	++_talkListEnd; +	if (_talkListEnd == NUM_TALK_ENTRIES) { +		_talkListEnd = 0; +	} +} + +void ToucheEngine::removeFromTalkTable(int keyChar) { +	debugC(9, kDebugEngine, "ToucheEngine::removeFromTalkTable(%d)", keyChar); +	int i = _talkListCurrent; +	while (i != _talkListEnd) { +		if (_talkTable[i].otherKeyChar == keyChar) { +			_talkTable[i].otherKeyChar = -1; +		} +		++i; +		i %= NUM_TALK_ENTRIES; +	} +} + +void ToucheEngine::addConversationChoice(int16 num) { +	debugC(9, kDebugEngine, "ToucheEngine::addConversationChoice(%d)", num); +	_conversationChoicesUpdated = true; +	int16 msg = _programConversationTable[_currentConversation + num].msg; +	for (int i = 0; i < NUM_CONVERSATION_CHOICES; ++i) { +		if (_conversationChoicesTable[i].msg == msg) { +			break; +		} +		if (_conversationChoicesTable[i].msg == 0) { +			_conversationChoicesTable[i].msg = msg; +			_conversationChoicesTable[i].num = num; +			break; +		} +	} +} + +void ToucheEngine::removeConversationChoice(int16 num) { +	debugC(9, kDebugEngine, "ToucheEngine::removeConversationChoice(%d)", num); +	for (int i = 0; i < NUM_CONVERSATION_CHOICES; ++i) { +		if (_conversationChoicesTable[i].num == num) { +			_conversationChoicesUpdated = true; +			for(; i < NUM_CONVERSATION_CHOICES - 1; ++i) { +				_conversationChoicesTable[i].num = _conversationChoicesTable[i + 1].num; +				_conversationChoicesTable[i].msg = _conversationChoicesTable[i + 1].msg; +			} +			break; +		} +	} +} + +void ToucheEngine::runConversationScript(uint16 offset) { +	debugC(9, kDebugEngine, "ToucheEngine::runConversationScript() offset=0x%X", offset); +	_script.dataOffset = offset; +	_script.quitFlag = 0; +	runCurrentKeyCharScript(2); +} + +void ToucheEngine::findConversationByNum(int16 num) { +	debugC(9, kDebugEngine, "ToucheEngine::findConversationByNum(%d)", num); +	for (uint i = 0; i < _programConversationTable.size(); ++i) { +		if (_programConversationTable[i].num == num) { +			clearConversationChoices(); +			_currentConversation = i; +			runConversationScript(_programConversationTable[i].offset); +			break; +		} +	} +} + +void ToucheEngine::clearConversationChoices() { +	debugC(9, kDebugEngine, "ToucheEngine::clearConversationChoices()"); +	_conversationChoicesUpdated = true; +	for (int i = 0; i < NUM_CONVERSATION_CHOICES; ++i) { +		_conversationChoicesTable[i].num = 0; +		_conversationChoicesTable[i].msg = 0; +	} +	_drawCharacterConversionRepeatCounter = 0; +} + +void ToucheEngine::drawCharacterConversationRepeat2() { +	if (_conversationChoicesTable[4 + _drawCharacterConversionRepeatCounter].msg != 0) { +		++_drawCharacterConversionRepeatCounter; +		drawCharacterConversation(); +	} +} + +void ToucheEngine::drawCharacterConversationRepeat() { +	if (_drawCharacterConversionRepeatCounter != 0) { +		--_drawCharacterConversionRepeatCounter; +		drawCharacterConversation(); +	} +} + +void ToucheEngine::drawCharacterConversation() { +	_conversationChoicesUpdated = false; +	if (!_disableConversationScript) { +		if (_conversationChoicesTable[0].msg == 0) { +			_conversationEnded = true; +			return; +		} +		if (_conversationChoicesTable[1].msg == 0) { +			setupConversationScript(0); +			return; +		} +	} +	ui_drawConversationPanel(); +	for (int i = 0; i < 4; ++i) { +		drawString(_offscreenBuffer, 640, 16, 214, 42, 328 + i * 16, _conversationChoicesTable[_drawCharacterConversionRepeatCounter + i].msg); +	} +	updateScreenArea(_offscreenBuffer, 640, 0, 320, 0, 320, 640, 80); +	_conversationAreaCleared = false; +} + +void ToucheEngine::drawConversationString(int num, uint16 color) { +	const int y = 328 + num * 16; +	drawString(_offscreenBuffer, 640, 16, color, 42, y, _conversationChoicesTable[num + _drawCharacterConversionRepeatCounter].msg); +	updateScreenArea(_offscreenBuffer, 640, 0, y, 0, y, 640, 16); +} + +void ToucheEngine::clearConversationArea() { +	ui_drawConversationPanel(); +	updateScreenArea(_offscreenBuffer, 640, 0, 320, 0, 320, 640, 80); +	_conversationAreaCleared = true; +} + +void ToucheEngine::setupConversationScript(int num) { +	debugC(9, kDebugEngine, "ToucheEngine::setupConversationScript(%d)", num); +	if (num < 5 && _conversationChoicesTable[num].msg != 0) { +		num = _conversationChoicesTable[_drawCharacterConversionRepeatCounter + num].num; +		KeyChar *key = &_keyCharsTable[_currentKeyCharNum]; +		key->scriptDataOffset = _programConversationTable[_currentConversation + num].offset; +		key->scriptStackPtr = &key->scriptStackTable[39]; +		_drawCharacterConversionRepeatCounter = 0; +		removeConversationChoice(num); +		clearConversationArea(); +	} +} + +void ToucheEngine::handleConversation() { +	if (_conversationNum != 0) { +		findConversationByNum(_conversationNum); +		_conversationAreaCleared = false; +		drawCharacterConversation(); +		_roomAreaRect.setHeight(320); +		_hideInventoryTexts = true; +		_conversationEnded = false; +		_conversationNum = 0; +	} else if (_hideInventoryTexts && _conversationAreaCleared) { +		if (_keyCharsTable[_currentKeyCharNum].scriptDataOffset == 0) { +			drawCharacterConversation(); +		} +	} else if (!_conversationAreaCleared && _conversationChoicesUpdated) { +		drawCharacterConversation(); +	} +} + +static int getDirection(int x1, int y1, int z1, int x2, int y2, int z2) { +	int ret = -1; +	x2 -= x1; +	y2 -= y1; +	z2 -= z1; +	if (x2 == 0 && y2 == 0 && z2 == 0) { +		ret = -2; +	} else { +		if (ABS(x2) >= ABS(z2)) { +			if (ABS(x2) > ABS(y2)) { +				if (x2 > 0) { +					ret = 0; +				} else { +					ret = 3; +				} +			} else { +				if (y2 > 0) { +					ret = 1; +				} else { +					ret = 2; +				} +			} +		} else { +			if (z2 != 0) { +				if (z2 > 0) { +					ret = 1; +				} else { +					ret = 2; +				} +			} else { +				if (y2 > 0) { +					ret = 1; +				} else { +					ret = 2; +				} +			} +		} +	} +	return ret; +} + +void ToucheEngine::buildWalkPointsList(int keyChar) { +	debugC(9, kDebugEngine, "ToucheEngine::buildWalkPointsList(%d)", keyChar); +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	KeyChar *key = &_keyCharsTable[keyChar]; +	uint16 curPos, pos1, pos2; +	if (key->pointsDataNum & 0x8000) { +		const ProgramWalkData *pwd = &_programWalkTable[(key->pointsDataNum & 0x7FFF)]; +		if (_programPointsTable[pwd->point1].priority < _programPointsTable[pwd->point2].priority) { +			curPos = pwd->point1; +		} else { +			curPos = pwd->point2; +		} +	} else { +		curPos = key->pointsDataNum; +	} + +	int16 posNum = _programPointsTable[curPos].priority; +	if (posNum == 32000) { +		return; +	} +	key->walkPointsList[0] = curPos; +	int16 walkPointsCount = 1; +	do { +		for (uint i = 0; i < _programWalkTable.size(); ++i) { +			if ((_programWalkTable[i].point1 & 0x4000) == 0) { +				pos1 = _programWalkTable[i].point1; +				pos2 = _programWalkTable[i].point2; +				if (pos1 == curPos && posNum > _programPointsTable[pos2].priority) { +					curPos = pos2; +					assert(walkPointsCount < 40); +					key->walkPointsList[walkPointsCount] = curPos; +					++walkPointsCount; +					posNum = _programPointsTable[pos2].priority; +					break; +				} +				if (pos2 == curPos && posNum > _programPointsTable[pos1].priority) { +					curPos = pos1; +					assert(walkPointsCount < 40); +					key->walkPointsList[walkPointsCount] = curPos; +					++walkPointsCount; +					posNum = _programPointsTable[pos1].priority; +					break; +				} +			} +		} +	} while (_programPointsTable[curPos].priority != 0); +	assert(walkPointsCount < 40); +	key->walkPointsList[walkPointsCount] = -1; + +	key->xPosPrev = _programPointsTable[curPos].x; +	key->yPosPrev = _programPointsTable[curPos].y; +	key->zPosPrev = _programPointsTable[curPos].z; +	key->prevWalkDataNum = findWalkDataNum(curPos, -1); +	key->walkPointsListCount = 0; +	if (key->walkDataNum == -1) { +		return; +	} + +	pos1 = _programWalkTable[key->walkDataNum].point1; +	pos2 = _programWalkTable[key->walkDataNum].point2; +	if (key->pointsDataNum == pos1) { +		if (key->walkPointsList[1] == pos2) { +			++key->walkPointsListCount; +		} +		return; +	} +	if (key->pointsDataNum == pos2) { +		if (key->walkPointsList[1] == pos1) { +			++key->walkPointsListCount; +		} +		return; +	} +} + +int ToucheEngine::findWalkDataNum(int pointNum1, int pointNum2) { +	debugC(9, kDebugEngine, "ToucheEngine::findWalkDataNum(%d, %d)", pointNum1, pointNum2); +	if (pointNum1 != pointNum2) { +		for (uint i = 0; i < _programWalkTable.size(); ++i) { +			int p1 = _programWalkTable[i].point1 & 0xFFF; +			int p2 = _programWalkTable[i].point2 & 0xFFF; +			if (p1 == pointNum1) { +				if (p2 == pointNum2 || pointNum2 == 10000) { +					return i; +				} +			} else if (p2 == pointNum1) { +				if (p1 == pointNum2 || pointNum2 == 10000) { +					return i; +				} +			} +		} +	} +	return -1; +} + +void ToucheEngine::changeWalkPath(int num1, int num2, int16 val) { +	debugC(9, kDebugEngine, "ToucheEngine::changeWalkPath(%d, %d)", num1, num2); +	int num = findWalkDataNum(num1, num2); +	if (num != -1) { +		_programWalkTable[num].area1 = val; +	} +} + +void ToucheEngine::adjustKeyCharPosToWalkBox(KeyChar *key, int moveType) { +	const ProgramWalkData *pwd = &_programWalkTable[key->walkDataNum]; + +	const ProgramPointData *pts1 = &_programPointsTable[pwd->point1]; +	int16 x1 = pts1->x; +	int16 y1 = pts1->y; +	int16 z1 = pts1->z; + +	const ProgramPointData *pts2 = &_programPointsTable[pwd->point2]; +	int16 x2 = pts2->x; +	int16 y2 = pts2->y; +	int16 z2 = pts2->z; + +	int16 kx = key->xPos; +	int16 ky = key->yPos; +	int16 kz = key->zPos; + +	int16 dx = x2 - x1; +	int16 dy = y2 - y1; +	int16 dz = z2 - z1; + +	switch (moveType) { +	case 0: +		kx -= x1; +		if (dx != 0) { +			key->yPos = dy * kx / dx + y1; +			key->zPos = dz * kx / dx + z1; +		} +		break; +	case 1: +		ky -= y1; +		if (dy != 0) { +			key->xPos = dx * ky / dy + x1; +			key->zPos = dz * ky / dy + z1; +		} +		break; +	case 2: +		kz -= z1; +		if (dz != 0) { +			key->xPos = dx * kz / dz + x1; +			key->yPos = dy * kz / dz + y1; +		} +		break; +	} +} + +void ToucheEngine::lockWalkPath(int num1, int num2) { +	debugC(9, kDebugEngine, "ToucheEngine::lockWalkPath(%d, %d)", num1, num2); +	const int num = findWalkDataNum(num1, num2); +	if (num != -1) { +		_programWalkTable[num].point1 |= 0x4000; +		_programWalkTable[num].point2 |= 0x4000; +	} +} + +void ToucheEngine::unlockWalkPath(int num1, int num2) { +	debugC(9, kDebugEngine, "ToucheEngine::unlockWalkPath(%d, %d)", num1, num2); +	const int num = findWalkDataNum(num1, num2); +	if (num != -1) { +		_programWalkTable[num].point1 &= 0xFFF; +		_programWalkTable[num].point2 &= 0xFFF; +	} +} + +void ToucheEngine::resetPointsData(int num) { +	debugC(9, kDebugEngine, "ToucheEngine::resetPointsData(%d)", num); +	for (uint i = 1; i < _programPointsTable.size(); ++i) { +		_programPointsTable[i].priority = num; +	} +} + +bool ToucheEngine::sortPointsData(int num1, int num2) { +	debugC(9, kDebugEngine, "ToucheEngine::sortPointsData(%d, %d)", num1, num2); +	resetPointsData(32000); +	if (num1 == -1) { +		if (num2 == -1) { +			return false; +		} +		_programPointsTable[num2].priority = 0; +	} else { +		const int md1 = _programWalkTable[num1].point1; +		_programPointsTable[md1].priority = 0; +		const int md2 = _programWalkTable[num1].point2; +		_programPointsTable[md2].priority = 0; +	} +	bool quit = false; +	int priority = 1; +	while (!quit) { +		quit = true; +		for (uint i = 0; i < _programWalkTable.size(); ++i) { +			const int md1 = _programWalkTable[i].point1; +			const int md2 = _programWalkTable[i].point2; +			if ((md1 & 0x4000) == 0) { +				if (_programPointsTable[md1].priority == priority - 1 && _programPointsTable[md2].priority > priority) { +					_programPointsTable[md2].priority = priority; +					quit = false; +				} +				if (_programPointsTable[md2].priority == priority - 1 && _programPointsTable[md1].priority > priority) { +					_programPointsTable[md1].priority = priority; +					quit = false; +				} +			} +		} +		++priority; +	} +	return true; +} + +void ToucheEngine::updateKeyCharWalkPath(KeyChar *key, int16 dx, int16 dy, int16 dz) { +	debugC(9, kDebugEngine, "ToucheEngine::updateKeyCharWalkPath(key=%d, dx=%d, dy=%d, dz=%d)", key - _keyCharsTable, dx, dy, dz); +	if (key->walkDataNum == -1) { +		return; +	} +	int16 kx = key->xPos; +	int16 ky = key->yPos; +	int16 kz = key->zPos; +	if (kz != 160) { +		if (dx != 0) { +			dx = dx * kz / 160; +			if (dx == 0) { +				dx = 1; +			} +		} +		if (dy != 0) { +			dy = dy * kz / 160; +			if (dy == 0) { +				dy = 1; +			} +		} +		if (dz != 0) { +			dz = dz * kz / 160; +			if (dz == 0) { +				dz = 1; +			} +		} +	} + +	int16 curDirection = key->facingDirection; +	if (key->currentAnim > 1) { +		if (dx != 0 || dy != 0 || dz != 0) { +			if (curDirection == 3) { +				key->xPos -= dx; +			} else { +				key->xPos += dx; +			} +			key->xPosPrev = key->xPos; +		} +		return; +	} + +	int16 xpos, ypos, zpos, walkPoint1, walkPoint2, newDirection, incDx, incDy, incDz; +	while (1) { +		walkPoint1 = key->walkPointsList[key->walkPointsListCount]; +		walkPoint2 = key->walkPointsList[key->walkPointsListCount + 1]; +		key->currentWalkBox = walkPoint1; +		if (walkPoint1 == -1) { +			xpos = key->xPosPrev; +			ypos = key->yPosPrev; +			zpos = key->zPosPrev; +			if (key->prevWalkDataNum != -1) { +				key->walkDataNum = key->prevWalkDataNum; +				key->prevWalkDataNum = -1; +			} +		} else { +			xpos = _programPointsTable[walkPoint1].x; +			ypos = _programPointsTable[walkPoint1].y; +			zpos = _programPointsTable[walkPoint1].z; +		} +		newDirection = getDirection(kx, ky, kz, xpos, ypos, zpos); +		if (newDirection < 0) { +			newDirection = curDirection; +		} +		if (newDirection != curDirection) { +			key->currentAnimCounter = 0; +			key->facingDirection = newDirection; +			return; +		} +		incDx = xpos - kx; +		incDy = ypos - ky; +		incDz = zpos - kz; +		if (incDz != 0 || incDy != 0 || incDx != 0) { +			break; +		} +		if (walkPoint1 == -1) { +			if (key->currentAnim == 1) { +				setKeyCharRandomFrame(key); +			} +			return; +		} +		key->prevPointsDataNum = key->pointsDataNum; +		key->pointsDataNum = walkPoint1; +		if (walkPoint2 == -1) { +			key->walkPointsList[0] = -1; +			key->walkPointsListCount = 0; +		} else { +			++key->walkPointsListCount; +			int16 walkDataNum = findWalkDataNum(walkPoint1, walkPoint2); +			if (walkDataNum != -1) { +				key->walkDataNum = walkDataNum; +			} +		} +	} + +	if (key->currentAnim < 1) { +		key->currentAnimCounter = 0; +		key->currentAnim = 1; +		if (dx == 0 && dy == 0 && dz == 0) { +			return; +		} +	} + +	switch (newDirection) { +	case 0: +	case 3: +		if (dx == 0) { +			return; +		} +		if (newDirection == 3) { +			dx = -dx; +		} +		if (ABS(dx) >= ABS(incDx)) { +			if (walkPoint1 != -1) { +				if (walkPoint2 == -1) { +					newDirection = getDirection(xpos, ypos, zpos, key->xPosPrev, key->yPosPrev, key->zPosPrev); +					if (key->prevWalkDataNum != -1) { +						key->walkDataNum = key->prevWalkDataNum; +						key->prevWalkDataNum = -1; +					} +				} else { +					newDirection = getDirection(xpos, ypos, zpos, _programPointsTable[walkPoint2].x, _programPointsTable[walkPoint2].y, _programPointsTable[walkPoint2].z); +					int16 walkDataNum = findWalkDataNum(walkPoint1, walkPoint2); +					if (walkDataNum != -1) { +						key->walkDataNum = walkDataNum; +					} +				} +				if (newDirection == -2) { +					key->xPos = xpos; +					key->yPos = ypos; +					key->zPos = zpos; +					setKeyCharRandomFrame(key); +					return; +				} +				if (newDirection < 0) { +					newDirection = curDirection; +				} +				key->prevPointsDataNum = key->pointsDataNum; +				key->pointsDataNum = walkPoint1; +				++key->walkPointsListCount; +				if (newDirection != curDirection) { +					key->facingDirection = newDirection; +					key->currentAnimCounter = 0; +					key->xPos = xpos; +					key->yPos = ypos; +					key->zPos = zpos; +					return; +				} +			} else { +				key->xPos = xpos; +				key->yPos = ypos; +				key->zPos = zpos; +				return; +			} +		} +		key->xPos += dx; +		adjustKeyCharPosToWalkBox(key, 0); +		break; +	case 1: +	case 2: +		if (ABS(dz) >= ABS(incDz) && incDz != 0) { +			if (walkPoint1 != -1) { +				if (walkPoint2 == -1) { +					newDirection = getDirection(xpos, ypos, zpos, key->xPosPrev, key->yPosPrev, key->zPosPrev); +				} else { +					newDirection = getDirection(xpos, ypos, zpos, _programPointsTable[walkPoint2].x, _programPointsTable[walkPoint2].y, _programPointsTable[walkPoint2].z); +					int16 walkDataNum = findWalkDataNum(walkPoint1, walkPoint2); +					if (walkDataNum != -1) { +						key->walkDataNum = walkDataNum; +					} +				} +				if (newDirection == -2) { +					key->xPos = xpos; +					key->yPos = ypos; +					key->zPos = zpos; +					setKeyCharRandomFrame(key); +					return; +				} +				if (newDirection < 0) { +					newDirection = curDirection; +				} +				key->prevPointsDataNum = key->pointsDataNum; +				key->pointsDataNum = walkPoint1; +				++key->walkPointsListCount; +				if (newDirection != curDirection) { +					key->facingDirection = newDirection; +					key->currentAnimCounter = 0; +					key->xPos = xpos; +					key->yPos = ypos; +					key->zPos = zpos; +					return; +				} +			} else { +				key->xPos = xpos; +				key->yPos = ypos; +				key->zPos = zpos; +				return; +			} +		} +		if (incDz != 0) { +			key->zPos += dz; +			adjustKeyCharPosToWalkBox(key, 2); +		} else { +			if (ABS(dz) < ABS(incDy)) { +				key->yPos += dz; +				adjustKeyCharPosToWalkBox(key, 1); +			} else { +				key->xPos = xpos; +				key->yPos = ypos; +				key->zPos = zpos; +			} +		} +		break; +	} +} + +void ToucheEngine::markWalkPoints(int keyChar) { +	assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +	KeyChar *key = &_keyCharsTable[keyChar]; +	int16 pointsDataNum = key->pointsDataNum; +	resetPointsData(0); +	if (pointsDataNum != -1) { +		_programPointsTable[pointsDataNum].priority = 1; +		bool quit = false; +		while (!quit) { +			quit = true; +			for (uint i = 0; i < _programWalkTable.size(); ++i) { +				int16 md1 = _programWalkTable[i].point1; +				int16 md2 = _programWalkTable[i].point2; +				if ((md1 & 0x4000) == 0) { +					if (_programPointsTable[md1].priority != 0 && _programPointsTable[md2].priority == 0) { +						_programPointsTable[md2].priority = 1; +						quit = false; +					} +					if (_programPointsTable[md2].priority != 0 && _programPointsTable[md1].priority == 0) { +						_programPointsTable[md1].priority = 1; +						quit = false; +					} +				} +			} +		} +	} +} + +void ToucheEngine::buildWalkPath(int dstPosX, int dstPosY, int keyChar) { +	debugC(9, kDebugEngine, "ToucheEngine::buildWalkPath(x=%d, y=%d, key=%d)", dstPosX, dstPosY, keyChar); +	if (_currentEpisodeNum == 130) { +		return; +	} +	markWalkPoints(keyChar); + +	int minDistance = 0x7D000000; +	int minPointsDataNum = -1; +	for (uint i = 1; i < _programPointsTable.size(); ++i) { +		if (_programPointsTable[i].priority != 0) { +			int dx = ABS<int>(_programPointsTable[i].x - dstPosX); +			int dy = ABS<int>(_programPointsTable[i].y - dstPosY); +			int distance = dx * dx + dy * dy; +			if (distance < minDistance) { +				minDistance = distance; +				minPointsDataNum = i; +			} +		} +	} + +	minDistance = 32000; +	int minWalkDataNum = -1; +	for (uint i = 0; i < _programWalkTable.size(); ++i) { +		const ProgramWalkData *pwd = &_programWalkTable[i]; +		if ((pwd->point1 & 0x4000) == 0) { +			int distance = 32000; +			ProgramPointData *pts1 = &_programPointsTable[pwd->point1]; +			ProgramPointData *pts2 = &_programPointsTable[pwd->point2]; +			if (pts1->priority != 0) { +				int dx = pts2->x - pts1->x; +				int dy = pts2->y - pts1->y; +				if (dx == 0) { +					if (dstPosY > MIN(pts2->y, pts1->y) && dstPosY < MAX(pts2->y, pts1->y)) { +						distance = ABS(dstPosX - pts1->x); +						if (distance <= 100) { +							distance *= distance; +						} +					} +				} else if (dy == 0) { +					if (dstPosX > MIN(pts2->x, pts1->x) && dstPosX < MAX(pts2->x, pts1->x)) { +						distance = ABS(dstPosY - pts1->y); +						if (distance <= 100) { +							distance *= distance; +						} +					} +				} else { +					if (dstPosY > MIN(pts2->y, pts1->y) && dstPosY < MAX(pts2->y, pts1->y) && +						dstPosX > MIN(pts2->x, pts1->x) && dstPosX < MAX(pts2->x, pts1->x) ) { +						distance = (dstPosY - pts1->y) * dx - (dstPosX - pts1->x) * dy; +						distance = (dx * dx + dy * dy) / distance; +					} +				} +				if (distance < minDistance) { +					minDistance = distance; +					minWalkDataNum = i; +				} +			} +		} +	} +	if (!sortPointsData(minWalkDataNum, minPointsDataNum)) { +		return; +	} +	int dstPosZ; +	buildWalkPointsList(keyChar); +	KeyChar *key = &_keyCharsTable[keyChar]; +	if (minWalkDataNum == -1) { +		dstPosX = _programPointsTable[minPointsDataNum].x; +		dstPosY = _programPointsTable[minPointsDataNum].y; +		dstPosZ = _programPointsTable[minPointsDataNum].z; +	} else { +		ProgramWalkData *pwd = &_programWalkTable[minWalkDataNum]; +		ProgramPointData *pts1 = &_programPointsTable[pwd->point1]; +		ProgramPointData *pts2 = &_programPointsTable[pwd->point2]; +		int16 dx = pts2->x - pts1->x; +		int16 dy = pts2->y - pts1->y; +		int16 dz = pts2->z - pts1->z; +		if (ABS(dy) > ABS(dx)) { +			dstPosZ = pts2->z - (pts2->y - dstPosY) * dz / dy; +			dstPosX = pts2->x - (pts2->y - dstPosY) * dx / dy; +		} else { +			dstPosZ = pts2->z - (pts2->x - dstPosX) * dz / dx; +			dstPosY = pts2->y - (pts2->x - dstPosX) * dy / dx; +		} +		if (key->walkDataNum == key->prevWalkDataNum && key->walkPointsList[1] == -1) { +			if (key->walkPointsList[0] == _programWalkTable[minWalkDataNum].point1 || key->walkPointsList[0] == _programWalkTable[minWalkDataNum].point2) { +				++key->walkPointsListCount; +			} +		} +	} +	key->prevWalkDataNum = minWalkDataNum; +	key->xPosPrev = dstPosX; +	key->yPosPrev = dstPosY; +	key->zPosPrev = dstPosZ; +	if (_flagsTable[902] != 0) { +		Graphics::fillRect(_backdropBuffer, _currentBitmapWidth, dstPosX, dstPosY, 4, 4, 0xFC); +	} +} + +void ToucheEngine::addToAnimationTable(int num, int posNum, int keyChar, int delayCounter) { +	for (int i = 0; i < NUM_ANIMATION_ENTRIES; ++i) { +		AnimationEntry *anim = &_animationTable[i]; +		if (anim->num == 0) { +			anim->num = num; +			anim->delayCounter = delayCounter; +			anim->posNum = posNum; +			int16 xPos, yPos, x2Pos, y2Pos; +			if (posNum >= 0) { +				assert(posNum >= 0 && posNum < NUM_KEYCHARS); +				xPos = _keyCharsTable[posNum].xPos; +				yPos = _keyCharsTable[posNum].yPos - 50; +			} else { +				posNum = -posNum; +				xPos = _programPointsTable[posNum].x; +				yPos = _programPointsTable[posNum].y; +			} +			xPos -= _flagsTable[614]; +			yPos -= _flagsTable[615]; +			assert(keyChar >= 0 && keyChar < NUM_KEYCHARS); +			x2Pos = _keyCharsTable[keyChar].xPos - _flagsTable[614]; +			y2Pos = _keyCharsTable[keyChar].yPos - _flagsTable[615] - 50; +			xPos -= x2Pos; +			yPos -= y2Pos; +			xPos /= 8; +			yPos /= 8; +			anim->x = x2Pos; +			anim->y = y2Pos; +			anim->dx = xPos; +			anim->dy = yPos; +			anim->displayCounter = 8; +			anim->displayRect.left = -1; +		} +	} +} + +void ToucheEngine::copyAnimationImage(int dstX, int dstY, int w, int h, const uint8 *src, int srcX, int srcY, int fillColor) { +	Area copyRegion(dstX, dstY, w, h); +	copyRegion.srcX = srcX; +	copyRegion.srcY = srcY; +	if (copyRegion.clip(_screenRect)) { +		if (fillColor != -1) { +			Graphics::copyMask(_offscreenBuffer, 640, copyRegion.r.left, copyRegion.r.top, +			  src, 58, copyRegion.srcX, copyRegion.srcY, +			  copyRegion.r.width(), copyRegion.r.height(), +			  (uint8)fillColor); +		} else { +			Graphics::copyRect(_offscreenBuffer, 640, copyRegion.r.left, copyRegion.r.top, +			  src, 58, copyRegion.srcX, copyRegion.srcY, +			  copyRegion.r.width(), copyRegion.r.height(), +			  Graphics::kTransparent); +		} +	} +} + +void ToucheEngine::drawAnimationImage(AnimationEntry *anim) { +	if (anim->displayRect.left != -1) { +		addToDirtyRect(anim->displayRect); +	} +	int x = anim->x; +	int y = anim->y; +	int dx = -anim->dx; +	int dy = -anim->dy; + +	int displayRectX1 = 30000; +	int displayRectY1 = 30000; +	int displayRectX2 = -30000; +	int displayRectY2 = -30000; + +	dx /= 3; +	dy /= 3; + +	res_loadImage(anim->num, _iconData); +	int color = 0xCF; + +	x += dx * 5 - 29; +	y += dy * 5 - 21; +	dx = -dx; +	dy = -dy; +	for (int i = 0; i < 6; ++i) { +		if (i == 5) { +			color = -1; +		} +		copyAnimationImage(x, y, 58, 42, _iconData, 0, 0, color); +		--color; +		displayRectX1 = MIN(x, displayRectX1); +		displayRectX2 = MAX(x, displayRectX2); +		displayRectY1 = MIN(y, displayRectY1); +		displayRectY2 = MAX(y, displayRectY2); +		x += dx; +		y += dy; +	} +	anim->displayRect = Common::Rect(displayRectX1, displayRectY1, displayRectX2 + 58, displayRectY2 + 42); +//	if (rectClip(&_roomAreaRect, &anim->displayRect)) { +//		addToDirtyRect(&anim->displayRect); +//	} +} + +void ToucheEngine::processAnimationTable() { +	for (int i = 0; i < NUM_ANIMATION_ENTRIES; ++i) { +		AnimationEntry *anim = &_animationTable[i]; +		if (anim->num != 0) { +			if (anim->displayCounter == 0) { +				anim->num = 0; +				if (anim->displayRect.left != -1) { +					addToDirtyRect(anim->displayRect); +				} +			} else { +				if (anim->delayCounter != 0) { +					--anim->delayCounter; +				} else { +					anim->x += anim->dx; +					anim->y += anim->dy; +					drawAnimationImage(anim); +					--anim->displayCounter; +				} +			} +		} +	} +} + +void ToucheEngine::clearAnimationTable() { +	memset(_animationTable, 0, sizeof(_animationTable)); +} + +void ToucheEngine::addToDirtyRect(const Common::Rect &r) { +	// XXX +} + +void ToucheEngine::clearDirtyRects() { +	// XXX +} + +void ToucheEngine::setPalette(int firstColor, int colorCount, int rScale, int gScale, int bScale) { +	uint8 pal[256 * 4]; +	for (int i = firstColor; i < firstColor + colorCount; ++i) { +		int r = _paletteBuffer[i * 4 + 0]; +		r = (r * rScale) >> 8; +		pal[i * 4 + 0] = (uint8)r; + +		int g = _paletteBuffer[i * 4 + 1]; +		g = (g * gScale) >> 8; +		pal[i * 4 + 1] = (uint8)g; + +		int b = _paletteBuffer[i * 4 + 2]; +		b = (b * bScale) >> 8; +		pal[i * 4 + 2] = (uint8)b; + +		pal[i * 4 + 3] = 0; +	} +	_system->setPalette(&pal[firstColor * 4], firstColor, colorCount); +} + +void ToucheEngine::copyPaletteColor(int srcColorIndex, int dstColorIndex) { +	memcpy(&_paletteBuffer[dstColorIndex * 4], &_paletteBuffer[srcColorIndex * 4], 4); +} + +void ToucheEngine::updateScreenArea(const uint8 *src, int srcPitch, int srcX, int srcY, int dstX, int dstY, int w, int h) { +	_system->copyRectToScreen(src + srcY * srcPitch + srcX, srcPitch, dstX, dstY, w, h); +	_system->updateScreen(); +} + +void ToucheEngine::updateEntireScreen() { +	int h = (_flagsTable[606] != 0) ? 400 : 352; +	_system->copyRectToScreen(_offscreenBuffer, 640, 0, 0, 640, h); +	_system->updateScreen(); +} + +void ToucheEngine::updateDirtyScreenAreas() { +	// XXX +	updateScreenArea(_offscreenBuffer, 640, 0, 0, 0, 0, 640, 400); +	if (_fullRedrawCounter) { +//		updateEntireScreen(); +		--_fullRedrawCounter; +	} else { +//		for (int i = 0; i < _dirtyRectsCount; ++i) { +//			Common::Rect *r = &_dirtyRects[i]; +//			updateScreenArea(_offscreenBuffer, 640, r->x, r->y, r->x, r->y, r->w, r->h); +//		} +		if (_redrawScreenCounter1) { +			--_redrawScreenCounter1; +//			updateScreenArea(_offscreenBuffer, 640, _cursorObjectRect.x, _cursorObjectRect.y, _cursorObjectRect.x, _cursorObjectRect.y, _cursorObjectRect.w, _cursorObjectRect.h); +		} +	} +} + +void ToucheEngine::updatePalette() { +	_system->setPalette(_paletteBuffer, 0, 256); +} + +} // namespace Touche diff --git a/engines/touche/touche.h b/engines/touche/touche.h new file mode 100644 index 0000000000..d7dd188ac7 --- /dev/null +++ b/engines/touche/touche.h @@ -0,0 +1,778 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 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$ + * $Id: $ + * + */ + +#ifndef TOUCHE_ENGINE_H +#define TOUCHE_ENGINE_H + +#include "common/array.h" +#include "common/endian.h" +#include "common/file.h" +#include "common/rect.h" +#include "common/util.h" + +#include "sound/mixer.h" + +#include "engines/engine.h" + +namespace Touche { + +struct Area { +	Common::Rect r; +	int16 srcX, srcY; + +	Area() { +		srcX = srcY = 0; +	} + +	Area(int16 x, int16 y, int16 w, int16 h) { +		r = Common::Rect(x, y, x + w, y + h); +		srcX = srcY = 0; +	} + +	bool clip(const Common::Rect &rect) { +		const int dx = r.left - rect.left; +		if (dx < 0) { +			srcX -= dx; +		} +		const int dy = r.top - rect.top; +		if (dy < 0) { +			srcY -= dy; +		} +		if (rect.left > r.left) { +			r.left = rect.left; +		} +		if (rect.top > r.top) { +			r.top = rect.top; +		} +		if (rect.right < r.right) { +			r.right = rect.right; +		} +		if (rect.bottom < r.bottom) { +			r.bottom = rect.bottom; +		} +		return (r.right > r.left && r.bottom > r.top); +	} +}; + +struct KeyChar { +	uint16 num; +	uint16 flags; +	int16 currentAnimCounter; +	int16 strNum; +	int16 walkDataNum; +	int16 spriteNum; +	Common::Rect prevBoundingRect; +	Common::Rect boundingRect; +	int16 xPos; +	int16 yPos; +	int16 zPos; +	int16 xPosPrev; +	int16 yPosPrev; +	int16 zPosPrev; +	int16 prevWalkDataNum; +	uint16 textColor; +	int16 inventoryItems[4]; +	int16 money; +	int16 pointsDataNum; +	int16 currentWalkBox; +	uint16 prevPointsDataNum; +	int16 currentAnim; +	int16 facingDirection; +	int16 currentAnimSpeed; +	int16 framesList[16]; +	int16 framesListCount; +	int16 currentFrame; +	int16 anim1Start; +	int16 anim1Count; +	int16 anim2Start; +	int16 anim2Count; +	int16 anim3Start; +	int16 anim3Count; +	int16 followingKeyCharNum; +	int16 followingKeyCharPos; +	uint16 sequenceDataIndex; +	uint16 sequenceDataOffset; +	int16 walkPointsListCount; +	int16 walkPointsList[40]; +	uint16 scriptDataStartOffset; +	uint16 scriptDataOffset; +	int16 *scriptStackPtr; +	int16 delay; +	int16 waitingKeyChar; +	int16 waitingKeyCharPosTable[3]; +	int16 scriptStackTable[40]; +}; + +struct Script { +	uint8 opcodeNum; +	uint32 dataOffset; +	int16 keyCharNum; +	uint8 *dataPtr; +	int16 *stackDataPtr; +	int16 *stackDataBasePtr; +	int16 quitFlag; +	int16 stackDataTable[500]; + +	void init(uint8 *data) { +		dataPtr = data; +		stackDataPtr = stackDataBasePtr = &stackDataTable[499]; +		dataOffset = 0; +		quitFlag = 0; +	} + +	uint8 readByte(uint32 offs) const { +		return *(dataPtr + offs); +	} + +	int16 readWord(uint32 offs) const { +		return READ_LE_UINT16(dataPtr + offs); +	} + +	uint8 readNextByte() { +		uint8 val = readByte(dataOffset); +		++dataOffset; +		return val; +	} + +	int16 readNextWord() { +		int16 val = readWord(dataOffset); +		dataOffset += 2; +		return val; +	} +}; + +struct TalkEntry { +	int16 otherKeyChar; +	int16 talkingKeyChar; +	int16 num; +}; + +struct ConversationChoice { +	int16 num; +	int16 msg; +}; + +struct AnimationEntry { +	int16 num; +	int16 x; +	int16 y; +	int16 dx; +	int16 dy; +	int16 posNum; +	int16 delayCounter; +	int16 displayCounter; +	Common::Rect displayRect; +}; + +struct SequenceEntry { +	int16 sprNum; +	int16 seqNum; +}; + +struct SpriteData { +	uint32 size; +	uint8 *ptr; +	uint16 bitmapWidth; +	uint16 bitmapHeight; +	uint16 w; +	uint16 h; +}; + +struct MidiContext { +	uint8 unk2; +	uint8 unkA; +	uint8 unkB; +	uint16 volume; +	uint8 unkF; +	uint16 currentVolume; +}; + +struct ProgramPointData { +	int16 x, y, z; +	int16 priority; +}; + +struct ProgramWalkData { +	int16 point1; +	int16 point2; +	int16 clippingRect; +	int16 area1; +	int16 area2; +}; + +struct ProgramAreaData { +	Area area; +	int16 id; +	int16 state; +	int16 animCount; +	int16 animNext; +}; + +struct ProgramBackgroundData { +	Area area; +	int16 type; +	int16 offset; +	int16 scaleMul; +	int16 scaleDiv; +}; + +struct ProgramHitBoxData { +	int16 item; +	int16 talk; +	uint16 state; +	int16 str; +	int16 defaultStr; +	int16 actions[8]; +	Common::Rect hitBoxes[2]; +}; + +struct ProgramActionScriptOffsetData { +	int16 object1; +	int16 action; +	int16 object2; +	uint16 offset; +}; + +struct ProgramKeyCharScriptOffsetData { +	int16 keyChar; +	uint16 offset; +}; + +struct ProgramConversationData { +	int16 num; +	uint16 offset; +	int16 msg; +}; + +enum { +	kDebugEngine   = 1 << 0, +	kDebugGraphics = 1 << 1, +	kDebugResource = 1 << 2, +	kDebugOpcodes  = 1 << 3, +	kDebugUserIntf = 1 << 4 +}; + +enum ResourceType { +	kResourceTypeRoomImage = 0, +	kResourceTypeSequence, +	kResourceTypeSpriteImage, +	kResourceTypeIconImage, +	kResourceTypeRoomInfo, +	kResourceTypeProgram, +	kResourceTypeMusic, +	kResourceTypeSound +}; + +enum TalkMode { +	kTalkModeTextOnly = 0, +	kTalkModeVoiceOnly, +	kTalkModeVoiceAndText, +	kTalkModeCount +}; + +enum ScriptFlag { +	kScriptStopped = 1 << 0, +	kScriptPaused  = 1 << 1 +}; + +class ToucheEngine: public Engine { +public: + +	enum { +		NUM_OPCODES = 135, +		NUM_FLAGS = 2000, +		NUM_KEYCHARS = 32, +		NUM_AREAS = 10, +		NUM_SPRITES = 5, +		NUM_SEQUENCES = 7, +		NUM_CONVERSATION_CHOICES = 40, +		NUM_TALK_ENTRIES = 16, +		NUM_ANIMATION_ENTRIES = 4, +		NUM_INVENTORY_ITEMS = 100, +		NUM_DIRTY_RECTS = 50, +		NUM_GAMESTATE_FILES = 100 +	}; + +	typedef void (ToucheEngine::*OpcodeProc)(); + +	ToucheEngine(OSystem *system, Common::Language language); +	virtual ~ToucheEngine(); + +	virtual int init(); +	virtual int go(); + +protected: + +	void restart(); +	void mainLoop(); +	void processEvents(); +	void runCycle(); +	int16 getRandomNumber(int max); +	void changePaletteRange(); +	void playSoundInRange(); +	void resetSortedKeyCharsTable(); +	void setupEpisode(int num); +	void setupNewEpisode(); +	void drawKeyChar(KeyChar *key); +	void sortKeyChars(); +	void runKeyCharScript(KeyChar *key); +	void runCurrentKeyCharScript(int mode); +	void executeScriptOpcode(int16 param); +	void initKeyChars(int keyChar); +	void setKeyCharTextColor(int keyChar, uint16 color); +	void waitForKeyCharPosition(int keyChar); +	void setKeyCharBox(int keyChar, int value); +	void setKeyCharFrame(int keyChar, int16 type, int16 value1, int16 value2); +	void setKeyCharFacingDirection(int keyChar, int16 dir); +	void initKeyCharScript(int keyChar, int16 spriteNum, int16 seqDataIndex, int16 seqDataOffs); +	uint16 findProgramKeyCharScriptOffset(int keyChar) const; +	bool scrollRoom(int keyChar); +	void drawIcon(int x, int y, int num); +	void centerScreenToKeyChar(int keyChar); +	void waitForKeyCharsSet(); +	void redrawRoom(); +	void fadePalette(int firstColor, int lastColor, int scale, int scaleInc, int fadingStepsCount); +	void fadePaletteFromFlags(); +	void moveKeyChar(uint8 *dst, int dstPitch, KeyChar *key); +	void changeKeyCharFrame(KeyChar *key, int keyChar); +	void setKeyCharRandomFrame(KeyChar *key); +	void setKeyCharMoney(); +	const char *getString(int num) const; +	int getStringWidth(int m, int num) const; +	void drawString(uint8 *dst, int dstPitch, int m, uint16 color, int x, int y, int16 num); +	void drawGameString(int m, uint16 color, int x1, int y, const char *str); +	int restartKeyCharScriptOnAction(int action, int obj1, int obj2); +	void buildSpriteScalingTable(int z1, int z2); +	void drawSpriteOnBackdrop(int num, int x, int y); +	void updateTalkFrames(int keyChar); +	void setKeyCharTalkingFrame(int keyChar); +	void lockUnlockHitBox(int num, int lock); +	void drawHitBoxes(); +	void setCursor(int num); +	void updateCursor(int num); +	void handleMouseButtonClicked(); +	void handleMouseButtonPressed(); +	void handleMouseInput(int flag); +	void handleMouseInputRoomArea(int flag); +	void handleMouseInputInventoryArea(int flag); +	void scrollScreenToPos(int num); +	void clearRoomArea(); +	void startNewMusic(); +	void startNewSound(); +	void updateSpeech(); +	int handleActionMenuUnderCursor(const int16 *actions, int offs, int y, int str); + +	void redrawBackground(); +	void processAreaTable(); +	void clearAreaTable(); +	void addToAreaTable(const Area *area); +	void addRoomArea(int num, int flag); +	void updateRoomAreas(int num, int flags); +	void setRoomAreaState(int num, uint16 state); +	void findAndRedrawRoomRegion(int num); +	void updateRoomRegions(); +	void redrawRoomRegion(int num, bool markForRedraw); + +	void initInventoryObjectsTable(); +	void initInventoryLists(); +	void drawInventory(int index, int flag); +	void drawAmountOfMoneyInInventory(); +	void packInventoryItems(int index); +	void appendItemToInventoryList(int index); +	void addItemToInventory(int inventory, int16 item); +	void removeItemFromInventory(int inventory, int16 item); +	void changeInventoryItemState(int flag, int itemNum, int itemRnd, int inventoryItem); + +	void resetTalkingVars(); +	int updateKeyCharTalk(int pauseFlag); +	const char *formatTalkText(int mode, int *y, int *h, const char *text); +	void addToTalkTable(int talkingKeyChar, int num, int otherKeyChar); +	void removeFromTalkTable(int keyChar); +	void addConversationChoice(int16 num); +	void removeConversationChoice(int16 num); +	void runConversationScript(uint16 offset); +	void findConversationByNum(int16 num); +	void clearConversationChoices(); +	void drawCharacterConversationRepeat2(); +	void drawCharacterConversationRepeat(); +	void drawCharacterConversation(); +	void drawConversationString(int num, uint16 color); +	void clearConversationArea(); +	void setupConversationScript(int num); +	void handleConversation(); + +	void buildWalkPointsList(int keyChar); +	int findWalkDataNum(int pointNum1, int pointNum2); +	void changeWalkPath(int num1, int num2, int16 val); +	void adjustKeyCharPosToWalkBox(KeyChar *key, int moveType); +	void lockWalkPath(int num1, int num2); +	void unlockWalkPath(int num1, int num2); +	void resetPointsData(int num); +	bool sortPointsData(int num1, int num2); +	void updateKeyCharWalkPath(KeyChar *key, int16 dx, int16 dy, int16 dz); +	void markWalkPoints(int keyChar); +	void buildWalkPath(int dstPosX, int dstPosY, int keyChar); + +	void addToAnimationTable(int num, int posNum, int keyChar, int delayCounter); +	void copyAnimationImage(int dstX, int dstY, int w, int h, const uint8 *src, int srcX, int srcY, int fillColor); +	void drawAnimationImage(AnimationEntry *anim); +	void processAnimationTable(); +	void clearAnimationTable(); + +	void addToDirtyRect(const Common::Rect &r); +	void clearDirtyRects(); +	void setPalette(int firstColor, int colorCount, int redScale, int greenScale, int blueScale); +	void copyPaletteColor(int srcColorIndex, int dstColorIndex); +	void updateScreenArea(const uint8 *src, int srcPitch, int srcX, int srcY, int dstX, int dstY, int w, int h); +	void updateEntireScreen(); +	void updateDirtyScreenAreas(); +	void updatePalette(); + +	void saveGameStateData(Common::WriteStream *stream); +	void loadGameStateData(Common::ReadStream *stream); +	bool saveGameState(int num, const char *description); +	bool loadGameState(int num, const char *description); +	void readGameStateDescription(int num, char *description, int len); +	void generateGameStateFileName(int num, char *dst, int len, bool prefixOnly = false) const; + +	void op_nop(); +	void op_jnz(); +	void op_jz(); +	void op_jmp(); +	void op_true(); +	void op_false(); +	void op_push(); +	void op_testFalse(); +	void op_add(); +	void op_sub(); +	void op_mul(); +	void op_div(); +	void op_mod(); +	void op_and(); +	void op_or(); +	void op_not(); +	void op_testGreater(); +	void op_testEquals(); +	void op_testLower(); +	void op_fetchScriptWord(); +	void op_testGreaterOrEquals(); +	void op_testLowerOrEquals(); +	void op_testNotEquals(); +	void op_endConversation(); +	void op_stopScript(); +	void op_getFlag(); +	void op_setFlag(); +	void op_fetchScriptByte(); +	void op_getScriptValue(); +	void op_setScriptValue(); +	void op_getKeyCharWalkBox(); +	void op_startSound(); +	void op_initKeyCharTalk(); +	void op_loadRoom(); +	void op_updateRoom(); +	void op_startTalk(); +	void op_loadSprite(); +	void op_loadSequence(); +	void op_setKeyCharBox(); +	void op_initKeyCharScript(); +	void op_setKeyCharFrame(); +	void op_setKeyCharDirection(); +	void op_clearConversationChoices(); +	void op_addConversationChoice(); +	void op_removeConversationChoice(); +	void op_getInventoryItem(); +	void op_setInventoryItem(); +	void op_startEpisode(); +	void op_setConversationNum(); +	void op_enableInventoryItem(); +	void op_enableInput(); +	void op_disableInput(); +	void op_faceKeyChar(); +	void op_getKeyCharCurrentAnim(); +	void op_getCurrentKeyChar(); +	void op_isKeyCharActive(); +	void op_setPalette(); +	void op_changeWalkPath(); +	void op_lockWalkPath(); +	void op_initializeKeyChar(); +	void op_setupWaitingKeyChars(); +	void op_updateRoomAreas(); +	void op_unlockWalkPath(); +	void op_addItemToInventoryAndRedraw(); +	void op_giveItemTo(); +	void op_resetHitBoxes(); +	void op_fadePalette(); +	void op_disableInventoryItem(); +	void op_getInventoryItemFlags(); +	void op_drawInventory(); +	void op_stopKeyCharScript(); +	void op_restartKeyCharScript(); +	void op_getKeyCharCurrentWalkBox(); +	void op_getKeyCharPointsDataNum(); +	void op_setupFollowingKeyChar(); +	void op_startAnimation(); +	void op_setKeyCharTextColor(); +	void op_startMusic(); +	void op_copyPaletteColor(); +	void op_delay(); +	void op_lockHitBox(); +	void op_removeItemFromInventory(); +	void op_unlockHitBox(); +	void op_addRoomArea(); +	void op_setKeyCharFlags(); +	void op_unsetKeyCharFlags(); +	void op_loadVoice(); +	void op_drawSpriteOnBackdrop(); +	void op_startPaletteFadeIn(); +	void op_startPaletteFadeOut(); +	void op_setRoomAreaState(); + +	void res_openDataFile(); +	void res_closeDataFile(); +	void res_allocateTables(); +	void res_deallocateTables(); +	uint32 res_getDataOffset(ResourceType type, int num, uint32 *size = NULL); +	void res_loadSpriteImage(int num, uint8 *dst); +	void res_loadProgram(int num); +	void res_decodeProgramData(); +	void res_loadRoom(int num); +	void res_loadSprite(int num, int index); +	void res_loadSequence(int num, int index); +	void res_decodeScanLineImageRLE(uint8 *dst, int lineWidth); +	void res_loadBackdrop(); +	void res_loadImage(int num, uint8 *dst); +	void res_loadImageHelper(uint8 *imgData, int imgWidth, int imgHeight); +	void res_loadSound(int flag, int num); +	void res_loadMusic(int num); +	void res_loadSpeech(int num); +	void res_loadSpeechSegment(int num); +	void res_stopSpeech(); + +	bool ui_processEvents(); +	void ui_drawButtonBorders(const Common::Rect *r, int count); +	void ui_drawMusicVolumeBar(); +	void ui_drawTalkMode(); +	void ui_drawAllBorders(); +	void ui_drawSaveGamesList(int page); +	void ui_drawSaveLoadMenu(int page, int saveOrLoad); +	int ui_getButtonPressed(const Common::Rect *r, int count) const; +	void ui_drawButtonText(const int16 *texts, const Common::Rect *r, int count, bool centerTexts); +	void ui_drawArrow(int x, int y, int dx, uint8 color); +	void ui_drawOptionsMenu(); +	void ui_drawCurrentGameStateDescription(); +	int ui_handleSaveLoad(int saveOrLoad); +	void ui_handleOptions(int forceDisplay); +	void ui_drawActionsPanel(int dstX, int dstY, int deltaX, int deltaY); +	void ui_drawConversationPanelBorder(int dstY, int srcX, int srcY); +	void ui_drawConversationPanel(); +	void ui_printStatusString(const char *str); +	void ui_clearStatusString(); +	int ui_displayQuitDialog(); +	void ui_displayTextMode(int str); + + +	Common::Language _language; +	Common::RandomSource _rnd; + +	Common::Point _inp_mousePos; +	bool _inp_mouseButtonClicked; +	bool _inp_mouseButtonPressed; +	int _disabledInputCounter; +	bool _hideInventoryTexts; + +	bool _displayQuitDialog; +	int _saveLoadCurrentPage; +	int _saveLoadCurrentSlot; +	bool _saveLoadMarks[NUM_GAMESTATE_FILES]; +	char _saveLoadCurrentDescription[33]; +	int _saveLoadCurrentDescriptionLen; + +	int _defaultSoundPriority; +	int _newMusicNum; +	int _currentMusicNum; +	int _newSoundNum; +	int _newSoundDelay; +	int _newSoundPriority; +	int _playSoundCounter; +	Audio::SoundHandle _sfxHandle; +	Audio::SoundHandle _speechHandle; +	MidiContext _snd_midiContext; + +	int16 _inventoryList1[101]; +	int16 _inventoryList2[101]; +	int16 _inventoryList3[7]; +	int16 *_inventoryListPtrs[3]; +	int16 _inventoryListCount[9]; +	int16 _inventoryItemsInfoTable[NUM_INVENTORY_ITEMS]; +	int16 *_inventoryVar1; +	int16 *_inventoryVar2; +	int _currentCursorObject; + +	int _talkTextMode; +	int _talkListEnd; +	int _talkListCurrent; +	bool _talkTextRectDefined; +	bool _talkTextDisplayed; +	bool _talkTextInitialized; +	bool _skipTalkText; +	int _talkTextSpeed; +	int _keyCharTalkCounter; +	int _talkTableLastTalkingKeyChar; +	int _talkTableLastOtherKeyChar; +	int _talkTableLastStringNum; +	int _objectDescriptionNum; +	char _talkTextBuffer[200]; +	TalkEntry _talkTable[NUM_TALK_ENTRIES]; + +	bool _conversationChoicesUpdated; +	int _conversationReplyNum; +	bool _conversationEnded; +	int _conversationNum; +	int _drawCharacterConversionRepeatCounter; +	int _currentConversation; +	bool _disableConversationScript; +	bool _conversationAreaCleared; +	ConversationChoice _conversationChoicesTable[NUM_CONVERSATION_CHOICES]; + +	int16 _flagsTable[NUM_FLAGS]; +	KeyChar _keyCharsTable[NUM_KEYCHARS]; +	KeyChar *_sortedKeyCharsTable[NUM_KEYCHARS]; +	int _currentKeyCharNum; + +	int _newEpisodeNum; +	int _currentEpisodeNum; + +	Area _areaTable[NUM_AREAS]; +	int _areaTableCount; + +	int _currentAmountOfMoney; +	int _giveItemToKeyCharNum; +	int _giveItemToObjectNum; +	int _giveItemToCounter; +	int _currentRoomNum; +	int _waitingSetKeyCharNum1; +	int _waitingSetKeyCharNum2; +	int _waitingSetKeyCharNum3; +	uint8 _updatedRoomAreasTable[200]; +	Common::Rect _moveKeyCharRect; +	Common::Point _screenOffset; +	int _currentObjectNum; +	int _processRandomPaletteCounter; +	int16 _spriteScalingIndex[1000]; +	int16 _spriteScalingTable[1000]; + +	bool _fastWalkMode; + +	AnimationEntry _animationTable[NUM_ANIMATION_ENTRIES]; + +	Script _script; + +	Common::File _fData; +	Common::File _fSpeech[2]; +	int _compressedSpeechData; + +	uint8 *_textData; +	uint8 *_backdropBuffer; +	uint8 *_menuKitData; +	uint8 *_convKitData; +	uint8 *_sequenceDataTable[5]; +	uint8 *_programData; +	uint32 _programDataSize; +	uint8 *_mouseData; +	uint8 *_iconData; + +	SequenceEntry _sequenceEntryTable[NUM_SEQUENCES]; +	int _currentBitmapWidth; +	int _currentBitmapHeight; +	int _currentImageWidth; +	int _currentImageHeight; +	bool _speechPlaying; +	int _roomWidth; + +	uint8 *_programTextDataPtr; +	Common::Array<Common::Rect> _programRectsTable; +	Common::Array<ProgramPointData> _programPointsTable; +	Common::Array<ProgramWalkData> _programWalkTable; +	Common::Array<ProgramAreaData> _programAreaTable; +	Common::Array<ProgramBackgroundData> _programBackgroundTable; +	Common::Array<ProgramHitBoxData> _programHitBoxTable; +	Common::Array<ProgramActionScriptOffsetData> _programActionScriptOffsetTable; +	Common::Array<ProgramKeyCharScriptOffsetData> _programKeyCharScriptOffsetTable; +	Common::Array<ProgramConversationData> _programConversationTable; +	Common::Rect _cursorObjectRect; +	Common::Rect _talkTextRect, _talkTextRect2; +	Common::Rect _screenRect; +	Common::Rect _roomAreaRect; + +	bool _roomNeedRedraw; +	int _fullRedrawCounter; +	int _redrawScreenCounter1; +	uint8 *_offscreenBuffer; +	uint8 _paletteBuffer[256 * 4]; + +	static OpcodeProc _opcodesTable[NUM_OPCODES]; +	static SpriteData _spritesTable[NUM_SPRITES]; +	static const uint8 _directionsTable[]; +	static char _saveLoadDescriptionsTable[10][33]; +	static const Common::Rect _inventoryAreasTable[13]; +}; + +/* +	FLAGS LIST + +	115 : don't set backdrop palette on room loading +	118 : current amount of money +	119 : current cursor object +	176 : keychar max direction +	266 : keychar direction override +	267 : don't decode picture/sprite images (in load_image_helper) +	268 : don't decode picture/sprite images +	270 : play random sound +	290 : process random palette +	295 : game cycle counter (incremented) +	296 : game cycle counter (incremented) +	297 : game cycle counter (incremented) +	298 : game cycle counter (decremented) +	299 : game cycle counter (decremented) +	600 : last ascii key press +	603 : fade palette "scale" increment (in vbl handler) +	605 : fade palette "scale" +	606 : inventory redraw disabled +	607 : first palette color to fade +	608 : last palette color to fade +	609 : max fade palette "scale" +	610 : min fade palette "scale" +	611 : quit game +	612 : random number modulo +	613 : last generated random number +	614 : room scroll x offset +	615 : room scroll y offset +	616 : disable room scrolling +	617 : current speech file number +	621 : enable french version "features" +	902 : debug/draw walk boxes +	911 : load scripts/programs from external files +*/ + +} // namespace Touche + +#endif diff --git a/engines/touche/ui.cpp b/engines/touche/ui.cpp new file mode 100644 index 0000000000..997420345d --- /dev/null +++ b/engines/touche/ui.cpp @@ -0,0 +1,557 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 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$ + * $Id: $ + * + */ + +#include "common/stdafx.h" +#include "common/system.h" +#include "common/savefile.h" + +#include "touche/graphics.h" +#include "touche/touche.h" + +namespace Touche { + +static const Common::Rect buttonsRectTable1[15] = { +	Common::Rect(108, 120, 444, 135), +	Common::Rect(108, 136, 444, 151), +	Common::Rect(108, 152, 444, 167), +	Common::Rect(108, 168, 444, 183), +	Common::Rect(108, 184, 444, 199), +	Common::Rect(108, 200, 444, 215), +	Common::Rect(108, 216, 444, 231), +	Common::Rect(108, 232, 444, 247), +	Common::Rect(108, 248, 444, 263), +	Common::Rect(108, 264, 444, 279), +	Common::Rect(452, 120, 546, 144), +	Common::Rect(452, 152, 546, 176), +	Common::Rect(452, 216, 546, 240), +	Common::Rect(452, 248, 546, 272), +	Common::Rect(452, 184, 546, 208) +}; + +static const Common::Rect buttonsRectTable2[10] = { +	Common::Rect(396, 130, 420, 154), +	Common::Rect(396, 160, 420, 184), +	Common::Rect(396, 190, 420, 214), +	Common::Rect(126, 130, 380, 154), +	Common::Rect(126, 160, 380, 184), +	Common::Rect(126, 190, 380, 214), +	Common::Rect(126, 250, 150, 274), +	Common::Rect(396, 250, 420, 274), +	Common::Rect(154, 256, 392, 268), +	Common::Rect(126, 222, 420, 242) +}; + +static int16 settingsMenuTextsTable[] = { 0, 0, 0, -92, -93, -94, -87, -88, 0, -91 }; + +static const int16 optionsMenuTextsTable[] = { -52, -53, -54, -55, -90 }; + +static const int16 saveMenuTextsTable[] = { 2000, -56, -52, 2001, 0 }; + +static const int16 loadMenuTextsTable[] = { 2000, -56, -53, 2001, 0 }; + +bool ToucheEngine::ui_processEvents() { +	bool quit = false; +	OSystem::Event event; +	while (_system->pollEvent(event)) { +		switch (event.type) { +		case OSystem::EVENT_QUIT: +			quit = true; +			break; +		case OSystem::EVENT_KEYDOWN: +			if (_saveLoadCurrentDescriptionLen != -1) { +				if (event.kbd.keycode == 8) { +					if (_saveLoadCurrentDescriptionLen > 0) { +						--_saveLoadCurrentDescriptionLen; +						_saveLoadCurrentDescription[_saveLoadCurrentDescriptionLen] = 0; +					} +				} else if (isprint((char)event.kbd.ascii)) { +					if (_saveLoadCurrentDescriptionLen < 32) { +						_saveLoadCurrentDescription[_saveLoadCurrentDescriptionLen] = (char)event.kbd.ascii; +						++_saveLoadCurrentDescriptionLen; +						_saveLoadCurrentDescription[_saveLoadCurrentDescriptionLen] = 0; +					} +				} +			} +			break; +		case OSystem::EVENT_MOUSEMOVE: +			_inp_mousePos.x = event.mouse.x; +			_inp_mousePos.y = event.mouse.y; +			break; +		case OSystem::EVENT_LBUTTONDOWN: +			_inp_mousePos.x = event.mouse.x; +			_inp_mousePos.y = event.mouse.y; +			_inp_mouseButtonClicked = true; +			break; +		case OSystem::EVENT_LBUTTONUP: +			_inp_mousePos.x = event.mouse.x; +			_inp_mousePos.y = event.mouse.y; +			break; +		default: +			break; +		} +	} +	_system->updateScreen(); +	_system->delayMillis(50); +	return quit; +} + +void ToucheEngine::ui_drawButtonBorders(const Common::Rect *r, int count) { +	while (count--) { +		Graphics::drawRect(_offscreenBuffer, 640, r->left, r->top, r->width(), r->height(), 0xF7, 0xF9); +		++r; +	} +} + +void ToucheEngine::ui_drawMusicVolumeBar() { +	int volume = _snd_midiContext.volume * 232 / 256; +	if (volume != 0) { +		Graphics::fillRect(_offscreenBuffer, 640, 157, 259, volume, 6, 0xF0); +	} +	if (volume <= 232) { +		Graphics::fillRect(_offscreenBuffer, 640, 157 + volume, 259, 232 - volume, 6, 0xD2); +	} +} + +void ToucheEngine::ui_drawTalkMode() { +	settingsMenuTextsTable[0] = 0; +	settingsMenuTextsTable[1] = 0; +	settingsMenuTextsTable[2] = 0; +	settingsMenuTextsTable[_talkTextMode] = -86; +} + +void ToucheEngine::ui_drawAllBorders() { +	Graphics::fillRect(_offscreenBuffer, 640, 90, 102, 460, 196, 248); +	Graphics::drawRect(_offscreenBuffer, 640, 90, 102, 460, 196, 0xF7, 0xF9); +	Graphics::drawRect(_offscreenBuffer, 640, 106, 118, 340, 164, 0xF9, 0xF7); +	ui_drawButtonBorders(&buttonsRectTable1[10], 5); +} + +void ToucheEngine::ui_drawSaveGamesList(int page) { +	ui_drawAllBorders(); +	for (int i = 0; i < 10; ++i) { +		const Common::Rect *r = &buttonsRectTable1[i]; +		uint8 color = (_saveLoadCurrentSlot == i) ? 0xCB : 0xD9; +		char num[10]; +		sprintf(num, "%d.", page + i); +		Graphics::drawString16(_offscreenBuffer, 640, color, r->left, r->top, num); +		Graphics::drawString16(_offscreenBuffer, 640, color, r->left + 30, r->top, _saveLoadDescriptionsTable[i]); +	} +} + +void ToucheEngine::ui_drawCurrentGameStateDescription() { +	const Common::Rect *r = &buttonsRectTable1[_saveLoadCurrentSlot]; +	Graphics::fillRect(_offscreenBuffer, 640, r->left, r->top, r->width(), r->height(), 0xF8); + +	int y = r->top; +	int x = r->left; +	char num[10]; +	sprintf(num, "%d.", _saveLoadCurrentSlot); +	Graphics::drawString16(_offscreenBuffer, 640, 0xCB, x, y, num); +	x += 30; +	Graphics::drawString16(_offscreenBuffer, 640, 0xCB, x, y, _saveLoadCurrentDescription); +	x += Graphics::getStringWidth16(_saveLoadCurrentDescription); +	Graphics::drawString16(_offscreenBuffer, 640, 0xCB, x, y, "_"); + +	updateScreenArea(_offscreenBuffer, 640, r->left, r->top, r->left, r->top, r->width(), r->height()); +} + +void ToucheEngine::ui_drawSaveLoadMenu(int page, int saveOrLoad) { +	for (int i = 0; i < 10; ++i) { +		_saveLoadDescriptionsTable[i][0] = 0; +		const int gameState = page + i; +		if (_saveLoadMarks[gameState]) { +			readGameStateDescription(gameState, _saveLoadDescriptionsTable[i], 32); +		} +	} +	ui_drawSaveGamesList(page); +	if (saveOrLoad == 0) { +		ui_drawButtonText(loadMenuTextsTable, &buttonsRectTable1[10], 5, true); +	} else { +		ui_drawButtonText(saveMenuTextsTable, &buttonsRectTable1[10], 5, true); +	} +	updateScreenArea(_offscreenBuffer, 640, 90, 102, 90, 102, 460, 196); +} + +int ToucheEngine::ui_getButtonPressed(const Common::Rect *r, int count) const { +	for (int i = 0; i < count; ++i) { +		if (r[i].contains(_inp_mousePos)) { +			return i; +		} +	} +	return -1; +} + +void ToucheEngine::ui_drawButtonText(const int16 *texts, const Common::Rect *r, int count, bool centerTexts) { +	for (int i = 0; i < count; ++i, ++texts, ++r) { +		int x, y; +		if (*texts < 2000) { +			const char *str = getString(*texts); +			x = r->left; +			y = r->top; +			if (centerTexts) { +				const int w = getStringWidth(16, *texts); +				x += (r->width() - w) / 2; +				y += (r->height() - 16) / 2; +			} +			Graphics::drawString16(_offscreenBuffer, 640, 0xFF, x, y, str); +		} else { +			x = r->left + r->width() / 2; +			y = r->top + r->height() / 2; +			int dx, dy; +			switch (*texts) { +			case 2000: // up arrow +				dx = 1; +				dy = 2; +				break; +			case 2001: // down arrow +				dx = -1; +				dy = -2; +				break; +			} +			ui_drawArrow(x, y + dy + 1, dx, 0xD2); +			ui_drawArrow(x, y + dy, dx, 0xFF); +		} +	} +} + +void ToucheEngine::ui_drawArrow(int x, int y, int dx, uint8 color) { +	static const int16 arrowCoordsTable[] = { +		 5,  0,  9,  0, +		 5,  0,  5,  4, +		-5,  4,  5,  4, +		-5,  0, -5,  4, +		-9,  0, -5,  0, +		-9,  0,  0, -9, +		 0, -9,  9,  0 +	}; +	for (uint i = 0; i < ARRAYSIZE(arrowCoordsTable) / 4; ++i) { +		const int x1 = x + arrowCoordsTable[i * 4 + 0]; +		const int y1 = y + arrowCoordsTable[i * 4 + 1] * dx; +		const int x2 = x + arrowCoordsTable[i * 4 + 2]; +		const int y2 = y + arrowCoordsTable[i * 4 + 3] * dx; +		Graphics::drawLine(_offscreenBuffer, 640, x1, y1, x2, y2, color); +	} +} + +void ToucheEngine::ui_drawOptionsMenu() { +	ui_drawTalkMode(); +	ui_drawAllBorders(); +	ui_drawButtonText(optionsMenuTextsTable, &buttonsRectTable1[10], 5, true); +	ui_drawButtonBorders(buttonsRectTable2, 10); +	ui_drawButtonText(settingsMenuTextsTable, buttonsRectTable2, 10, true); +	ui_drawMusicVolumeBar(); +	updateScreenArea(_offscreenBuffer, 640, 90, 102, 90, 102, 460, 196); +} + +int ToucheEngine::ui_handleSaveLoad(int saveOrLoad) { +	char gameStateFileName[16]; +	generateGameStateFileName(999, gameStateFileName, 15, true); +	_saveFileMan->listSavefiles(gameStateFileName, _saveLoadMarks, NUM_GAMESTATE_FILES); +	int ret = 0; +	bool quitMenu = false; +	while (!quitMenu) { +		_saveLoadCurrentDescription[0] = 0; +		_saveLoadCurrentDescriptionLen = 0; +		ui_drawSaveLoadMenu(_saveLoadCurrentPage, saveOrLoad); +		int descriptionLen = 0; +		int button = -1; +		while (button == -1 && !quitMenu) { +			button = ui_getButtonPressed(buttonsRectTable1, 15); +			if (!_inp_mouseButtonClicked) { +				button = -1; +			} +			if (saveOrLoad == 0) { +				_saveLoadCurrentPage = (_saveLoadCurrentSlot / 10) * 10; +				if (_saveLoadCurrentDescriptionLen != descriptionLen) { +					descriptionLen = _saveLoadCurrentDescriptionLen; +					ui_drawCurrentGameStateDescription(); +					strcpy(_saveLoadDescriptionsTable[_saveLoadCurrentSlot % 10], _saveLoadCurrentDescription); +				} +			} +			quitMenu = ui_processEvents(); +		} +		_inp_mouseButtonClicked = false; +		switch (button) { +		case 10: +			_saveLoadCurrentPage -= 10; +			if (_saveLoadCurrentPage < 0) { +				_saveLoadCurrentPage = 90; +			} +			break; +		case 11: +			quitMenu = true; +			ret = 0; +			break; +		case 12: +			quitMenu = true; +			ret = 1; +			if (saveOrLoad == 0) { +				if (saveGameState(_saveLoadCurrentSlot, _saveLoadDescriptionsTable[_saveLoadCurrentSlot % 10])) { +					ret = 2; +				} +			} else { +				if (loadGameState(_saveLoadCurrentSlot, _saveLoadDescriptionsTable[_saveLoadCurrentSlot % 10])) { +					ret = 2; +				} +			} +			break; +		case 13: +			_saveLoadCurrentPage += 10; +			if (_saveLoadCurrentPage > 90) { +				_saveLoadCurrentPage = 0; +			} +			break; +		default: +			if (button >= 0 && button <= 9) { +				_saveLoadCurrentSlot = _saveLoadCurrentPage + button; +			} +			break; +		} +	} +	return ret; +} + +void ToucheEngine::ui_handleOptions(int forceDisplay) { +	if (_disabledInputCounter == 0 || forceDisplay != 0) { +		_saveLoadCurrentDescriptionLen = -1; +		updateCursor(_currentKeyCharNum); +		int16 mode = _flagsTable[618]; +		_flagsTable[618] = 0; +		updateEntireScreen(); +		bool quitMenu = false; +		while (!quitMenu) { +			ui_drawOptionsMenu(); +			int button = -1; +			while (button == -1 && !quitMenu) { +				if (_inp_mouseButtonClicked) { +					button = ui_getButtonPressed(buttonsRectTable1, 15); +					if (button < 10) { +						button = ui_getButtonPressed(buttonsRectTable2, 10) + 20; +					} +				} +				quitMenu = ui_processEvents(); +			} +			_inp_mouseButtonClicked = false; +			switch (button) { +			case 10: +				if (ui_handleSaveLoad(1) == 2) { +					quitMenu = true; +				} +				break; +			case 11: +				if (ui_handleSaveLoad(0) == 2) { +					quitMenu = true; +				} +				break; +			case 12: +				quitMenu = true; +				break; +			case 13: +				quitMenu = true; +				_flagsTable[611] = 1; +				break; +			case 14: +				restart(); +				quitMenu = true; +				break; +			case 20: +				_talkTextMode = kTalkModeTextOnly; +				break; +			case 21: +				_talkTextMode = kTalkModeVoiceOnly; +				break; +			case 22: +				_talkTextMode = kTalkModeVoiceAndText; +				break; +			case 26: +				if (_snd_midiContext.volume > 0) { +					_snd_midiContext.volume -= 16; +				} +				break; +			case 27: +				if (_snd_midiContext.volume < 256) { +					_snd_midiContext.volume += 16; +				} +				break; +			} +		} +		_fullRedrawCounter = 2; +		_flagsTable[618] = mode; +		if (_flagsTable[611] != 0) { +			_flagsTable[611] = ui_displayQuitDialog(); +		} +		_snd_midiContext.currentVolume = _snd_midiContext.volume; +	} +} + +void ToucheEngine::ui_drawActionsPanel(int dstX, int dstY, int deltaX, int deltaY) { +	Graphics::copyRect(_offscreenBuffer, 640, dstX, dstY, +	  _menuKitData, 42, 0, 0, +	  14, 24, +	  Graphics::kTransparent); +	Graphics::copyRect(_offscreenBuffer, 640, deltaX - 14 + dstX, dstY, +	  _menuKitData, 42, 0, 40, +	  14, 24, +	  Graphics::kTransparent); +	Graphics::copyRect(_offscreenBuffer, 640, dstX, deltaY - 16 + dstY,  +	  _menuKitData, 42, 0, 24,  +	  14, 16, +	  Graphics::kTransparent); +	Graphics::copyRect(_offscreenBuffer, 640, deltaX - 14 + dstX, deltaY - 16 + dstY, +	  _menuKitData, 42, 0, 64,  +	  14, 16, +	  Graphics::kTransparent); +	int x1 = deltaX - 28; +	int x2 = dstX + 14; +	while (x1 > 0) { +		int w = (x1 > 14) ? 14 : x1; +		Graphics::copyRect(_offscreenBuffer, 640, x2, dstY,  +		  _menuKitData, 42, 0, 80,  +		  w, 24,  +		  Graphics::kTransparent); +		Graphics::copyRect(_offscreenBuffer, 640, x2, deltaY - 16 + dstY,  +		  _menuKitData, 42, 0, 104,  +		  w, 16, +		  Graphics::kTransparent); +		x1 -= 14; +		x2 += 14; +	} +	x1 = deltaY - 40; +	x2 = dstY + 24; +	while (x1 > 0) { +		int w = (x1 > 120) ? 120 : x1; +		Graphics::copyRect(_offscreenBuffer, 640, dstX, x2,  +		  _menuKitData, 42, 14, 0,  +		  14, w,  +		  Graphics::kTransparent); +		Graphics::copyRect(_offscreenBuffer, 640, deltaX - 14 + dstX, x2,  +		  _menuKitData, 42, 28, 0,  +		  14, w, +		  Graphics::kTransparent); +		x1 -= 120; +		x2 += 120; +	} +} + +void ToucheEngine::ui_drawConversationPanelBorder(int dstY, int srcX, int srcY) { +	int dstX = 24; +	int w = 48; +	for (int i = 0; i < 13; ++i) { +		if (i == 12) { +			w = 34; +		} +		Graphics::copyRect(_offscreenBuffer, 640, dstX, dstY, _convKitData, 152, srcX, srcY, w, 6); +		dstX += w; +	} +} + +void ToucheEngine::ui_drawConversationPanel() { +	Graphics::copyRect(_offscreenBuffer, 640, 0, 320, _convKitData, 152, 0, 0, 72, 80); +	int dstX = 54; +	int dstY = 326; +	int w = 96; +	for (int i = 0; i < 7; ++i) { +		if (i == 5) { +			w = 50; +		} +		Graphics::copyRect(_offscreenBuffer, 640, dstX, dstY, _convKitData, 152, 24, 6, w, 68); +		dstX += w; +	} +	--dstX; +	Graphics::copyRect(_offscreenBuffer, 640, dstX, 320, _convKitData, 152, 120, 0, 7, 80); +	dstX -= 3; +	if (_drawCharacterConversionRepeatCounter != 0) { +		ui_drawConversationPanelBorder(320, 72, 0); +		Graphics::copyRect(_offscreenBuffer, 640, 0, 320, _convKitData, 152, 128, 0, 24, 21); +		Graphics::copyRect(_offscreenBuffer, 640, dstX, 320, _convKitData, 152, 128, 34, 10, 10); +	} else { +		ui_drawConversationPanelBorder(320, 24, 0); +	} +	if (_conversationChoicesTable[_drawCharacterConversionRepeatCounter + 4].msg != 0) { +		ui_drawConversationPanelBorder(394, 72, 74); +		Graphics::copyRect(_offscreenBuffer, 640, 0, 379, _convKitData, 152, 128, 59, 24, 21); +		Graphics::copyRect(_offscreenBuffer, 640, dstX, 394, _convKitData, 152, 128, 46, 10, 6); +	} else { +		ui_drawConversationPanelBorder(394, 24, 74); +	} +} + +void ToucheEngine::ui_printStatusString(const char *str) { +	Graphics::fillRect(_offscreenBuffer, 640, 0, 0, 640, 16, 0xD7); +	Graphics::drawRect(_offscreenBuffer, 640, 0, 0, 640, 16, 0xD6, 0xD8); +	Graphics::drawString16(_offscreenBuffer, 640, 0xFF, 0, 0, str); +	updateScreenArea(_offscreenBuffer, 640, 0, 0, 0, 0, 640, 16); +} + +void ToucheEngine::ui_clearStatusString() { +	Graphics::copyRect(_offscreenBuffer, 640, 0, 0,  +	  _backdropBuffer, _currentBitmapWidth, _flagsTable[614], _flagsTable[615], +	  640, 16); +	updateScreenArea(_offscreenBuffer, 640, 0, 0, 0, 0, 640, 16); +} + +int ToucheEngine::ui_displayQuitDialog() { +	debug(kDebugUserIntf, "ui_displayQuitDialog()"); +	ui_printStatusString(getString(-85)); +	int ret = 0; +	bool quitLoop = false; +	while (!quitLoop) { +		OSystem::Event event; +		while (_system->pollEvent(event)) { +			switch (event.type) { +			case OSystem::EVENT_QUIT: +				quitLoop = true; +				ret = 1; +				break; +			case OSystem::EVENT_KEYDOWN: +				quitLoop = true; +				switch (_language) { +				case Common::FR_FRA: +					if (event.kbd.ascii == 'o' || event.kbd.ascii == 'O') { +						ret = 1; +					} +					break; +				default: +					if (event.kbd.ascii == 'y' || event.kbd.ascii == 'Y') { +						ret = 1; +					} +					break; +				} +				break; +			default: +				break; +			} +		} +		_system->delayMillis(50); +	} +	ui_clearStatusString(); +	return ret; +} + +void ToucheEngine::ui_displayTextMode(int str) { +	debug(kDebugUserIntf, "ui_displayTextMode(%d)", str); +	ui_printStatusString(getString(str)); +	_system->delayMillis(1000); +	ui_clearStatusString(); +} + +} // namespace Touche | 
