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 #include "common/endian.h"
24 #include "common/file.h"
25 #include "common/util.h"
26 
27 #include "cruise/cruise_main.h"
28 #include "cruise/mouse.h"
29 #include "cruise/staticres.h"
30 
31 namespace Cruise {
32 
33 const int SPACE_WIDTH = 5;
34 
35 /**
36  * Determines the line size by finding the highest character in the given font set
37  */
getLineHeight(int16 charCount,const FontEntry * fontPtr)38 int32 getLineHeight(int16 charCount, const FontEntry *fontPtr) {
39 	int32 highestChar = 0;
40 
41 	if (!charCount)
42 		return (0);
43 
44 	for (int i = 0; i < charCount; ++i) {
45 		int charHeight = fontPtr[i].charHeight;
46 		if (charHeight > highestChar) highestChar = charHeight;
47 	}
48 
49 	return highestChar;
50 }
51 
52 /**
53  * This function determines how many lines the text will have
54  */
getTextLineCount(int32 rightBorder_X,int16 wordSpacingWidth,const FontEntry * fontData,const char * textString)55 int32 getTextLineCount(int32 rightBorder_X, int16 wordSpacingWidth,
56 					   const FontEntry *fontData, const char *textString) {
57 	const char *localString = textString;
58 	const char *tempPtr = textString;
59 	uint8 ch;
60 	int32 total = 0, lineLength = 0;
61 
62 	if (rightBorder_X == 0)
63 		error("getTextLineCount() - invalid parameter");
64 
65 	if (!*textString)
66 		return (0);
67 
68 	ch = *localString;
69 
70 	do {
71 		int32 charData = fontCharacterTable[ch];
72 
73 		if (ch == '|') {
74 			lineLength = rightBorder_X;
75 			localString = tempPtr;
76 		} else if (charData >= 0) {
77 			lineLength += wordSpacingWidth + (int16)fontData[charData].charWidth;
78 		} else if (ch == ' ') {
79 			lineLength += wordSpacingWidth + SPACE_WIDTH;
80 			localString = tempPtr;
81 		}
82 
83 		if (lineLength >= rightBorder_X) {
84 			total += rightBorder_X;
85 			tempPtr = localString;
86 			lineLength = 0;
87 		}
88 
89 		ch = *++tempPtr;
90 	} while (ch);
91 
92 	if (lineLength > 0)
93 		total += rightBorder_X;
94 
95 
96 	return (total / rightBorder_X);
97 }
98 
loadFNT(const char * fileName)99 void loadFNT(const char *fileName) {
100 	uint8 header[4];
101 
102 	_systemFNT = NULL;
103 
104 	Common::File fontFileHandle;
105 
106 	if (!fontFileHandle.exists(fileName))
107 		return;
108 
109 	fontFileHandle.open((const char *)fileName);
110 
111 	fontFileHandle.read(header, 4);
112 
113 	if (strcmp((char *)header, "FNT") == 0) {
114 		uint32 fontSize = fontFileHandle.readUint32BE();
115 
116 		_systemFNT = (uint8 *)mallocAndZero(fontSize);
117 
118 		if (_systemFNT != NULL) {
119 			fontFileHandle.seek(4);
120 			fontFileHandle.read(_systemFNT, fontSize);
121 
122 			// Flip structure values from BE to LE for font files - this is for consistency
123 			// with font resources, which are in LE formatt
124 			FontInfo *f = (FontInfo *)_systemFNT;
125 			bigEndianLongToNative(&f->offset);
126 			bigEndianLongToNative(&f->size);
127 			flipGen(&f->numChars, 6);	// numChars, hSpacing, and vSpacing
128 
129 			FontEntry *fe = (FontEntry *)(_systemFNT + sizeof(FontInfo));
130 
131 			for (int i = 0; i < f->numChars; ++i, ++fe) {
132 				bigEndianLongToNative(&fe->offset);	// Flip 32-bit offset field
133 				flipGen(&fe->v1, 8);	// Flip remaining 16-bit fields
134 			}
135 		}
136 	}
137 
138 	fontFileHandle.close();
139 }
140 
initSystem()141 void initSystem() {
142 	int32 i;
143 
144 	itemColor = 15;
145 	titleColor = 9;
146 	selectColor = 13;
147 	subColor = 10;
148 
149 	for (i = 0; i < 64; i++) {
150 		strcpy(preloadData[i].name, "");
151 		preloadData[i].ptr = NULL;
152 		preloadData[i].nofree = 0;
153 	}
154 
155 	doFade = 0;
156 	fadeFlag = 0;
157 	scroll = 0;
158 	switchPal = 0;
159 	masterScreen = 0;
160 
161 	changeCursor(CURSOR_NOMOUSE);
162 	changeCursor(CURSOR_NORMAL);
163 	mouseOn();
164 
165 	strcpy(cmdLine, "");
166 
167 	loadFNT("system.fnt");
168 }
169 
freeSystem()170 void freeSystem() {
171 	MemFree(_systemFNT);
172 }
173 
bigEndianShortToNative(void * var)174 void bigEndianShortToNative(void *var) {
175 	WRITE_UINT16(var, READ_BE_UINT16(var));
176 }
177 
bigEndianLongToNative(void * var)178 void bigEndianLongToNative(void *var) {
179 	WRITE_UINT32(var, READ_BE_UINT32(var));
180 }
181 
flipGen(void * var,int32 length)182 void flipGen(void *var, int32 length) {
183 	short int *varPtr = (int16 *) var;
184 
185 	for (int i = 0; i < (length / 2); i++) {
186 		bigEndianShortToNative(&varPtr[i]);
187 	}
188 }
189 
renderWord(const uint8 * fontPtr_Data,uint8 * outBufferPtr,int xOffset,int yOffset,int32 height,int32 param4,int32 stringRenderBufferSize,int32 width,int32 charWidth)190 void renderWord(const uint8 *fontPtr_Data, uint8 *outBufferPtr, int xOffset, int yOffset,
191 				int32 height, int32 param4, int32 stringRenderBufferSize, int32 width, int32 charWidth) {
192 	const uint8 *fontPtr_Data2 = fontPtr_Data + height * 2;
193 	outBufferPtr += yOffset * width + xOffset;
194 
195 	for (int i = 0; i < height; i++) {	// y++
196 		uint16 bitSet1 = READ_BE_UINT16(fontPtr_Data);
197 		uint16 bitSet2 = READ_BE_UINT16(fontPtr_Data2);
198 
199 		fontPtr_Data += sizeof(uint16);
200 		fontPtr_Data2 += sizeof(uint16);
201 
202 		for (int j = 0; j < charWidth; j++) {
203 			if (((bitSet1 >> 15) & 1)) {
204 				*outBufferPtr = ((bitSet2 >> 15) & 1) + 1;
205 			}
206 			outBufferPtr++;
207 
208 			bitSet1 <<= 1;
209 			bitSet2 <<= 1;
210 		}
211 		outBufferPtr += width - charWidth;
212 	}
213 }
214 
215 // returns character count and pixel size (via pointer) per line of the string (old: prepareWordRender(int32 param, int32 var1, int16* out2, uint8* ptr3, uint8* string))
prepareWordRender(int32 inRightBorder_X,int16 wordSpacingWidth,int16 * strPixelLength,const FontEntry * fontData,const char * textString)216 int32 prepareWordRender(int32 inRightBorder_X, int16 wordSpacingWidth,
217 						int16 *strPixelLength, const FontEntry *fontData, const char *textString) {
218 	const char *localString = textString;
219 
220 	int32 counter = 0;
221 	int32 finish = 0;
222 	int32 temp_pc = 0;	// var_A                // temporary pixel count save
223 	int32 temp_cc = 0;	// var_C                // temporary char  count save
224 	int32 pixelCount = 0;	// si
225 
226 	do {
227 		uint8 character = *(localString++);
228 		int16 charData = fontCharacterTable[character];
229 
230 		if (character == ' ') {
231 			temp_cc = counter;
232 			temp_pc = pixelCount;
233 
234 			if (pixelCount + wordSpacingWidth + SPACE_WIDTH >=
235 			        inRightBorder_X) {
236 				finish = 1;
237 			} else {
238 				pixelCount += wordSpacingWidth + SPACE_WIDTH;
239 			}
240 		} else {
241 			if (character == '|' || !character) {
242 				finish = 1;
243 			} else {
244 				if (charData >= 0) {
245 					if (pixelCount + wordSpacingWidth +
246 							(int16)fontData[charData].charWidth >= inRightBorder_X) {
247 						finish = 1;
248 						if (temp_pc) {
249 							pixelCount = temp_pc;
250 							counter = temp_cc;
251 						}
252 					} else {
253 						pixelCount += wordSpacingWidth +
254 							(int16)fontData[charData].charWidth;
255 					}
256 				}
257 			}
258 		}
259 		counter++;
260 	} while (!finish);
261 
262 	*strPixelLength = (int16) pixelCount;
263 	return counter;
264 }
265 
drawString(int32 x,int32 y,const char * string,uint8 * buffer,uint8 fontColor,int32 rightBorder_X)266 void drawString(int32 x, int32 y, const char *string, uint8 *buffer, uint8 fontColor, int32 rightBorder_X) {
267 
268 	// Get the rendered text to display
269 	gfxEntryStruct *s = renderText(rightBorder_X, string);
270 
271 	// Draw the message
272 	drawMessage(s, x, y, rightBorder_X - x, fontColor, buffer);
273 
274 	// Free the data
275 	delete s->imagePtr;
276 	free(s);
277 }
278 
279 // calculates all necessary datas and renders text
renderText(int inRightBorder_X,const char * string)280 gfxEntryStruct *renderText(int inRightBorder_X, const char *string) {
281 	const FontInfo *fontPtr;
282 	const FontEntry *fontPtr_Desc;
283 	const uint8 *fontPtr_Data;
284 	int16 wordSpacingWidth;	// 0 or -1
285 	int16 wordSpacingHeight;// 0 or -1
286 	int32 rightBorder_X;
287 	int32 lineHeight;
288 	int32 numLines;
289 	int32 stringHeight;
290 	int32 stringFinished;
291 	int32 stringWidth;	// var_1C
292 	int32 stringRenderBufferSize;
293 	//  int32 useDynamicBuffer;
294 	uint8 *currentStrRenderBuffer;
295 	//  int32 var_8;                          // don't need that one
296 	int32 heightOffset;	// var_12     // how much pixel-lines have already been drawn
297 	//  int32 var_1E;
298 	gfxEntryStruct *generatedGfxEntry;
299 
300 	// check if string is empty
301 	if (!string) {
302 		return NULL;
303 	}
304 	// check if font has been loaded, else get system font
305 	if (fontFileIndex != -1) {
306 		fontPtr = (const FontInfo *)filesDatabase[fontFileIndex].subData.ptr;
307 
308 		if (!fontPtr) {
309 			fontPtr = (const FontInfo *)_systemFNT;
310 		}
311 	} else {
312 		fontPtr = (const FontInfo *)_systemFNT;
313 	}
314 
315 	if (!fontPtr) {
316 		return NULL;
317 	}
318 
319 	fontPtr_Desc = (const FontEntry *)((const uint8 *)fontPtr + sizeof(FontInfo));
320 	fontPtr_Data = (const uint8 *)fontPtr + fontPtr->offset;
321 
322 	lineHeight = getLineHeight(fontPtr->numChars, fontPtr_Desc);
323 
324 	wordSpacingWidth = fontPtr->hSpacing;
325 	wordSpacingHeight = fontPtr->vSpacing;
326 
327 	// if right border is higher then screenwidth (+ spacing), adjust border
328 	if (inRightBorder_X > 310) {
329 		rightBorder_X = 310;
330 	} else {
331 		rightBorder_X = inRightBorder_X;
332 	}
333 	numLines = getTextLineCount(rightBorder_X, wordSpacingWidth, fontPtr_Desc, string);	// ok
334 
335 	if (!numLines) {
336 		return NULL;
337 	}
338 
339 	stringHeight = ((wordSpacingHeight + lineHeight + 2) * numLines) + 1;
340 	stringFinished = 0;
341 	stringWidth = rightBorder_X + 2;	// max render width to the right
342 	stringRenderBufferSize = stringWidth * stringHeight * 4;
343 	inRightBorder_X = rightBorder_X;
344 
345 	currentStrRenderBuffer =
346 	    (uint8 *) mallocAndZero(stringRenderBufferSize);
347 	resetBitmap(currentStrRenderBuffer, stringRenderBufferSize);
348 
349 	generatedGfxEntry = (gfxEntryStruct *) MemAlloc(sizeof(gfxEntryStruct));
350 	generatedGfxEntry->imagePtr = currentStrRenderBuffer;
351 	generatedGfxEntry->imageSize = stringRenderBufferSize / 2;
352 	generatedGfxEntry->fontIndex = fontFileIndex;
353 	generatedGfxEntry->height = stringHeight;
354 	generatedGfxEntry->width = stringWidth;	// maximum render width to the right
355 
356 	// var_8 = 0;
357 	heightOffset = 0;
358 
359 	do {
360 		int spacesCount = 0;	// si
361 		unsigned char character = *string;
362 		short int strPixelLength;	// var_16
363 		const char *ptrStringEnd;	// var_4     //ok
364 		int drawPosPixel_X;	// di
365 
366 		// find first letter in string, skip all spaces
367 		while (character == ' ') {
368 			spacesCount++;
369 			character = *(string + spacesCount);
370 		}
371 
372 		string += spacesCount;
373 
374 		// returns character count and pixel length (via pointer) per line of the text string
375 		ptrStringEnd = string + prepareWordRender(inRightBorder_X, wordSpacingWidth, &strPixelLength, fontPtr_Desc, string);	//ok
376 
377 		// determine how much space is left to the right and left (center text)
378 		if (inRightBorder_X > strPixelLength) {
379 			//var_8 = (inRightBorder_X - strPixelLength) / 2;
380 			drawPosPixel_X =
381 			    (inRightBorder_X - strPixelLength) / 2;
382 		} else {
383 			drawPosPixel_X = 0;
384 		}
385 		//drawPosPixel_X = var_8;
386 
387 		// draw textline, character wise
388 		do {
389 			character = *(string++);
390 
391 			short int charData = fontCharacterTable[character];	// get character position
392 
393 			if (character) {
394 				if (character == ' ' || character == 0x7C) {
395 					drawPosPixel_X += wordSpacingWidth + SPACE_WIDTH;	// if char = "space" adjust word starting postion (don't render space though);
396 				} else {
397 					if (charData >= 0) {
398 						const FontEntry &fe = fontPtr_Desc[charData];
399 
400 						// should ist be stringRenderBufferSize/2 for the second last param?
401 						renderWord((const uint8 *)fontPtr_Data + fe.offset,
402 						           currentStrRenderBuffer,
403 						           drawPosPixel_X,
404 						           fe.height2 - fe.charHeight +
405 						           lineHeight + heightOffset,
406 						           fe.charHeight,
407 							   fe.v1,
408 						           stringRenderBufferSize,
409 						           stringWidth,
410 								   (int16)fe.charWidth);
411 
412 						drawPosPixel_X +=
413 						    wordSpacingWidth + (int16)fe.charWidth;
414 					}
415 				}
416 			} else {
417 				stringFinished = 1;	// character = 0x00
418 			}
419 		} while ((string < ptrStringEnd) && !stringFinished);
420 
421 		// var_8 = 0;
422 		heightOffset += wordSpacingHeight + lineHeight;
423 	} while (!stringFinished);
424 
425 	return generatedGfxEntry;
426 }
427 
freeGfx(gfxEntryStruct * pGfx)428 void freeGfx(gfxEntryStruct *pGfx) {
429 	if (pGfx->imagePtr) {
430 		MemFree(pGfx->imagePtr);
431 	}
432 
433 	MemFree(pGfx);
434 }
435 
436 
437 } // End of namespace Cruise
438