1 /* ScummVM - Graphic Adventure Engine
2 *
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.
6 *
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.
11 *
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.
16 *
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.
20 *
21 */
22
23 /*
24 * Based on the Reverse Engineering work of Christophe Fontanel,
25 * maintainer of the Dungeon Master Encyclopaedia (http://dmweb.free.fr/)
26 */
27
28 #include "common/system.h"
29
30 #include "dm/text.h"
31
32 namespace DM {
33
TextMan(DMEngine * vm)34 TextMan::TextMan(DMEngine *vm) : _vm(vm) {
35 _messageAreaCursorColumn = 0;
36 _messageAreaCursorRow = 0;
37 for (uint16 i = 0; i < 4; ++i)
38 _messageAreaRowExpirationTime[i] = 0;
39
40 _bitmapMessageAreaNewRow = new byte[320 * 7];
41 _isScrolling = false;
42 _startedScrollingAt = -1;
43 _messageAreaCopy = new byte[320 * 7 * 4];
44 }
45
~TextMan()46 TextMan::~TextMan() {
47 delete[] _bitmapMessageAreaNewRow;
48 delete[] _messageAreaCopy;
49 }
50
printTextToBitmap(byte * destBitmap,uint16 destByteWidth,int16 destX,int16 destY,Color textColor,Color bgColor,const char * text,uint16 destHeight)51 void TextMan::printTextToBitmap(byte *destBitmap, uint16 destByteWidth, int16 destX, int16 destY,
52 Color textColor, Color bgColor, const char *text, uint16 destHeight) {
53 if ((destX -= 1) < 0) // fixes missalignment, to be checked
54 destX = 0;
55 if ((destY -= 4) < 0) // fixes missalignment, to be checked
56 destY = 0;
57
58 uint16 destPixelWidth = destByteWidth * 2;
59
60 uint16 textLength = strlen(text);
61 uint16 nextX = destX;
62 uint16 nextY = destY;
63 byte *srcBitmap = _vm->_displayMan->getNativeBitmapOrGraphic(kDMGraphicIdxFont);
64
65 byte *tmp = _vm->_displayMan->_tmpBitmap;
66 for (uint16 i = 0; i < (kDMFontLetterWidth + 1) * kDMFontLetterHeight * 128; ++i)
67 tmp[i] = srcBitmap[i] ? textColor : bgColor;
68
69 srcBitmap = tmp;
70
71 for (const char *begin = text, *end = text + textLength; begin != end; ++begin) {
72 if (nextX + kDMFontLetterWidth + 1 >= destPixelWidth || (*begin == '\n')) {
73 nextX = destX;
74 nextY += kDMFontLetterHeight + 1;
75 }
76 if (nextY + kDMFontLetterHeight >= destHeight)
77 break;
78
79 uint16 srcX = (1 + 5) * *begin; // 1 + 5 is not the letter width, arbitrary choice of the unpacking code
80
81 Box box((nextX == destX) ? (nextX + 1) : nextX, nextX + kDMFontLetterWidth + 1, nextY, nextY + kDMFontLetterHeight - 1);
82 _vm->_displayMan->blitToBitmap(srcBitmap, destBitmap, box, (nextX == destX) ? (srcX + 1) : srcX, 0, 6 * 128 / 2, destByteWidth, kDMColorNoTransparency,
83 kDMFontLetterHeight, destHeight);
84
85 nextX += kDMFontLetterWidth + 1;
86 }
87 }
88
printToLogicalScreen(uint16 destX,uint16 destY,Color textColor,Color bgColor,const char * text)89 void TextMan::printToLogicalScreen(uint16 destX, uint16 destY, Color textColor, Color bgColor, const char *text) {
90 printTextToBitmap(_vm->_displayMan->_bitmapScreen, _vm->_displayMan->_screenWidth / 2, destX, destY, textColor, bgColor, text, _vm->_displayMan->_screenHeight);
91 }
92
printToViewport(int16 posX,int16 posY,Color textColor,const char * text,Color bgColor)93 void TextMan::printToViewport(int16 posX, int16 posY, Color textColor, const char *text, Color bgColor) {
94 printTextToBitmap(_vm->_displayMan->_bitmapViewport, k112_byteWidthViewport, posX, posY, textColor, bgColor, text, k136_heightViewport);
95 }
96
printWithTrailingSpaces(byte * destBitmap,int16 destByteWidth,int16 destX,int16 destY,Color textColor,Color bgColor,const char * text,int16 requiredTextLength,int16 destHeight)97 void TextMan::printWithTrailingSpaces(byte *destBitmap, int16 destByteWidth, int16 destX, int16 destY, Color textColor,
98 Color bgColor, const char *text, int16 requiredTextLength, int16 destHeight) {
99 Common::String str = text;
100 for (int16 i = str.size(); i < requiredTextLength; ++i)
101 str += ' ';
102 printTextToBitmap(destBitmap, destByteWidth, destX, destY, textColor, bgColor, str.c_str(), destHeight);
103 }
104
printLineFeed()105 void TextMan::printLineFeed() {
106 printMessage(kDMColorBlack, "\n");
107 }
108
printMessage(Color color,const char * string,bool printWithScroll)109 void TextMan::printMessage(Color color, const char *string, bool printWithScroll) {
110 uint16 characterIndex;
111 Common::String wrkString;
112
113 while (*string) {
114 if (*string == '\n') { /* New line */
115 string++;
116 if ((_messageAreaCursorColumn != 0) || (_messageAreaCursorRow != 0)) {
117 _messageAreaCursorColumn = 0;
118 createNewRow();
119 }
120 } else if (*string == ' ') {
121 string++;
122 if (_messageAreaCursorColumn != 53) {
123 printString(color, " "); // I'm not sure if this is like the original
124 }
125 } else {
126 characterIndex = 0;
127 do {
128 wrkString += *string++;
129 characterIndex++;
130 } while (*string && (*string != ' ') && (*string != '\n')); /* End of string, space or New line */
131 wrkString += '\0';
132 if (_messageAreaCursorColumn + characterIndex > 53) {
133 _messageAreaCursorColumn = 2;
134 createNewRow();
135 }
136 printString(color, wrkString.c_str());
137 }
138 }
139 }
140
createNewRow()141 void TextMan::createNewRow() {
142 if (_messageAreaCursorRow == 3) {
143 isTextScrolling(&_textScroller, true);
144 memset(_bitmapMessageAreaNewRow, kDMColorBlack, 320 * 7);
145 _isScrolling = true;
146 setScrollerCommand(&_textScroller, 1);
147
148 for (uint16 rowIndex = 0; rowIndex < 3; rowIndex++)
149 _messageAreaRowExpirationTime[rowIndex] = _messageAreaRowExpirationTime[rowIndex + 1];
150
151 _messageAreaRowExpirationTime[3] = -1;
152 } else
153 _messageAreaCursorRow++;
154 }
155
printString(Color color,const char * string)156 void TextMan::printString(Color color, const char* string) {
157 int16 stringLength = strlen(string);
158 if (isTextScrolling(&_textScroller, false))
159 printToLogicalScreen(_messageAreaCursorColumn * 6, (_messageAreaCursorRow * 7 - 1) + 177, color, kDMColorBlack, string);
160 else {
161 printTextToBitmap(_bitmapMessageAreaNewRow, k160_byteWidthScreen, _messageAreaCursorColumn * 6, 0, color, kDMColorBlack, string, 7);
162 _isScrolling = true;
163 if (isTextScrolling(&_textScroller, false))
164 setScrollerCommand(&_textScroller, 1);
165 }
166 _messageAreaCursorColumn += stringLength;
167 _messageAreaRowExpirationTime[_messageAreaCursorRow] = _vm->_gameTime + 200;
168
169 }
170
initialize()171 void TextMan::initialize() {
172 moveCursor(0, 3);
173 for (uint16 i = 0; i < 4; ++i)
174 _messageAreaRowExpirationTime[i] = -1;
175 }
176
moveCursor(int16 column,int16 row)177 void TextMan::moveCursor(int16 column, int16 row) {
178 if (column < 0)
179 column = 0;
180 else if (column >= 53)
181 column = 52;
182
183 _messageAreaCursorColumn = column;
184 if (row < 0)
185 row = 0;
186 else if (row >= 4)
187 row = 3;
188
189 _messageAreaCursorRow = row;
190 }
191
clearExpiredRows()192 void TextMan::clearExpiredRows() {
193 _vm->_displayMan->_useByteBoxCoordinates = false;
194 Box displayBox;
195 displayBox._rect.left = 0;
196 displayBox._rect.right = 319;
197 for (uint16 rowIndex = 0; rowIndex < 4; rowIndex++) {
198 int32 expirationTime = _messageAreaRowExpirationTime[rowIndex];
199 if ((expirationTime == -1) || (expirationTime > _vm->_gameTime) || _isScrolling)
200 continue;
201 displayBox._rect.top = 172 + (rowIndex * 7);
202 displayBox._rect.bottom = displayBox._rect.top + 6;
203 isTextScrolling(&_textScroller, true);
204 _vm->_displayMan->fillBoxBitmap(_vm->_displayMan->_bitmapScreen, displayBox, kDMColorBlack, k160_byteWidthScreen, k200_heightScreen);
205 _messageAreaRowExpirationTime[rowIndex] = -1;
206 }
207 }
208
printEndGameString(int16 x,int16 y,Color textColor,const char * text)209 void TextMan::printEndGameString(int16 x, int16 y, Color textColor, const char* text) {
210 char modifiedString[50];
211
212 char *wrkStringPtr = modifiedString;
213 *wrkStringPtr = *text++;
214 while (*wrkStringPtr) {
215 if ((*wrkStringPtr >= 'A') && (*wrkStringPtr <= 'Z'))
216 *wrkStringPtr -= 64; /* Use the same font as the one used for scrolls */
217
218 wrkStringPtr++;
219 *wrkStringPtr = *text++;
220 }
221 printToLogicalScreen(x, y, textColor, kDMColorDarkestGray, modifiedString);
222 }
223
clearAllRows()224 void TextMan::clearAllRows() {
225 isTextScrolling(&_textScroller, true);
226
227 Box tmpBox(0, 319, 169, 199);
228 _vm->_displayMan->fillScreenBox(tmpBox, kDMColorBlack);
229
230 _messageAreaCursorRow = 3;
231 _messageAreaCursorColumn = 0;
232 for (int16 rowIndex = 0; rowIndex < 4; rowIndex++)
233 _messageAreaRowExpirationTime[rowIndex] = -1;
234 }
235
updateMessageArea()236 void TextMan::updateMessageArea() {
237 if (_isScrolling) {
238 if (_startedScrollingAt == -1) {
239 _startedScrollingAt = g_system->getMillis();
240 memcpy(_messageAreaCopy, _vm->_displayMan->_bitmapScreen + (200 - 7 * 4) * 320, 320 * 7 * 4);
241 }
242
243 int linesToCopy = (g_system->getMillis() - _startedScrollingAt) / 50;
244 if (linesToCopy >= 7) {
245 linesToCopy = 7;
246 _startedScrollingAt = -1;
247 _isScrolling = false;
248 }
249 memcpy(_vm->_displayMan->_bitmapScreen + (200 - 7 * 4) * 320, _messageAreaCopy + linesToCopy * 320,
250 320 * (7 * 4 - linesToCopy));
251 memcpy(_vm->_displayMan->_bitmapScreen + (200 - linesToCopy) * 320, _bitmapMessageAreaNewRow, 320 * linesToCopy);
252 }
253 }
254
255 }
256