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