1 
2 /**
3  *
4  * @file font.cpp
5  *
6  * Part of the OpenJazz project
7  *
8  * @par History:
9  * - 23rd August 2005: Created font.c
10  * - 3rd February 2009: Renamed font.c to font.cpp
11  *
12  * @par Licence:
13  * Copyright (c) 2005-2017 Alister Thomson
14  *
15  * OpenJazz is distributed under the terms of
16  * the GNU General Public License, version 2.0
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  * @par Description:
23  * Deals with the loading, displaying and freeing of screen fonts.
24  *
25  */
26 
27 
28 #include "../file.h"
29 #include "font.h"
30 #include "video.h"
31 
32 #include <string.h>
33 
34 
35 /**
36  * Load a font from the given .0FN file.
37  *
38  * @param fileName Name of an .0FN file
39  */
Font(const char * fileName)40 Font::Font (const char* fileName) {
41 
42 	File* file;
43 	unsigned char* pixels;
44 	unsigned char* blank;
45 	int fileSize;
46 	int count, size, width, height;
47 
48 	// Load font from a font file
49 
50 	try {
51 
52 		file = new File(fileName, false);
53 
54 	} catch (int e) {
55 
56 		throw e;
57 
58 	}
59 
60 	fileSize = file->getSize();
61 
62 	nCharacters = 128;
63 
64 
65 	file->seek(20, true);
66 	lineHeight = file->loadChar() << 1;
67 
68 
69 	// Create blank character data
70 
71 	blank = new unsigned char[3];
72 	memset(blank, 0, 3);
73 
74 
75 	// Load characters
76 
77 	for (count = 0; count < 128; count++) {
78 
79 		if (file->tell() >= fileSize) {
80 
81 			nCharacters = count;
82 
83 			break;
84 
85 		}
86 
87 		size = file->loadShort();
88 
89 		if (size > 4) {
90 
91 			pixels = file->loadRLE(size);
92 
93 			width = pixels[0];
94 			width += pixels[1] << 8;
95 			height = pixels[2];
96 			height += pixels[3] << 8;
97 
98 			if (size - 4 >= width * height)
99 				characters[count] = createSurface(pixels + 4, width, height);
100 			else
101 				characters[count] = createSurface(blank, 3, 1);
102 
103 			delete[] pixels;
104 
105 		} else characters[count] = createSurface(blank, 3, 1);
106 
107 		SDL_SetColorKey(characters[count], SDL_SRCCOLORKEY, 0);
108 
109 	}
110 
111 	delete[] blank;
112 
113 	delete file;
114 
115 
116 	// Create ASCII->font map
117 
118 	for (count = 0; count < 33; count++) map[count] = 0;
119 	map[33] = 107; // !
120 	map[34] = 116; // "
121 	map[35] = 0; // #
122 	map[36] = 63; // $
123 	map[37] = 0; // %
124 	map[38] = 0; // &
125 	map[39] = 115; // '
126 	map[40] = 111; // (
127 	map[41] = 112; // )
128 	map[42] = 0; // *
129 	map[43] = 105; // +
130 	map[44] = 101; // ,
131 	map[45] = 104; // -
132 	map[46] = 102; // .
133 	map[47] = 108; // /
134 	for (count = 48; count < 58; count++) map[count] = count + 5;  // Numbers
135 	map[58] = 114; // :
136 	map[59] = 113; // ;
137 	map[60] = 0; // <
138 	map[61] = 106; // =
139 	map[62] = 0; // >
140 	map[63] = 103; // ?
141 	map[64] = 0; // @
142 	for (count = 65; count < 91; count++) map[count] = count - 38; // Upper-case letters
143 	for (; count < 97; count++) map[count] = 0;
144 	for (; count < 123; count++) map[count] = count - 96; // Lower-case letters
145 	for (; count < 128; count++) map[count] = 0;
146 
147 	for (count = 0; count < 128; count++) {
148 
149 		if (map[count] >= nCharacters) map[count] = 0;
150 
151 	}
152 
153 	return;
154 
155 }
156 
157 
158 /**
159  * Create a font from the panel pixel data.
160  *
161  * @param pixels Panel pixel data
162  * @param big Whether to use the small or the big font
163  */
Font(unsigned char * pixels,bool big)164 Font::Font (unsigned char* pixels, bool big) {
165 
166 	unsigned char* chrPixels;
167 	int count, y;
168 
169 	if (big) lineHeight = 8;
170 	else lineHeight = 7;
171 
172 	chrPixels = new unsigned char[8 * lineHeight];
173 
174 	for (count = 0; count < 40; count++) {
175 
176 		for (y = 0; y < lineHeight; y++)
177 			memcpy(chrPixels + (y * 8), pixels + (count * 8) + (y * SW), 8);
178 
179 		characters[count] = createSurface(chrPixels, 8, lineHeight);
180 
181 		if (big) SDL_SetColorKey(characters[count], SDL_SRCCOLORKEY, 31);
182 
183 	}
184 
185 	nCharacters= 40;
186 
187 	delete[] chrPixels;
188 
189 
190 	// Create ASCII->font map
191 
192 	if (big) {
193 
194 		// Goes " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-:."
195 
196 		for (count = 0; count < 45; count++) map[count] = 0;
197 		map[count++] = 37;
198 		map[count++] = 39;
199 		for (; count < 48; count++) map[count] = 0;
200 		for (; count < 58; count++) map[count] = count - 47; // Numbers
201 		map[count++] = 38;
202 		for (; count < 65; count++) map[count] = 0;
203 		for (; count < 91; count++) map[count] = count - 54; // Upper-case letters
204 		for (; count < 97; count++) map[count] = 0;
205 		for (; count < 123; count++) map[count] = count - 86; // Lower-case letters
206 		for (; count < 128; count++) map[count] = 0;
207 
208 	} else {
209 
210 		// Goes " 0123456789oo" (where oo = infinity)
211 		// Use :; to represent the infinity symbol
212 
213 		for (count = 0; count < 48; count++) map[count] = 0;
214 		for (; count < 60; count++) map[count] = count - 47; // Numbers and :;
215 		for (; count < 128; count++) map[count] = 0;
216 
217 	}
218 
219 	return;
220 
221 }
222 
223 
224 /**
225  * Load a font from a .000 file.
226  *
227  * @param bonus whether to use FONTS.000 or BONUS.000
228  */
Font(bool bonus)229 Font::Font (bool bonus) {
230 
231 	File* file;
232 	unsigned char* pixels;
233 	int fileSize;
234 	int count, width, height;
235 
236 	// Load font from FONTS.000 or BONUS.000
237 
238 	try {
239 
240 		file = new File(bonus? "BONUS.000": "FONTS.000", false);
241 
242 	} catch (int e) {
243 
244 		throw e;
245 
246 	}
247 
248 
249 	fileSize = file->getSize();
250 
251 	nCharacters = file->loadShort(256);
252 
253 	if (bonus) {
254 
255 		count = file->loadShort();
256 		nCharacters -= count;
257 
258 		// Skip sprites
259 
260 		for (; count > 0; count--) {
261 
262 			file->seek(4, false);
263 
264 			width = file->loadShort();
265 			if (width == 0xFFFF) width = 0;
266 
267 			file->seek((width << 2) + file->loadShort(), false);
268 
269 		}
270 
271 	}
272 
273 	// Load characters
274 
275 	for (count = 0; count < nCharacters; count++) {
276 
277 		if (file->tell() >= fileSize) {
278 
279 			nCharacters = count;
280 
281 			break;
282 
283 		}
284 
285 		width = file->loadShort(SW);
286 		height = file->loadShort(SH);
287 
288 		if (bonus) width = (width + 3) & ~3;
289 		else width <<= 2;
290 
291 		file->seek(4, false);
292 
293 		pixels = file->loadPixels(width * height);
294 
295 		characters[count] = createSurface(pixels, width, height);
296 		SDL_SetColorKey(characters[count], SDL_SRCCOLORKEY, 254);
297 
298 		delete[] pixels;
299 
300 	}
301 
302 	delete file;
303 
304 	lineHeight = characters[0]->h;
305 
306 
307 	// Create blank character data
308 
309 	pixels = new unsigned char[3];
310 	memset(pixels, 254, 3);
311 	characters[nCharacters] = createSurface(pixels, 3, 1);
312 	SDL_SetColorKey(characters[nCharacters], SDL_SRCCOLORKEY, 254);
313 	delete[] pixels;
314 
315 
316 	// Create ASCII->font map
317 
318 	count = 0;
319 
320 	if (bonus) {
321 
322 		for (; count < 42; count++) map[count] = nCharacters;
323 		map[count++] = 37; // *
324 		for (; count < 46; count++) map[count] = nCharacters;
325 		map[count++] = 39; // .
326 		map[count++] = 38; // /
327 		for (; count < 59; count++) map[count] = count - 22; // Numbers and :
328 
329 	} else {
330 
331 		for (; count < 37; count++) map[count] = nCharacters;
332 		map[count++] = 36; // %
333 		for (; count < 48; count++) map[count] = nCharacters;
334 		for (; count < 58; count++) map[count] = count - 22; // Numbers
335 
336 	}
337 
338 	for (; count < 65; count++) map[count] = nCharacters;
339 	for (; count < 91; count++) map[count] = count - 65; // Upper-case letters
340 	for (; count < 97; count++) map[count] = nCharacters;
341 	for (; count < 123; count++) map[count] = count - 97; // Lower-case letters
342 	for (; count < 128; count++) map[count] = nCharacters;
343 
344 	nCharacters++;
345 
346 	for (count = 0; count < 128; count++) {
347 
348 		if (map[count] >= nCharacters) map[count] = 0;
349 
350 	}
351 
352 	return;
353 
354 }
355 
356 
357 /**
358  * Delete the font.
359  */
~Font()360 Font::~Font () {
361 
362 	int count;
363 
364 	for (count = 0; count < nCharacters; count++) SDL_FreeSurface(characters[count]);
365 
366 	return;
367 
368 }
369 
370 
371 /**
372  * Draw a string using the font.
373  *
374  * @param string The string to draw
375  * @param x The x-coordinate at which to draw the string
376  * @param y The y-coordinate at which to draw the string
377  *
378  * @return The x-coordinate of the end of the string
379  */
showString(const char * string,int x,int y)380 int Font::showString (const char* string, int x, int y) {
381 
382 	SDL_Surface* surface;
383 	SDL_Rect dst;
384 	unsigned int count;
385 	int xOffset, yOffset;
386 
387 	// Determine the position at which to draw the first character
388 	xOffset = x;
389 	yOffset = y;
390 
391 	// Go through each character of the string
392 	for (count = 0; string[count]; count++) {
393 
394 		if (string[count] == '\n') {
395 
396 			xOffset = x;
397 			yOffset += lineHeight;
398 
399 		} else {
400 
401 			// Determine the character's position on the screen
402 			dst.y = yOffset;
403 			dst.x = xOffset;
404 
405 			// Determine the character's surface
406 			surface = characters[int(map[int(string[count])])];
407 
408 			// Draw the character to the screen
409 			SDL_BlitSurface(surface, NULL, canvas, &dst);
410 
411 			xOffset += surface->w + 2;
412 
413 		}
414 
415 	}
416 
417 	return xOffset;
418 
419 }
420 
421 
422 /**
423  * Draw a JJ1 cutscene string using the font.
424  *
425  * @param string The JJ1 cutstring to draw
426  * @param x The x-coordinate at which to draw the string
427  * @param y The y-coordinate at which to draw the string
428  *
429  * @return The x-coordinate of the end of the string
430  */
showSceneString(const unsigned char * string,int x,int y)431 int Font::showSceneString (const unsigned char* string, int x, int y) {
432 
433 	SDL_Surface* surface;
434 	SDL_Rect dst;
435 	unsigned int count;
436 	int offset;
437 
438 	// Determine the position at which to draw the first character
439 	offset = x;
440 
441 	// Go through each character of the string
442 	for (count = 0; string[count]; count++) {
443 
444 		// Determine the character's position on the screen
445 		dst.y = y;
446 		dst.x = offset;
447 
448 		// Determine the character's surface
449 		if (string[count] < nCharacters) surface = characters[int(string[count])];
450 		else surface = characters[0];
451 
452 		// Draw the character to the screen
453 		SDL_BlitSurface(surface, NULL, canvas, &dst);
454 
455 		offset += surface->w + 1;
456 
457 	}
458 
459 	return offset;
460 
461 }
462 
463 
464 /**
465  * Draw a number using the font.
466  *
467  * @param n The number to draw
468  * @param x The x-coordinate at which to draw the number
469  * @param y The y-coordinate at which to draw the number
470  *
471  * @return The x-coordinate of the end of the number
472  */
showNumber(int n,int x,int y)473 void Font::showNumber (int n, int x, int y) {
474 
475 	SDL_Surface *surface;
476 	SDL_Rect dst;
477 	int count, offset;
478 
479 	// n being 0 is a special case. It must not be considered to be a trailing
480 	// zero, as these are not displayed.
481 	if (!n) {
482 
483 		// Determine 0's surface
484 		surface = characters[int(map[int('0')])];
485 
486 		// Determine 0's position on the screen
487 		dst.y = y;
488 		dst.x = x - surface->w;
489 
490 		// Draw 0 to the screen
491 		SDL_BlitSurface(surface, NULL, canvas, &dst);
492 
493 		return;
494 
495 	}
496 
497 	// Determine the length of the number to be drawn
498 	if (n > 0) count = n;
499 	else count = -n;
500 
501 	// Determine the position at which to draw the lowest digit
502 	offset = x;
503 
504 	while (count) {
505 
506 		// Determine the digit's surface
507 		surface = characters[int(map['0' + (count % 10)])];
508 
509 		offset -= surface->w;
510 
511 		// Determine the digit's position on the screen
512 		dst.y = y;
513 		dst.x = offset;
514 
515 		// Draw the digit to the screen
516 		SDL_BlitSurface(surface, NULL, canvas, &dst);
517 
518 		count /= 10;
519 
520 	}
521 
522 	// If needed, draw the negative sign
523 	if (n < 0) {
524 
525 		// Determine the negative sign's surface
526 		surface = characters[int(map[int('-')])];
527 
528 		// Determine the negative sign's position on the screen
529 		dst.y = y;
530 		dst.x = offset - surface->w;
531 
532 		// Draw the negative sign to the screen
533 		SDL_BlitSurface(surface, NULL, canvas, &dst);
534 
535 	}
536 
537 	return;
538 
539 }
540 
541 
542 /**
543  * Map a range of palette indices to another range
544  *
545  * @param start Start of original range
546  * @param length Span of original range
547  * @param newStart Start of new range
548  * @param newLength Span of new range
549  */
mapPalette(int start,int length,int newStart,int newLength)550 void Font::mapPalette (int start, int length, int newStart, int newLength) {
551 
552 	SDL_Color palette[256];
553 	int count;
554 
555 	for (count = 0; count < length; count++)
556 		palette[count].r = palette[count].g = palette[count].b =
557 			(count * newLength / length) + newStart;
558 
559 	for (count = 0; count < nCharacters; count++)
560 		SDL_SetPalette(characters[count], SDL_LOGPAL, palette, start, length);
561 
562 	return;
563 
564 }
565 
566 
567 /**
568  * Restore a palette to its original state.
569  */
restorePalette()570 void Font::restorePalette () {
571 
572 	int count;
573 
574 	for (count = 0; count < nCharacters; count++)
575 		video.restoreSurfacePalette(characters[count]);
576 
577 	return;
578 
579 }
580 
581 
582 /**
583  * Get the height of a single line of any text.
584  *
585  * @return The height
586  */
getHeight()587 int Font::getHeight () {
588 
589 	return lineHeight;
590 
591 }
592 
593 
594 /**
595  * Get the width of a single line of a given string.
596  *
597  * @param string The string to measure
598  *
599  * @return The width
600  */
getStringWidth(const char * string)601 int Font::getStringWidth (const char *string) {
602 
603 	int count;
604 	int stringWidth = 0;
605 
606 	// Go through each character of the string
607 	for (count = 0; string[count]; count++) {
608 
609 		// Only get the width of the first line
610 		if (string[count] == '\n') return stringWidth;
611 
612 		stringWidth += characters[int(map[int(string[count])])]->w + 2;
613 
614 	}
615 
616 	return stringWidth;
617 
618 }
619 
620 
621 /**
622  * Get the width of a single line of a given JJ1 cutscene string.
623  *
624  * @param string The string to measure
625  *
626  * @return The width
627  */
getSceneStringWidth(const unsigned char * string)628 int Font::getSceneStringWidth (const unsigned char *string) {
629 
630 	int count;
631 	int stringWidth = 0;
632 
633 	// Go through each character of the string
634 	for (count = 0; string[count]; count++) {
635 
636 		if (string[count] < nCharacters) stringWidth += characters[int(string[count])]->w + 1;
637 		else stringWidth += characters[0]->w + 1;
638 
639 	}
640 
641 	return stringWidth;
642 
643 }
644 
645 
646