aboutsummaryrefslogtreecommitdiff
path: root/engines/sky/text.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sky/text.cpp')
-rw-r--r--engines/sky/text.cpp703
1 files changed, 703 insertions, 0 deletions
diff --git a/engines/sky/text.cpp b/engines/sky/text.cpp
new file mode 100644
index 0000000000..2c997340ab
--- /dev/null
+++ b/engines/sky/text.cpp
@@ -0,0 +1,703 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-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 "sky/disk.h"
+#include "sky/logic.h"
+#include "sky/text.h"
+#include "sky/sky.h"
+#include "sky/skydefs.h"
+#include "sky/struc.h"
+#include "sky/compact.h"
+
+namespace Sky {
+
+#define FIRST_TEXT_SEC 77
+#define FIRST_TEXT_BUFFER 274
+#define LAST_TEXT_BUFFER 284
+#define NO_OF_TEXT_SECTIONS 8 // 8 sections per language
+#define CHAR_SET_FILE 60150
+#define MAX_SPEECH_SECTION 7
+#define CHAR_SET_HEADER 128
+#define MAX_NO_LINES 10
+
+Text::Text(Disk *skyDisk, SkyCompact *skyCompact) {
+ _skyDisk = skyDisk;
+ _skyCompact = skyCompact;
+
+ initHuffTree();
+
+ _mainCharacterSet.addr = _skyDisk->loadFile(CHAR_SET_FILE);
+ _mainCharacterSet.charHeight = MAIN_CHAR_HEIGHT;
+ _mainCharacterSet.charSpacing = 0;
+
+ fnSetFont(0);
+
+ if (!SkyEngine::isDemo()) {
+ _controlCharacterSet.addr = _skyDisk->loadFile(60520);
+ _controlCharacterSet.charHeight = 12;
+ _controlCharacterSet.charSpacing = 0;
+
+ _linkCharacterSet.addr = _skyDisk->loadFile(60521);
+ _linkCharacterSet.charHeight = 12;
+ _linkCharacterSet.charSpacing = 1;
+
+ patchLINCCharset();
+ } else {
+ _controlCharacterSet.addr = NULL;
+ _linkCharacterSet.addr = NULL;
+ }
+}
+
+Text::~Text(void) {
+ for (int i = FIRST_TEXT_BUFFER; i <= LAST_TEXT_BUFFER; i++)
+ if (SkyEngine::_itemList[i]) {
+ free(SkyEngine::_itemList[i]);
+ SkyEngine::_itemList[i] = NULL;
+ }
+
+ if (_mainCharacterSet.addr)
+ free(_mainCharacterSet.addr);
+ if (_controlCharacterSet.addr)
+ free(_controlCharacterSet.addr);
+ if (_linkCharacterSet.addr)
+ free(_linkCharacterSet.addr);
+}
+
+void Text::patchChar(byte *charSetPtr, int width, int height, int c, const uint16 *data) {
+ byte *ptr = charSetPtr + (CHAR_SET_HEADER + (height << 2) * c);
+
+ charSetPtr[c] = width;
+
+ for (int i = 0; i < height; i++) {
+ ptr[i * 4 + 0] = (data[i] & 0xFF00) >> 8;
+ ptr[i * 4 + 1] = data[i] & 0x00FF;
+ ptr[i * 4 + 2] = (data[i + height] & 0xFF00) >> 8;
+ ptr[i * 4 + 3] = data[i + height] & 0x00FF;
+ }
+}
+
+void Text::patchLINCCharset() {
+ // The LINC terminal charset looks strange in some cases. This
+ // function attempts to patch up the worst blemishes.
+
+ byte *charSetPtr = _controlCharacterSet.addr;
+ int charHeight = _controlCharacterSet.charHeight;
+
+ // In v0.0288, the character spacing is too wide. Decrease the width
+ // for every character by one, except for space which needs to be one
+ // pixel wider than before.
+
+ if (SkyEngine::_systemVars.gameVersion == 288) {
+ for (int i = 1; i < CHAR_SET_HEADER; i++)
+ charSetPtr[i]--;
+ charSetPtr[0]++;
+ }
+
+ // NOTE: I have only tested this part of the code with v0.0372
+
+ // Several characters are different in this charset than in the other
+ // two. This is particularly noticeable when using a non-English
+ // version.
+
+ // Since the same character data is used both in LINC terminals and
+ // in LINC-space, we need to provide a mask (for the black outline)
+ // even though it's only visible in the latter. We store the mask
+ // as the second half of the array to make it more human-readable.
+ // In the actual game data, the character and mask are interleaved.
+
+ const uint16 slash[] = {
+ 0x0000, 0x0000, 0x0000, 0x0800, 0x1000, 0x1000,
+ 0x2000, 0x2000, 0x4000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0800, 0x1C00, 0x3800, 0x3800,
+ 0x7000, 0x7000, 0xE000, 0x4000, 0x0000, 0x0000
+ };
+
+ const uint16 lt[] = {
+ 0x0000, 0x0000, 0x0800, 0x1000, 0x2000, 0x4000,
+ 0x2000, 0x1000, 0x0800, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0800, 0x1C00, 0x3800, 0x7000, 0xE000,
+ 0x7000, 0x3800, 0x1C00, 0x0800, 0x0000, 0x0000
+ };
+
+ const uint16 gt[] = {
+ 0x0000, 0x0000, 0x4000, 0x2000, 0x1000, 0x0800,
+ 0x1000, 0x2000, 0x4000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x4000, 0xE000, 0x7000, 0x3800, 0x1C00,
+ 0x3800, 0x7000, 0xE000, 0x4000, 0x0000, 0x0000
+ };
+
+ const uint16 a_umlaut[] = {
+ 0x0000, 0x0000, 0x2800, 0x0000, 0x3000, 0x0800,
+ 0x3800, 0x4800, 0x3800, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x2800, 0x7C00, 0x3800, 0x7800, 0x3C00,
+ 0x7C00, 0xFC00, 0x7C00, 0x3800, 0x0000, 0x0000
+ };
+
+ const uint16 o_umlaut[] = {
+ 0x0000, 0x0000, 0x4800, 0x0000, 0x3000, 0x4800,
+ 0x4800, 0x4800, 0x3000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x4800, 0xFC00, 0x7800, 0x7800, 0xFC00,
+ 0xFC00, 0xFC00, 0x7800, 0x3000, 0x0000, 0x0000
+ };
+
+ const uint16 u_umlaut[] = {
+ 0x0000, 0x0000, 0x4800, 0x0000, 0x4800, 0x4800,
+ 0x4800, 0x4800, 0x3800, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x4800, 0xFC00, 0x4800, 0xFC00, 0xFC00,
+ 0xFC00, 0xFC00, 0x7C00, 0x3800, 0x0000, 0x0000
+ };
+
+ const uint16 A_umlaut[] = {
+ 0x0000, 0x4800, 0x0000, 0x3000, 0x4800, 0x4800,
+ 0x7800, 0x4800, 0x4800, 0x0000, 0x0000, 0x0000,
+ 0x4800, 0xFC00, 0x7800, 0x7800, 0xFC00, 0xFC00,
+ 0xFC00, 0xFC00, 0xFC00, 0x4800, 0x0000, 0x0000
+ };
+
+ const uint16 O_umlaut[] = {
+ 0x0000, 0x4800, 0x0000, 0x3000, 0x4800, 0x4800,
+ 0x4800, 0x4800, 0x3000, 0x0000, 0x0000, 0x0000,
+ 0x4800, 0xFC00, 0x7800, 0x7800, 0xFC00, 0xFC00,
+ 0xFC00, 0xFC00, 0x7800, 0x3000, 0x0000, 0x0000
+ };
+
+ const uint16 U_umlaut[] = {
+ 0x0000, 0x4800, 0x0000, 0x4800, 0x4800, 0x4800,
+ 0x4800, 0x4800, 0x3000, 0x0000, 0x0000, 0x0000,
+ 0x4800, 0xFC00, 0x4800, 0xFC00, 0xFC00, 0xFC00,
+ 0xFC00, 0xFC00, 0x7800, 0x3000, 0x0000, 0x0000
+ };
+
+ const uint16 normal_j[] = {
+ 0x0000, 0x0000, 0x0000, 0x0800, 0x0000, 0x0800,
+ 0x0800, 0x0800, 0x0800, 0x4800, 0x3000, 0x0000,
+ 0x0000, 0x0000, 0x0800, 0x1C00, 0x0800, 0x1C00,
+ 0x1C00, 0x1C00, 0x5C00, 0xFC00, 0x7800, 0x3000
+ };
+
+ const uint16 german_sz[] = {
+ 0x0000, 0x0000, 0x2000, 0x5000, 0x5000, 0x4800,
+ 0x4800, 0x4800, 0x5000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x2000, 0x7000, 0xF800, 0xF800, 0xFC00,
+ 0xFC00, 0xFC00, 0xF800, 0x7000, 0x0000, 0x0000
+ };
+
+ const uint16 normal_1[] = {
+ 0x0000, 0x0000, 0x0000, 0x1000, 0x7000, 0x1000,
+ 0x1000, 0x1000, 0x7c00, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x1000, 0x7800, 0xF800, 0x7800,
+ 0x3800, 0x7c00, 0xFE00, 0x7c00, 0x0000, 0x0000
+ };
+
+ // The next five are needed for at least the French version
+
+ const uint16 e_acute[] = {
+ 0x0000, 0x1000, 0x2000, 0x0000, 0x3000, 0x4800,
+ 0x7800, 0x4000, 0x3000, 0x0000, 0x0000, 0x0000,
+ 0x1000, 0x3800, 0x7000, 0x7000, 0x7800, 0xFC00,
+ 0xFC00, 0xF800, 0x7800, 0x3000, 0x0000, 0x0000
+ };
+
+ const uint16 e_grave[] = {
+ 0x0000, 0x2000, 0x1000, 0x0000, 0x3000, 0x4800,
+ 0x7800, 0x4000, 0x3000, 0x0000, 0x0000, 0x0000,
+ 0x2000, 0x7000, 0x3800, 0x3800, 0x7800, 0xFC00,
+ 0xFC00, 0xF800, 0x7800, 0x3000, 0x0000, 0x0000
+ };
+
+ const uint16 e_circumflex[] = {
+ 0x0000, 0x1000, 0x2800, 0x0000, 0x3000, 0x4800,
+ 0x7800, 0x4000, 0x3000, 0x0000, 0x0000, 0x0000,
+ 0x1000, 0x3800, 0x7C00, 0x7800, 0x7800, 0xFC00,
+ 0xFC00, 0xF800, 0x7800, 0x3000, 0x0000, 0x0000
+ };
+
+ const uint16 o_circumflex[] = {
+ 0x0000, 0x1000, 0x2800, 0x0000, 0x3000, 0x4800,
+ 0x4800, 0x4800, 0x3000, 0x0000, 0x0000, 0x0000,
+ 0x1000, 0x3800, 0x7C00, 0x3800, 0x7800, 0xFC00,
+ 0xFC00, 0xFC00, 0x7800, 0x3000, 0x0000, 0x0000
+ };
+
+ const uint16 u_circumflex[] = {
+ 0x0000, 0x1000, 0x2800, 0x0000, 0x4800, 0x4800,
+ 0x4800, 0x4800, 0x3800, 0x0000, 0x0000, 0x0000,
+ 0x1000, 0x3800, 0x7C00, 0x7800, 0xFC00, 0xFC00,
+ 0xFC00, 0xFC00, 0x7C00, 0x3800, 0x0000, 0x0000
+ };
+
+ patchChar(charSetPtr, 5, charHeight, 3, u_umlaut);
+ patchChar(charSetPtr, 5, charHeight, 8, german_sz);
+ patchChar(charSetPtr, 5, charHeight, 9, o_umlaut);
+ patchChar(charSetPtr, 5, charHeight, 93, U_umlaut);
+ patchChar(charSetPtr, 5, charHeight, 74, normal_j);
+ patchChar(charSetPtr, 6, charHeight, 17, normal_1);
+
+ patchChar(charSetPtr, 5, charHeight, 11, e_acute);
+ patchChar(charSetPtr, 5, charHeight, 61, e_grave);
+ patchChar(charSetPtr, 5, charHeight, 29, e_circumflex);
+ patchChar(charSetPtr, 5, charHeight, 32, o_circumflex);
+ patchChar(charSetPtr, 5, charHeight, 30, u_circumflex);
+
+ if (SkyEngine::_systemVars.gameVersion <= 303) {
+ patchChar(charSetPtr, 5, charHeight, 10, a_umlaut);
+ } else {
+ patchChar(charSetPtr, 5, charHeight, 94, A_umlaut);
+ patchChar(charSetPtr, 5, charHeight, 95, O_umlaut);
+
+ // Used by, for instance, the BRIEFING.DOC file in all (?) languages
+ patchChar(charSetPtr, 5, charHeight, 96, lt);
+ patchChar(charSetPtr, 5, charHeight, 97, gt);
+ patchChar(charSetPtr, 5, charHeight, 98, slash);
+ }
+}
+
+void Text::fnSetFont(uint32 fontNr) {
+
+ struct charSet *newCharSet;
+
+ switch (fontNr) {
+ case 0:
+ newCharSet = &_mainCharacterSet;
+ break;
+ case 1:
+ newCharSet = &_controlCharacterSet;
+ break;
+ case 2:
+ newCharSet = &_linkCharacterSet;
+ break;
+ default:
+ error("Tried to set invalid font (%d)", fontNr);
+ }
+
+ _curCharSet = fontNr;
+ _characterSet = newCharSet->addr;
+ _charHeight = (byte)newCharSet->charHeight;
+ _dtCharSpacing = newCharSet->charSpacing;
+}
+
+void Text::fnTextModule(uint32 textInfoId, uint32 textNo) {
+
+ fnSetFont(1);
+ uint16* msgData = (uint16 *)_skyCompact->fetchCpt(textInfoId);
+ lowTextManager_t textId = lowTextManager(textNo, msgData[1], msgData[2], 209, false);
+ Logic::_scriptVariables[RESULT] = textId.compactNum;
+ Compact *textCompact = _skyCompact->fetchCpt(textId.compactNum);
+ textCompact->xcood = msgData[3];
+ textCompact->ycood = msgData[4];
+ fnSetFont(0);
+}
+
+void Text::getText(uint32 textNr) { //load text #"textNr" into textBuffer
+
+ if (patchMessage(textNr))
+ return;
+
+ uint32 sectionNo = (textNr & 0x0F000) >> 12;
+
+ if (SkyEngine::_itemList[FIRST_TEXT_SEC + sectionNo] == NULL) { //check if already loaded
+ debug(5, "Loading Text item(s) for Section %d", (sectionNo >> 2));
+
+ uint32 fileNo = sectionNo + ((SkyEngine::_systemVars.language * NO_OF_TEXT_SECTIONS) + 60600);
+ SkyEngine::_itemList[FIRST_TEXT_SEC + sectionNo] = (void **)_skyDisk->loadFile((uint16)fileNo);
+ }
+ uint8 *textDataPtr = (uint8 *)SkyEngine::_itemList[FIRST_TEXT_SEC + sectionNo];
+
+ uint32 offset = 0;
+
+ uint32 blockNr = textNr & 0xFE0;
+ textNr &= 0x1F;
+
+ if (blockNr) {
+ uint16 *blockPtr = (uint16*)(textDataPtr + 4);
+ uint32 nr32MsgBlocks = blockNr >> 5;
+
+ do {
+ offset += READ_LE_UINT16(blockPtr);
+ blockPtr++;
+ } while (--nr32MsgBlocks);
+ }
+
+ if (textNr) {
+ uint8 *blockPtr = textDataPtr + blockNr + READ_LE_UINT16(textDataPtr);
+ do {
+ uint8 skipBytes = *blockPtr++;
+ if (skipBytes & 0x80) {
+ skipBytes &= 0x7F;
+ skipBytes <<= 3;
+ }
+ offset += skipBytes;
+ } while (--textNr);
+ }
+
+ uint32 bitPos = offset & 3;
+ offset >>= 2;
+ offset += READ_LE_UINT16(textDataPtr + 2);
+
+ textDataPtr += offset;
+
+ //bit pointer: 0->8, 1->6, 2->4 ...
+ bitPos ^= 3;
+ bitPos++;
+ bitPos <<= 1;
+
+ char *dest = (char *)_textBuffer;
+ char textChar;
+
+ do {
+ textChar = getTextChar(&textDataPtr, &bitPos);
+ *dest++ = textChar;
+ } while (textChar);
+}
+
+void Text::fnPointerText(uint32 pointedId, uint16 mouseX, uint16 mouseY) {
+
+ Compact *ptrComp = _skyCompact->fetchCpt(pointedId);
+ lowTextManager_t text = lowTextManager(ptrComp->cursorText, TEXT_MOUSE_WIDTH, L_CURSOR, 242, false);
+ Logic::_scriptVariables[CURSOR_ID] = text.compactNum;
+ if (Logic::_scriptVariables[MENU]) {
+ _mouseOfsY = TOP_LEFT_Y - 2;
+ if (mouseX < 150)
+ _mouseOfsX = TOP_LEFT_X + 24;
+ else
+ _mouseOfsX = TOP_LEFT_X - 8 - text.textWidth;
+ } else {
+ _mouseOfsY = TOP_LEFT_Y - 10;
+ if (mouseX < 150)
+ _mouseOfsX = TOP_LEFT_X + 13;
+ else
+ _mouseOfsX = TOP_LEFT_X - 8 - text.textWidth;
+ }
+ Compact *textCompact = _skyCompact->fetchCpt(text.compactNum);
+ logicCursor(textCompact, mouseX, mouseY);
+}
+
+void Text::logicCursor(Compact *textCompact, uint16 mouseX, uint16 mouseY) {
+
+ textCompact->xcood = (uint16)(mouseX + _mouseOfsX);
+ textCompact->ycood = (uint16)(mouseY + _mouseOfsY);
+ if (textCompact->ycood < TOP_LEFT_Y)
+ textCompact->ycood = TOP_LEFT_Y;
+}
+
+bool Text::getTextBit(uint8 **data, uint32 *bitPos) {
+
+ if (*bitPos) {
+ (*bitPos)--;
+ } else {
+ (*data)++;
+ *bitPos = 7;
+ }
+
+ return (bool)(((**data) >> (*bitPos)) & 1);
+}
+
+char Text::getTextChar(uint8 **data, uint32 *bitPos) {
+ int pos = 0;
+ while (1) {
+ if (getTextBit(data, bitPos))
+ pos = _huffTree[pos].rChild;
+ else
+ pos = _huffTree[pos].lChild;
+
+ if (_huffTree[pos].lChild == 0 && _huffTree[pos].rChild == 0) {
+ return _huffTree[pos].value;
+ }
+ }
+}
+
+displayText_t Text::displayText(uint32 textNum, uint8 *dest, bool centre, uint16 pixelWidth, uint8 color) {
+ //Render text into buffer *dest
+ getText(textNum);
+ return displayText(_textBuffer, dest, centre, pixelWidth, color);
+}
+
+displayText_t Text::displayText(char *textPtr, uint8 *dest, bool centre, uint16 pixelWidth, uint8 color) {
+
+ //Render text pointed to by *textPtr in buffer *dest
+ uint32 centerTable[10];
+ uint16 lineWidth = 0;
+
+ uint32 numLines = 0;
+ _numLetters = 2;
+
+ // work around bug #778105 (line width exceeded)
+ char *tmpPtr = strstr(textPtr, "MUND-BEATMUNG!");
+ if (tmpPtr)
+ strcpy(tmpPtr, "MUND BEATMUNG!");
+
+ // work around bug #1151924 (line width exceeded when talking to gardener using spanish text)
+ // This text apparently only is broken in the floppy versions, the CD versions contain
+ // the correct string "MANIFESTACION - ARTISTICA.", which doesn't break the algorithm/game.
+ tmpPtr = strstr(textPtr, "MANIFESTACION-ARTISTICA.");
+ if (tmpPtr)
+ strcpy(tmpPtr, "MANIFESTACION ARTISTICA.");
+
+ char *curPos = textPtr;
+ char *lastSpace = textPtr;
+ uint8 textChar = (uint8)*curPos++;
+
+ while (textChar >= 0x20) {
+ if ((_curCharSet == 1) && (textChar >= 0x80))
+ textChar = 0x20;
+
+ textChar -= 0x20;
+ if (textChar == 0) {
+ lastSpace = curPos; //keep track of last space
+ centerTable[numLines] = lineWidth;
+ }
+
+ lineWidth += _characterSet[textChar]; //add character width
+ lineWidth += (uint16)_dtCharSpacing; //include character spacing
+
+ if (pixelWidth <= lineWidth) {
+
+ if (*(lastSpace-1) == 10)
+ error("line width exceeded!");
+
+ *(lastSpace-1) = 10;
+ lineWidth = 0;
+ numLines++;
+ curPos = lastSpace; //go back for new count
+ }
+
+ textChar = (uint8)*curPos++;
+ _numLetters++;
+ }
+
+ uint32 dtLastWidth = lineWidth; //save width of last line
+ centerTable[numLines] = lineWidth; //and update centering table
+ numLines++;
+
+ if (numLines > MAX_NO_LINES)
+ error("Maximum no. of lines exceeded!");
+
+ uint32 dtLineSize = pixelWidth * _charHeight;
+ uint32 numBytes = (dtLineSize * numLines) + sizeof(struct dataFileHeader) + 4;
+
+ if (!dest)
+ dest = (uint8*)malloc(numBytes);
+
+ // clear text sprite buffer
+ memset(dest + sizeof(struct dataFileHeader), 0, numBytes - sizeof(struct dataFileHeader));
+
+ //make the header
+ ((struct dataFileHeader *)dest)->s_width = pixelWidth;
+ ((struct dataFileHeader *)dest)->s_height = (uint16)(_charHeight * numLines);
+ ((struct dataFileHeader *)dest)->s_sp_size = (uint16)(pixelWidth * _charHeight * numLines);
+ ((struct dataFileHeader *)dest)->s_offset_x = 0;
+ ((struct dataFileHeader *)dest)->s_offset_y = 0;
+
+ //reset position
+ curPos = textPtr;
+
+ uint8 *curDest = dest + sizeof(struct dataFileHeader); //point to where pixels start
+ byte *prevDest = curDest;
+ uint32 *centerTblPtr = centerTable;
+
+ do {
+ if (centre) {
+ uint32 width = (pixelWidth - *centerTblPtr) >> 1;
+ centerTblPtr++;
+ curDest += width;
+ }
+
+ textChar = (uint8)*curPos++;
+ while (textChar >= 0x20) {
+ makeGameCharacter(textChar - 0x20, _characterSet, curDest, color, pixelWidth);
+ textChar = *curPos++;
+ }
+
+ prevDest = curDest = prevDest + dtLineSize; //start of last line + start of next
+
+ } while (textChar >= 10);
+
+ struct displayText_t ret;
+ ret.textData = dest;
+ ret.textWidth = dtLastWidth;
+ return ret;
+}
+
+void Text::makeGameCharacter(uint8 textChar, uint8 *charSetPtr, uint8 *&dest, uint8 color, uint16 bufPitch) {
+
+ bool maskBit, dataBit;
+ uint8 charWidth = (uint8)((*(charSetPtr + textChar)) + 1 - _dtCharSpacing);
+ uint16 data, mask;
+ byte *charSpritePtr = charSetPtr + (CHAR_SET_HEADER + ((_charHeight << 2) * textChar));
+ byte *startPos = dest;
+ byte *curPos = startPos;
+
+ for (int i = 0; i < _charHeight; i++) {
+
+ byte *prevPos = curPos;
+
+ data = READ_BE_UINT16(charSpritePtr);
+ mask = READ_BE_UINT16(charSpritePtr + 2);
+ charSpritePtr += 4;
+
+ for (int j = 0; j < charWidth; j++) {
+
+ maskBit = (mask & 0x8000) != 0; //check mask
+ mask <<= 1;
+ dataBit = (data & 0x8000) != 0; //check data
+ data <<= 1;
+
+ if (maskBit)
+ if (dataBit)
+ *curPos = color;
+ else
+ *curPos = 240; //black edge
+
+ curPos++;
+ }
+ //advance a line
+ curPos = prevPos + bufPitch;
+ }
+ //update position
+ dest = startPos + charWidth + _dtCharSpacing * 2 - 1;
+}
+
+lowTextManager_t Text::lowTextManager(uint32 textNum, uint16 width, uint16 logicNum, uint8 color, bool centre) {
+
+ getText(textNum);
+ struct displayText_t textInfo = displayText(_textBuffer, NULL, centre, width, color);
+
+ uint32 compactNum = FIRST_TEXT_COMPACT;
+ Compact *cpt = _skyCompact->fetchCpt(compactNum);
+ while (cpt->status != 0) {
+ compactNum++;
+ cpt = _skyCompact->fetchCpt(compactNum);
+ }
+
+ cpt->flag = (uint16)(compactNum - FIRST_TEXT_COMPACT) + FIRST_TEXT_BUFFER;
+
+ if (SkyEngine::_itemList[cpt->flag])
+ free(SkyEngine::_itemList[cpt->flag]);
+
+ SkyEngine::_itemList[cpt->flag] = textInfo.textData;
+
+ cpt->logic = logicNum;
+ cpt->status = ST_LOGIC | ST_FOREGROUND | ST_RECREATE;
+ cpt->screen = (uint16) Logic::_scriptVariables[SCREEN];
+
+ struct lowTextManager_t ret;
+ ret.textData = textInfo.textData;
+ ret.textWidth = textInfo.textWidth;
+ ret.compactNum = (uint16)compactNum;
+
+ return ret;
+}
+
+void Text::changeTextSpriteColour(uint8 *sprData, uint8 newCol) {
+
+ dataFileHeader *header = (dataFileHeader *)sprData;
+ sprData += sizeof(dataFileHeader);
+ for (uint16 cnt = 0; cnt < header->s_sp_size; cnt++)
+ if (sprData[cnt] >= 241)
+ sprData[cnt] = newCol;
+}
+
+uint32 Text::giveCurrentCharSet(void) {
+ return _curCharSet;
+}
+
+void Text::initHuffTree() {
+ switch (SkyEngine::_systemVars.gameVersion) {
+ case 109:
+ _huffTree = _huffTree_00109;
+ break;
+ case 267:
+ _huffTree = _huffTree_00267;
+ break;
+ case 288:
+ _huffTree = _huffTree_00288;
+ break;
+ case 303:
+ _huffTree = _huffTree_00303;
+ break;
+ case 331:
+ _huffTree = _huffTree_00331;
+ break;
+ case 348:
+ _huffTree = _huffTree_00348;
+ break;
+ case 365:
+ _huffTree = _huffTree_00365;
+ break;
+ case 368:
+ _huffTree = _huffTree_00368;
+ break;
+ case 372:
+ _huffTree = _huffTree_00372;
+ break;
+ default:
+ error("Unknown game version %d", SkyEngine::_systemVars.gameVersion);
+ }
+}
+
+bool Text::patchMessage(uint32 textNum) {
+
+ uint16 patchIdx = _patchLangIdx[SkyEngine::_systemVars.language];
+ uint16 patchNum = _patchLangNum[SkyEngine::_systemVars.language];
+ for (uint16 cnt = 0; cnt < patchNum; cnt++) {
+ if (_patchedMessages[cnt + patchIdx].textNr == textNum) {
+ strcpy(_textBuffer, _patchedMessages[cnt + patchIdx].text);
+ return true;
+ }
+ }
+ return false;
+}
+
+const PatchMessage Text::_patchedMessages[NUM_PATCH_MSG] = {
+ { 28724, "Testo e Parlato" }, // - italian
+ { 28707, "Solo Testo" },
+ { 28693, "Solo Parlato" },
+ { 28724, "Text och tal" }, // - swedish
+ { 28707, "Endast text" },
+ { 28693, "Endast tal" },
+ { 28686, "Musikvolym" },
+ { 4336, "Wir befinden uns EINHUNDERTZWANZIG METER #ber dem ERBODEN!" }, // - german
+};
+
+const uint16 Text::_patchLangIdx[8] = {
+ 0xFFFF, // SKY_ENGLISH
+ 7, // SKY_GERMAN
+ 0xFFFF, // SKY_FRENCH
+ 0xFFFF, // SKY_USA
+ 3, // SKY_SWEDISH
+ 0, // SKY_ITALIAN
+ 0xFFFF, // SKY_PORTUGUESE
+ 0xFFFF // SKY_SPANISH
+};
+
+const uint16 Text::_patchLangNum[8] = {
+ 0, // SKY_ENGLISH
+ 1, // SKY_GERMAN
+ 0, // SKY_FRENCH
+ 0, // SKY_USA
+ 4, // SKY_SWEDISH
+ 3, // SKY_ITALIAN
+ 0, // SKY_PORTUGUESE
+ 0 // SKY_SPANISH
+};
+
+} // End of namespace Sky