Ticket #969: text.cpp

File text.cpp, 11.9 KB (added by SF/dirtyhairy, 21 years ago)

modified text.cpp

Line 
1/* ScummVM - Scumm Interpreter
2 * Copyright (C) 2003 The ScummVM project
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * $Header: /cvsroot/scummvm/scummvm/sky/text.cpp,v 1.43 2003/07/07 02:54:59 lavosspawn Exp $
19 *
20 */
21
22#include "stdafx.h"
23#include "common/scummsys.h"
24#include "sky/skydefs.h"
25#include "sky/sky.h"
26#include "sky/text.h"
27#include "sky/logic.h"
28
29#define FIRST_TEXT_SEC 77
30#define FIRST_TEXT_BUFFER 274
31#define NO_OF_TEXT_SECTIONS 8 // 8 sections per language
32#define CHAR_SET_FILE 60150
33#define MAX_SPEECH_SECTION 7
34#define CHAR_SET_HEADER 128
35#define MAX_NO_LINES 10
36
37SkyText::SkyText(SkyDisk *skyDisk) {
38 _skyDisk = skyDisk;
39
40 initHuffTree();
41
42 _mainCharacterSet.addr = _skyDisk->loadFile(CHAR_SET_FILE, NULL);
43 _mainCharacterSet.charHeight = MAIN_CHAR_HEIGHT;
44 _mainCharacterSet.charSpacing = 0;
45
46 fnSetFont(0);
47
48 if (!SkyState::isDemo()) {
49 _controlCharacterSet.addr = _skyDisk->loadFile(60520, NULL);
50 _controlCharacterSet.charHeight = 12;
51 _controlCharacterSet.charSpacing = 1;
52
53 _linkCharacterSet.addr = _skyDisk->loadFile(60521, NULL);
54 _linkCharacterSet.charHeight = 12;
55 _linkCharacterSet.charSpacing = 0;
56 } else {
57 _controlCharacterSet.addr = NULL;
58 _linkCharacterSet.addr = NULL;
59 }
60
61 if (SkyState::isCDVersion()) {
62 _preAfterTableArea = _skyDisk->loadFile(60522, NULL);
63 } else _preAfterTableArea = NULL;
64}
65
66SkyText::~SkyText(void) {
67
68 if (_controlCharacterSet.addr) free(_controlCharacterSet.addr);
69 if (_linkCharacterSet.addr) free(_linkCharacterSet.addr);
70 if (_preAfterTableArea) free(_preAfterTableArea);
71}
72
73void SkyText::fnSetFont(uint32 fontNr) {
74
75 struct charSet *newCharSet;
76
77 switch (fontNr) {
78 case 0:
79 newCharSet = &_mainCharacterSet;
80 break;
81 case 1:
82 newCharSet = &_controlCharacterSet;
83 break;
84 case 2:
85 newCharSet = &_linkCharacterSet;
86 break;
87 default:
88 error("Tried to set invalid font (%d)", fontNr);
89 }
90
91 _curCharSet = fontNr;
92 _characterSet = newCharSet->addr;
93 _charHeight = (byte)newCharSet->charHeight;
94 _dtCharSpacing = newCharSet->charSpacing;
95}
96
97void SkyText::fnTextModule(uint32 textInfoId, uint32 textNo) {
98
99 fnSetFont(1);
100 uint16* msgData = (uint16 *)SkyState::fetchCompact(textInfoId);
101 lowTextManager_t textId = lowTextManager(textNo, msgData[1], msgData[2], 209, false);
102 SkyLogic::_scriptVariables[RESULT] = textId.compactNum;
103 Compact *textCompact = SkyState::fetchCompact(textId.compactNum);
104 textCompact->xcood = msgData[3];
105 textCompact->ycood = msgData[4];
106 fnSetFont(0);
107}
108
109void SkyText::getText(uint32 textNr) { //load text #"textNr" into textBuffer
110 uint32 sectionNo = (textNr & 0x0F000) >> 12;
111
112 if (SkyState::_itemList[FIRST_TEXT_SEC + sectionNo] == (void **)NULL) { //check if already loaded
113 debug(5, "Loading Text item(s) for Section %d", (sectionNo>>2));
114
115 uint32 fileNo = sectionNo + ((SkyState::_systemVars.language * NO_OF_TEXT_SECTIONS) + 60600);
116 SkyState::_itemList[FIRST_TEXT_SEC + sectionNo] = (void **)_skyDisk->loadFile((uint16)fileNo, NULL);
117 }
118 _textItemPtr = (uint8 *)SkyState::_itemList[FIRST_TEXT_SEC + sectionNo];
119
120 uint32 offset = 0;
121 uint32 nr32MsgBlocks = (textNr & 0x0fe0);
122 uint32 skipBytes;
123 byte *blockPtr;
124 bool bitSeven;
125
126 if (nr32MsgBlocks) {
127 blockPtr = (byte *)(_textItemPtr + 4);
128 nr32MsgBlocks >>= 5;
129 do {
130 offset += READ_LE_UINT16(blockPtr);
131 blockPtr += 2;
132 } while (--nr32MsgBlocks);
133 }
134
135 uint32 remItems = textNr;
136 textNr &= 0x1f;
137 if (textNr) {
138
139 remItems &= 0x0fe0;
140 remItems += READ_LE_UINT16(_textItemPtr);
141 blockPtr = _textItemPtr + remItems;
142
143 do {
144 skipBytes = *blockPtr++;
145 bitSeven = (bool)((skipBytes >> (7)) & 0x1);
146 skipBytes &= ~(1UL << 7);
147
148 if (bitSeven)
149 skipBytes <<= 3;
150
151 offset += skipBytes;
152
153 } while (--textNr);
154 }
155
156 uint32 numBits = offset;
157 offset >>= 2;
158 offset += READ_LE_UINT16(_textItemPtr + 2);
159 _textItemPtr += offset;
160
161 //bit pointer: 0->8, 1->6, 2->4 ...
162 numBits &= 3;
163 numBits ^= 3;
164 numBits++;
165 numBits <<= 1;
166
167 _inputValue = *_textItemPtr++;
168 char *dest = (char *)_textBuffer;
169 char textChar;
170 _shiftBits = (uint8) numBits;
171
172 do {
173 textChar = getTextChar();
174 *dest++ = textChar;
175 } while(textChar);
176}
177
178void SkyText::fnPointerText(uint32 pointedId, uint16 mouseX, uint16 mouseY) {
179
180 Compact *ptrComp = SkyState::fetchCompact(pointedId);
181 lowTextManager_t text = lowTextManager(ptrComp->cursorText, TEXT_MOUSE_WIDTH, L_CURSOR, 242, false);
182 SkyLogic::_scriptVariables[CURSOR_ID] = text.compactNum;
183 if (SkyLogic::_scriptVariables[MENU]) {
184 _mouseOfsY = TOP_LEFT_Y - 2;
185 if (mouseX < 150) _mouseOfsX = TOP_LEFT_X + 24;
186 else _mouseOfsX = TOP_LEFT_X - 8 - _lowTextWidth;
187 } else {
188 _mouseOfsY = TOP_LEFT_Y - 10;
189 if (mouseX < 150) _mouseOfsX = TOP_LEFT_X + 13;
190 else _mouseOfsX = TOP_LEFT_X - 8 - _lowTextWidth;
191 }
192 Compact *textCompact = SkyState::fetchCompact(text.compactNum);
193 logicCursor(textCompact, mouseX, mouseY);
194}
195
196void SkyText::logicCursor(Compact *textCompact, uint16 mouseX, uint16 mouseY) {
197
198 textCompact->xcood = (uint16)(mouseX + _mouseOfsX);
199 textCompact->ycood = (uint16)(mouseY + _mouseOfsY);
200 if (textCompact->ycood < TOP_LEFT_Y) textCompact->ycood = TOP_LEFT_Y;
201}
202
203bool SkyText::getTBit() {
204
205 if (_shiftBits) {
206 (_shiftBits)--;
207 } else {
208 _inputValue = *_textItemPtr++;
209 _shiftBits = 7;
210 }
211
212 return (bool)(((_inputValue) >> (_shiftBits)) & 1);
213}
214
215displayText_t SkyText::displayText(uint8 *dest, bool centre, uint16 pixelWidth, uint8 color) {
216 //Render text in _textBuffer in buffer *dest
217 return displayText(this->_textBuffer, dest, centre, pixelWidth, color);
218}
219
220displayText_t SkyText::displayText(char *textPtr, uint8 *dest, bool centre, uint16 pixelWidth, uint8 color) {
221
222 //Render text pointed to by *textPtr in buffer *dest
223
224 uint8 textChar;
225 char *curPos = textPtr;
226 char *lastSpace = curPos;
227 //hack
228 char dummy=0;
229 //hack
230 byte *centerTblPtr = _centreTable;
231 uint16 lineWidth = 0;
232
233 _dtCol = color;
234 _dtLineWidth = pixelWidth;
235 _dtLines = 0;
236 _dtLetters = 1;
237 _dtData = dest;
238 _dtText = textPtr;
239 _dtCentre = centre;
240
241 textChar = (uint8)*curPos++;
242 _dtLetters++;
243
244 while ( (textChar >= 0x20) & (dummy==0) ) {
245
246 textChar -= 0x20;
247 if (textChar == 0) {
248 lastSpace = curPos; //keep track of last space
249 *(uint32 *)centerTblPtr = TO_LE_32(lineWidth);
250 }
251
252 lineWidth += *(_characterSet+textChar); //add character width
253 lineWidth += (uint16)_dtCharSpacing; //include character spacing
254
255 if (pixelWidth <= lineWidth) {
256
257 if (*(lastSpace-1) == 10)
258 dummy=1; //error("line width exceeded!");
259
260 *(lastSpace-1) = 10;
261 lineWidth = 0;
262 _dtLines++;
263 centerTblPtr += 4; //get next space in centering table
264 curPos = lastSpace; //go back for new count
265 }
266
267 textChar = (uint8)*curPos++;
268 _dtLetters++;
269 }
270
271 _dtLastWidth = lineWidth; //save width of last line
272 *(uint32 *)centerTblPtr = TO_LE_32(lineWidth); //and update centering table
273 _dtLines++;
274
275 if (_dtLines > MAX_NO_LINES)
276 error("Maximum no. of lines exceeded!");
277
278 _dtLineSize = pixelWidth * _charHeight;
279 uint32 numBytes = (_dtLineSize * _dtLines) + sizeof(struct dataFileHeader) + 4;
280
281 if (_dtData == NULL)
282 _dtData = (byte *)malloc(numBytes);
283
284 uint8 *curDest = _dtData;
285
286 uint32 bytesToClear = numBytes; //no of bytes to clear
287 bytesToClear -= sizeof(struct dataFileHeader); //don't touch the header.
288 memset(curDest + sizeof(struct dataFileHeader), 0, bytesToClear);
289 curPos += bytesToClear;
290
291 //make the header
292 ((struct dataFileHeader *)curDest)->s_width = _dtLineWidth;
293 ((struct dataFileHeader *)curDest)->s_height = (uint16)(_charHeight * _dtLines);
294 ((struct dataFileHeader *)curDest)->s_sp_size = (uint16)(_dtLineWidth * _charHeight * _dtLines);
295 ((struct dataFileHeader *)curDest)->s_offset_x = 0;
296 ((struct dataFileHeader *)curDest)->s_offset_y = 0;
297
298 //reset position
299 curPos = textPtr;
300
301 curDest += sizeof(struct dataFileHeader); //point to where pixels start
302 byte *prevDest = curDest;
303 centerTblPtr = _centreTable;
304
305 do {
306 if (_dtCentre) {
307
308 uint32 width = _dtLineWidth;
309 width -= READ_LE_UINT32(centerTblPtr);
310 centerTblPtr += 4;
311 width >>=1;
312 curDest += width;
313 }
314
315 textChar = (uint8)*curPos++;
316 while (textChar >= 0x20) {
317 textChar -= 0x20;
318 makeGameCharacter(textChar, _characterSet, curDest, color);
319 textChar = *curPos++;
320 }
321
322 curDest = prevDest; //start of last line
323 curDest += _dtLineSize; //start of next
324 prevDest = curDest;
325
326 } while (textChar >= 10);
327
328 struct displayText_t ret;
329 ret.textData = _dtData;
330 ret.textWidth = _dtLastWidth;
331 return ret;
332}
333
334void SkyText::makeGameCharacter(uint8 textChar, uint8 *charSetPtr, uint8 *&dest, uint8 color) {
335
336 bool maskBit, dataBit;
337 uint8 charWidth = (uint8)((*(charSetPtr + textChar)) + 1 - _dtCharSpacing);
338 uint16 data, mask;
339 byte *charSpritePtr = charSetPtr + (CHAR_SET_HEADER + ((_charHeight << 2) * textChar));
340 byte *startPos = dest;
341 byte *curPos = startPos;
342
343 for (int i = 0; i < _charHeight; i++) {
344
345 byte *prevPos = curPos;
346
347 data = READ_BE_UINT16(charSpritePtr);
348 mask = READ_BE_UINT16(charSpritePtr + 2);
349 charSpritePtr += 4;
350
351 for (int j = 0; j < charWidth; j++) {
352
353 maskBit = (mask & 0x8000) != 0; //check mask
354 mask <<= 1;
355 dataBit = (data & 0x8000) != 0; //check data
356 data <<= 1;
357
358 if (maskBit)
359 if (dataBit)
360 *curPos = color;
361 else
362 //black edge
363 *curPos = 240;
364
365 curPos++;
366 }
367
368 //advance a line
369 curPos = prevPos;
370 curPos += _dtLineWidth;
371 }
372
373 //update position
374 dest = startPos + charWidth + _dtCharSpacing*2 - 1;
375
376}
377
378lowTextManager_t SkyText::lowTextManager(uint32 textNum, uint16 width, uint16 logicNum, uint8 color, bool centre) {
379
380 getText(textNum);
381
382 struct displayText_t textInfo = displayText(NULL, centre, width, color);
383
384 _lowTextWidth = textInfo.textWidth;
385 byte *textData = textInfo.textData;
386
387 uint32 compactNum = FIRST_TEXT_COMPACT;
388
389 Compact *cpt = SkyState::fetchCompact(compactNum);
390
391 while (cpt->status != 0) {
392 compactNum++;
393 cpt = SkyState::fetchCompact(compactNum);
394 }
395
396 cpt->flag = (uint16)(compactNum - FIRST_TEXT_COMPACT) + FIRST_TEXT_BUFFER;
397
398 byte *oldText = (byte *)SkyState::_itemList[cpt->flag];
399 SkyState::_itemList[cpt->flag] = (void **)textData;
400
401 if (oldText != NULL)
402 free (oldText);
403
404 cpt->logic = logicNum;
405 cpt->status = ST_LOGIC | ST_FOREGROUND | ST_RECREATE;
406 cpt->screen = (uint16) SkyLogic::_scriptVariables[SCREEN];
407
408 struct lowTextManager_t ret;
409 ret.textData = _dtData;
410 ret.compactNum = (uint16)compactNum;
411
412 return ret;
413}
414
415void SkyText::changeTextSpriteColour(uint8 *sprData, uint8 newCol) {
416
417 dataFileHeader *header = (dataFileHeader *)sprData;
418 sprData += sizeof(dataFileHeader);
419 for (uint16 cnt = 0; cnt < header->s_sp_size; cnt++)
420 if (sprData[cnt] >= 241) sprData[cnt] = newCol;
421}
422
423void SkyText::initHuffTree() {
424 switch (SkyState::_systemVars.gameVersion) {
425 case 267:
426 _huffTree = _huffTree_00267;
427 break;
428 case 288:
429 _huffTree = _huffTree_00288;
430 break;
431 case 303:
432 _huffTree = _huffTree_00303;
433 break;
434 case 331:
435 _huffTree = _huffTree_00331;
436 break;
437 case 348:
438 _huffTree = _huffTree_00348;
439 break;
440 case 365:
441 _huffTree = _huffTree_00365;
442 break;
443 case 368:
444 _huffTree = _huffTree_00368;
445 break;
446 case 372:
447 _huffTree = _huffTree_00372;
448 break;
449 default:
450 error("Unknown game version");
451 }
452}
453
454char SkyText::getTextChar() {
455 int pos = 0;
456 for (;;) {
457 if (getTBit() == 0)
458 pos = _huffTree[pos].lChild;
459 else
460 pos = _huffTree[pos].rChild;
461 if (_huffTree[pos].lChild == 0 && _huffTree[pos].rChild == 0) {
462 return _huffTree[pos].value;
463 }
464 }
465}
466