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