From 13d9cdbd26b1c07edf47b9e4731b9d652a294ba5 Mon Sep 17 00:00:00 2001 From: Gregory Montoir Date: Fri, 3 Nov 2006 21:23:07 +0000 Subject: added 'touche' engine for the game 'Touche: The Adventures of the 5th Musketeer' svn-id: r24592 --- engines/engines.mk | 15 +- engines/touche/graphics.cpp | 210 +++ engines/touche/graphics.h | 54 + engines/touche/module.mk | 19 + engines/touche/opcodes.cpp | 822 +++++++++++ engines/touche/plugin.cpp | 154 ++ engines/touche/resource.cpp | 645 +++++++++ engines/touche/saveload.cpp | 418 ++++++ engines/touche/staticres.cpp | 659 +++++++++ engines/touche/touche.cpp | 3271 ++++++++++++++++++++++++++++++++++++++++++ engines/touche/touche.h | 778 ++++++++++ engines/touche/ui.cpp | 557 +++++++ 12 files changed, 7597 insertions(+), 5 deletions(-) create mode 100644 engines/touche/graphics.cpp create mode 100644 engines/touche/graphics.h create mode 100644 engines/touche/module.mk create mode 100644 engines/touche/opcodes.cpp create mode 100644 engines/touche/plugin.cpp create mode 100644 engines/touche/resource.cpp create mode 100644 engines/touche/saveload.cpp create mode 100644 engines/touche/staticres.cpp create mode 100644 engines/touche/touche.cpp create mode 100644 engines/touche/touche.h create mode 100644 engines/touche/ui.cpp (limited to 'engines') 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 +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 +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 +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 +static void saveOrLoad(S &s, SequenceEntry &seq) { + saveOrLoad(s, seq.sprNum); + saveOrLoad(s, seq.seqNum); +} + +template +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 +static void saveOrLoad(S &s, TalkEntry &entry) { + saveOrLoad(s, entry.otherKeyChar); + saveOrLoad(s, entry.talkingKeyChar); + saveOrLoad(s, entry.num); +} + +template +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 +static void saveOrLoad(S &s, Area &area) { + saveOrLoad(s, area.r); + saveOrLoad(s, area.srcX); + saveOrLoad(s, area.srcY); +} + +template +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 +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 +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 +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(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(_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(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(_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(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(offs - cursorW / 2, 0, 640 - cursorW); + offs = cursorPosX + 14; + h *= 16; + int cursorH = h + 28; + int cursorPosY = CLIP(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(_programPointsTable[i].x - dstPosX); + int dy = ABS(_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 _programRectsTable; + Common::Array _programPointsTable; + Common::Array _programWalkTable; + Common::Array _programAreaTable; + Common::Array _programBackgroundTable; + Common::Array _programHitBoxTable; + Common::Array _programActionScriptOffsetTable; + Common::Array _programKeyCharScriptOffsetTable; + Common::Array _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 -- cgit v1.2.3