Move actors to the first frame on entering a room.
[scummvm-innocent.git] / engines / sword1 / text.cpp
blob0c78798a49983201684f6c219abcf8b126b9e395
1 /* ScummVM - Graphic Adventure Engine
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * $URL$
22 * $Id$
27 #include "common/endian.h"
28 #include "common/util.h"
30 #include "sword1/text.h"
31 #include "sword1/resman.h"
32 #include "sword1/objectman.h"
33 #include "sword1/swordres.h"
34 #include "sword1/sworddefs.h"
35 #include "sword1/screen.h"
36 #include "sword1/sword1.h"
38 namespace Sword1 {
40 #define OVERLAP 3
41 #define SPACE ' '
42 #define MAX_LINES 30
45 Text::Text(ObjectMan *pObjMan, ResMan *pResMan, bool czechVersion) {
46 _objMan = pObjMan;
47 _resMan = pResMan;
48 _textCount = 0;
49 _fontId = (czechVersion) ? CZECH_GAME_FONT : GAME_FONT;
50 _font = (uint8*)_resMan->openFetchRes(_fontId);
52 _joinWidth = charWidth( SPACE ) - 2 * OVERLAP;
53 _charHeight = _resMan->getUint16(_resMan->fetchFrame(_font, 0)->height); // all chars have the same height
54 for (int i = 0; i < MAX_TEXT_OBS; i++)
55 _textBlocks[i] = NULL;
58 Text::~Text(void) {
59 for (int i = 0; i < MAX_TEXT_OBS; i++)
60 free(_textBlocks[i]);
61 //_resMan->resClose(_fontId); => wiped automatically by _resMan->flush();
64 uint32 Text::lowTextManager(uint8 *ascii, int32 width, uint8 pen) {
65 _textCount++;
66 if (_textCount > MAX_TEXT_OBS)
67 error("Text::lowTextManager: MAX_TEXT_OBS exceeded");
68 uint32 textObjId = (TEXT_sect * ITM_PER_SEC) - 1;
69 do {
70 textObjId++;
71 } while (_objMan->fetchObject(textObjId)->o_status);
72 // okay, found a free text object
74 _objMan->fetchObject(textObjId)->o_status = STAT_FORE;
75 makeTextSprite((uint8)textObjId, ascii, (uint16)width, pen);
77 return textObjId;
80 void Text::makeTextSprite(uint8 slot, uint8 *text, uint16 maxWidth, uint8 pen) {
81 LineInfo lines[MAX_LINES];
82 uint16 numLines = analyzeSentence(text, maxWidth, lines);
84 uint16 sprWidth = 0;
85 uint16 lineCnt;
86 for (lineCnt = 0; lineCnt < numLines; lineCnt++)
87 if (lines[lineCnt].width > sprWidth)
88 sprWidth = lines[lineCnt].width;
90 uint16 sprHeight = _charHeight * numLines;
91 uint32 sprSize = sprWidth * sprHeight;
92 assert(!_textBlocks[slot]); // if this triggers, the speechDriver failed to call Text::releaseText.
93 _textBlocks[slot] = (FrameHeader*)malloc(sprSize + sizeof(FrameHeader));
95 memcpy(_textBlocks[slot]->runTimeComp, "Nu ", 4);
96 _textBlocks[slot]->compSize = 0;
97 _textBlocks[slot]->width = _resMan->toUint16(sprWidth);
98 _textBlocks[slot]->height = _resMan->toUint16(sprHeight);
99 _textBlocks[slot]->offsetX = 0;
100 _textBlocks[slot]->offsetY = 0;
102 uint8 *linePtr = ((uint8*)_textBlocks[slot]) + sizeof(FrameHeader);
103 memset(linePtr, NO_COL, sprSize);
104 for (lineCnt = 0; lineCnt < numLines; lineCnt++) {
105 uint8 *sprPtr = linePtr + (sprWidth - lines[lineCnt].width) / 2; // center the text
106 for (uint16 pos = 0; pos < lines[lineCnt].length; pos++)
107 sprPtr += copyChar(*text++, sprPtr, sprWidth, pen) - OVERLAP;
108 text++; // skip space at the end of the line
109 if(SwordEngine::isPsx()) //Chars are half height in psx version
110 linePtr += (_charHeight / 2) * sprWidth;
111 else
112 linePtr += _charHeight * sprWidth;
116 uint16 Text::charWidth(uint8 ch) {
117 if (ch < SPACE)
118 ch = 64;
119 return _resMan->getUint16(_resMan->fetchFrame(_font, ch - SPACE)->width);
122 uint16 Text::analyzeSentence(uint8 *text, uint16 maxWidth, LineInfo *line) {
123 uint16 lineNo = 0;
125 bool firstWord = true;
126 while (*text) {
127 uint16 wordWidth = 0;
128 uint16 wordLength = 0;
130 while ((*text != SPACE) && *text) {
131 wordWidth += charWidth(*text) - OVERLAP;
132 wordLength++;
133 text++;
135 if (*text == SPACE)
136 text++;
138 wordWidth += OVERLAP; // no overlap on final letter of word!
139 if ( firstWord ) { // first word on first line, so no separating SPACE needed
140 line[0].width = wordWidth;
141 line[0].length = wordLength;
142 firstWord = false;
143 } else {
144 // see how much extra space this word will need to fit on current line
145 // (with a separating space character - also overlapped)
146 uint16 spaceNeeded = _joinWidth + wordWidth;
148 if (line[lineNo].width + spaceNeeded <= maxWidth ) {
149 line[lineNo].width += spaceNeeded;
150 line[lineNo].length += 1 + wordLength; // NB. space+word characters
151 } else { // put word (without separating SPACE) at start of next line
152 lineNo++;
153 assert( lineNo < MAX_LINES );
154 line[lineNo].width = wordWidth;
155 line[lineNo].length = wordLength;
159 return lineNo+1; // return no of lines
162 uint16 Text::copyChar(uint8 ch, uint8 *sprPtr, uint16 sprWidth, uint8 pen) {
163 FrameHeader *chFrame = _resMan->fetchFrame(_font, ch - SPACE);
164 uint8 *chData = ((uint8*)chFrame) + sizeof(FrameHeader);
165 uint8 *dest = sprPtr;
166 uint8 *decBuf = NULL;
167 uint8 *decChr;
168 uint16 frameHeight = 0;
170 if(SwordEngine::isPsx()) {
171 frameHeight = _resMan->getUint16(chFrame->height)/2;
172 if(_fontId == CZECH_GAME_FONT) { //Czech game fonts are compressed
173 decBuf = (uint8*) malloc((_resMan->getUint16(chFrame->width))*(_resMan->getUint16(chFrame->height)/2));
174 Screen::decompressHIF(chData, decBuf);
175 decChr = decBuf;
176 } else //Normal game fonts are not compressed
177 decChr = chData;
178 } else {
179 frameHeight = _resMan->getUint16(chFrame->height);
180 decChr = chData;
183 for (uint16 cnty = 0; cnty < frameHeight; cnty++) {
184 for (uint16 cntx = 0; cntx < _resMan->getUint16(chFrame->width); cntx++) {
185 if (*decChr == LETTER_COL)
186 dest[cntx] = pen;
187 else if (((*decChr == BORDER_COL) || (*decChr == BORDER_COL_PSX)) && (!dest[cntx])) // don't do a border if there's already a color underneath (chars can overlap)
188 dest[cntx] = BORDER_COL;
189 decChr++;
191 dest += sprWidth;
193 free(decBuf);
194 return _resMan->getUint16(chFrame->width);
197 FrameHeader *Text::giveSpriteData(uint32 textTarget) {
198 // textTarget is the resource ID of the Compact linking the textdata.
199 // that's 0x950000 for slot 0 and 0x950001 for slot 1. easy, huh? :)
200 textTarget &= ITM_ID;
201 assert(textTarget < MAX_TEXT_OBS);
203 return _textBlocks[textTarget];
206 void Text::releaseText(uint32 id, bool updateCount) {
207 id &= ITM_ID;
208 assert(id < MAX_TEXT_OBS);
209 if (_textBlocks[id]) {
210 free(_textBlocks[id]);
211 _textBlocks[id] = NULL;
212 if (updateCount)
213 _textCount--;
217 } // End of namespace Sword1