]> icculus.org git repositories - taylor/freespace2.git/blob - src/graphics/font.cpp
fix issue with FS1 fonts
[taylor/freespace2.git] / src / graphics / font.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Graphics/Font.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * source file for font stuff
16  *
17  * $Log$
18  * Revision 1.9  2003/08/03 15:51:58  taylor
19  * fix issue with FS1 fonts
20  *
21  * Revision 1.8  2003/06/19 11:51:41  taylor
22  * adjustments to memory leak fixes
23  *
24  * Revision 1.7  2003/06/11 18:30:32  taylor
25  * plug memory leaks
26  *
27  * Revision 1.6  2003/05/25 02:30:42  taylor
28  * Freespace 1 support
29  *
30  * Revision 1.5  2002/07/13 06:46:47  theoddone33
31  * Warning cleanups
32  *
33  * Revision 1.4  2002/06/09 04:41:17  relnev
34  * added copyright header
35  *
36  * Revision 1.3  2002/06/05 08:05:29  relnev
37  * stub/warning removal.
38  *
39  * reworked the sound code.
40  *
41  * Revision 1.2  2002/05/07 03:16:45  theoddone33
42  * The Great Newline Fix
43  *
44  * Revision 1.1.1.1  2002/05/03 03:28:09  root
45  * Initial import.
46  *
47  * 
48  * 9     7/09/99 10:32p Dave
49  * Command brief and red alert screens.
50  * 
51  * 8     12/14/98 9:21a Dan
52  * Put gr8_string() back in
53  * 
54  * 7     12/02/98 5:47p Dave
55  * Put in interface xstr code. Converted barracks screen to new format.
56  * 
57  * 6     11/30/98 1:07p Dave
58  * 16 bit conversion, first run.
59  * 
60  * 5     11/05/98 4:18p Dave
61  * First run nebula support. Beefed up localization a bit. Removed all
62  * conditional compiles for foreign versions. Modified mission file
63  * format.
64  * 
65  * 4     10/13/98 9:28a Dave
66  * Started neatening up freespace.h. Many variables renamed and
67  * reorganized. Added AlphaColors.[h,cpp]
68  * 
69  * 3     10/13/98 9:19a Andsager
70  * Add localization support to cfile.  Optional parameter with cfopen that
71  * looks for localized files.
72  * 
73  * 2     10/07/98 10:52a Dave
74  * Initial checkin.
75  * 
76  * 1     10/07/98 10:49a Dave
77  * 
78  * 49    7/02/98 3:41p Allender
79  * fix nasty assembler bug where pushf wasn't accompanied by corresponding
80  * pop in certain clipping conditions -- manifested only in software and
81  * usually on the PXO chat screen.
82  * 
83  * 48    6/18/98 10:10a Allender
84  * fixed compiler warnings
85  * 
86  * 47    6/13/98 10:48p Lawrance
87  * Changed code to utilize proper fixed-space 1 character.
88  * 
89  * 46    6/13/98 3:18p Hoffoss
90  * NOX()ed out a bunch of strings that shouldn't be translated.
91  * 
92  * 45    5/25/98 10:32a John
93  * Took out redundant code for font bitmap offsets that converted to a
94  * float, then later on converted back to an integer.  Quite unnecessary.
95  * 
96  * 44    5/12/98 11:45a John
97  * Fixed a bug with the length of string being passed to
98  * gr_get_string_size.  The bug occurred if you had two or more'\n'''s in
99  * a row.
100  * 
101  * 43    3/10/98 4:18p John
102  * Cleaned up graphics lib.  Took out most unused gr functions.   Made D3D
103  * & Glide have popups and print screen.  Took out all >8bpp software
104  * support.  Made Fred zbuffer.  Made zbuffer allocate dynamically to
105  * support Fred.  Made zbuffering key off of functions rather than one
106  * global variable.
107  * 
108  * 42    3/09/98 6:06p John
109  * Restructured font stuff to avoid duplicate code in Direct3D and Glide.
110  * Restructured Glide to avoid redundent state setting.
111  * 
112  * 41    3/02/98 10:33a John
113  * Fixed bug where Y clip was using X value.
114  * 
115  * 40    2/19/98 9:04a John
116  * Fixed fonts with glide
117  * 
118  * 39    2/17/98 7:27p John
119  * Got fonts and texturing working in Direct3D
120  * 
121  * 38    2/05/98 9:21p John
122  * Some new Direct3D code.   Added code to monitor a ton of stuff in the
123  * game.
124  * 
125  * 37    1/20/98 2:25p Hoffoss
126  * Fixed bug where timestamp printing incorrectly increments an hour at 6
127  * minutes.
128  * 
129  * 36    1/19/98 6:15p John
130  * Fixed all my Optimized Build compiler warnings
131  * 
132  * 35    11/30/97 12:18p John
133  * added more 24 & 32-bpp primitives
134  * 
135  * 34    11/29/97 2:24p John
136  * fixed warnings
137  * 
138  * 33    11/29/97 2:06p John
139  * added mode 16-bpp support
140  * 
141  * 32    11/10/97 2:35p Hoffoss
142  * Changed timestamp printing code to utilize new fixed-space '1'
143  * character.
144  * 
145  * 31    11/06/97 5:42p Hoffoss
146  * Added support for fixed size timstamp rendering.
147  * 
148  * 30    11/03/97 10:08p Hoffoss
149  * Changed gr_get_string_size to utilize an optional length specifier, if
150  * you want to use non-null terminated strings.
151  * 
152  * 29    11/03/97 10:59a John
153  * added support for more than one font.
154  * 
155  * 28    10/24/97 12:13p Hoffoss
156  * Added gr_force_fit_string().
157  * 
158  * 27    10/19/97 12:55p John
159  * new code to lock / unlock surfaces for smooth directx integration.
160  * 
161  * 26    10/14/97 4:50p John
162  * more 16 bpp stuff.
163  * 
164  * 25    10/14/97 8:08a John
165  * added a bunch more 16 bit support
166  * 
167  * 24    10/09/97 5:23p John
168  * Added support for more 16-bpp functions
169  * 
170  * 23    9/23/97 10:53a Hoffoss
171  * Fixed bug in assumptions made half the time, and not the other half.
172  * 
173  * 22    9/09/97 3:39p Sandeep
174  * warning level 4 bugs
175  * 
176  * 21    8/19/97 5:46p Hoffoss
177  * Changed font used in Fred, and added display to show current eye
178  * position.
179  * 
180  * 20    6/25/97 2:35p John
181  * added some functions to use the windows font for Fred.
182  * 
183  * 19    6/17/97 12:03p John
184  * Moved color/alphacolor functions into their own module.  Made all color
185  * functions be part of the low-level graphics drivers, not just the
186  * grsoft.
187  * 
188  * 18    6/13/97 2:37p John
189  * fixed a string bug that printed weird characters sometimes.
190  * 
191  * 17    6/11/97 6:00p John
192  * sped up alpha matching a bit.
193  * 
194  * 16    6/11/97 5:49p John
195  * Changed palette code to only recalculate alphacolors when needed, not
196  * when palette changes.
197  * 
198  * 15    6/11/97 5:01p John
199  * Fixed mission log text.  Added fast clipped font drawer.  
200  * 
201  * 14    6/11/97 4:11p John
202  * addec function to get font height
203  * 
204  * 13    6/11/97 1:12p John
205  * Started fixing all the text colors in the game.
206  * 
207  * 12    6/09/97 9:24a John
208  * Changed the way fonts are set.
209  * 
210  * 11    6/06/97 4:41p John
211  * Fixed alpha colors to be smoothly integrated into gr_set_color_fast
212  * code.
213  * 
214  * 10    6/06/97 2:40p John
215  * Made all the radar dim in/out
216  * 
217  * 9     6/05/97 4:53p John
218  * First rev of new antialiased font stuff.
219  * 
220  * 8     5/12/97 12:27p John
221  * Restructured Graphics Library to add support for multiple renderers.
222  * 
223  * 7     4/24/97 11:28a Hoffoss
224  * added code to handle gr_String being called before gr_init_font ever
225  * was called
226  * 
227  * 6     4/23/97 5:26p John
228  * First rev of new debug console stuff.
229  * 
230  * 5     4/22/97 12:20p John
231  * fixed more resource leaks
232  * 
233  * 4     4/22/97 10:33a John
234  * fixed the 2d resource leaks that Alan found.
235  * 
236  * 3     4/04/97 11:21a Hoffoss
237  * JOHN: Fixed invalid param that caused a trap in BoundsChecker.    
238  * 
239  * 2     4/01/97 9:26a Allender
240  * added support for descent style fonts although they are not used in the
241  * game yet
242  * 
243  * 1     3/31/97 9:42a Allender
244  *
245  * $NoKeywords: $
246  */
247
248 #ifndef PLAT_UNIX
249 #include <windows.h>
250 #include <windowsx.h>
251 #endif
252 #include <stdio.h>
253 #include <stdarg.h>
254 #include "grinternal.h"
255 #include "2d.h"
256 #include "cfile.h"
257 #include "font.h"
258 #include "palman.h"
259 #include "key.h"
260 #include "bmpman.h"
261 #include "localize.h"
262
263 int Num_fonts = 0;
264 font Fonts[MAX_FONTS];
265 font *Current_font = NULL;
266
267
268 // crops a string if required to force it to not exceed max_width pixels when printed.
269 // Does this by dropping characters at the end of the string and adding '...' to the end.
270 //    str = string to crop.  Modifies this string directly
271 //    max_str = max characters allowed in str
272 //    max_width = number of pixels to limit string to (less than or equal to).
273 //    Returns: returns same pointer passed in for str.
274 char *gr_force_fit_string(char *str, int max_str, int max_width)
275 {
276         int w;
277
278         gr_get_string_size(&w, NULL, str);
279         if (w > max_width) {
280                 if ((int) strlen(str) > max_str - 3) {
281                         Assert(max_str >= 3);
282                         str[max_str - 3] = 0;
283                 }
284
285                 strcpy(str + strlen(str) - 1, "...");
286                 gr_get_string_size(&w, NULL, str);
287                 while (w > max_width) {
288                         Assert(strlen(str) >= 4);  // if this is hit, a bad max_width was passed in and the calling function needs fixing.
289                         strcpy(str + strlen(str) - 4, "...");
290                         gr_get_string_size(&w, NULL, str);
291                 }
292         }
293
294         return str;
295 }
296
297 //takes the character BEFORE being offset into current font
298 // Returns the letter code
299 int get_char_width(ubyte c1,ubyte c2,int *width,int *spacing)
300 {
301         int i, letter;
302
303         Assert ( Current_font != NULL );
304         letter = c1-Current_font->first_ascii;
305
306         if (letter<0 || letter>=Current_font->num_chars) {                              //not in font, draw as space
307                 *width=0;
308                 *spacing = Current_font->w;
309                 return -1;
310         }
311
312         *width = Current_font->char_data[letter].byte_width;
313         *spacing = Current_font->char_data[letter].spacing;
314
315         i = Current_font->char_data[letter].kerning_entry;
316         if ( i > -1)  {
317                 if (!(c2==0 || c2=='\n')) {
318                         int letter2;
319
320                         letter2 = c2-Current_font->first_ascii;
321
322 #ifndef MAKE_FS1
323                         if ((letter2>=0) && (letter2<Current_font->num_chars) ) {                               //not in font, draw as space
324 #else
325                         // umm, not sure about this, issue with FS1 fonts that's no longer handled apparently
326                         if ((letter2>=0) && (letter2<Current_font->num_chars) && (Current_font->num_kern_pairs != 159) ) {      //not in font, draw as space
327 #endif
328                                 font_kernpair   *k = &Current_font->kern_data[i];
329                                 while( (k->c1 == letter) && (k->c2<letter2) && (i<Current_font->num_kern_pairs) )       {
330                                         i++;
331                                         k++;
332                                 }
333                                 if ( k->c2 == letter2 ) {
334                                         *spacing += k->offset;
335                                 }
336                         }
337                 }
338         }
339         return letter;
340 }
341
342 int get_centered_x(char *s)
343 {
344         int w,w2,s2;
345
346         for (w=0;*s!=0 && *s!='\n';s++) {
347                 get_char_width(s[0],s[1],&w2,&s2);
348                 w += s2;
349         }
350
351         return ((gr_screen.clip_width - w) / 2);
352 }
353
354 // draws a character centered on x
355 void gr_char_centered(int x, int y, char chr)
356 {
357         char str[2];
358         int w, sc;
359
360         sc = Lcl_special_chars;
361         if (chr == '1')
362                 chr = (char)(sc + 1);
363
364         str[0] = chr;
365         str[1] = 0;
366         gr_get_string_size(&w, NULL, str);
367         gr_string(x - w / 2, y, str);
368 }
369
370 void gr_print_timestamp(int x, int y, int timestamp)
371 {
372         char h[2], m[3], s[3];
373         int w, c;
374
375         // format the time information into strings
376         sprintf(h, "%.1d", (timestamp / 3600000) % 10);
377         sprintf(m, "%.2d", (timestamp / 60000) % 60);
378         sprintf(s, "%.2d", (timestamp / 1000) % 60);
379
380         gr_get_string_size(&w, NULL, "0");
381         gr_get_string_size(&c, NULL, ":");
382
383         gr_string(x + w, y, ":");
384         gr_string(x + w * 3 + c, y, ":");
385
386         x += w / 2;
387         gr_char_centered(x, y, h[0]);
388         x += w + c;
389         gr_char_centered(x, y, m[0]);
390         x += w;
391         gr_char_centered(x, y, m[1]);
392         x += w + c;
393         gr_char_centered(x, y, s[0]);
394         x += w;
395         gr_char_centered(x, y, s[1]);
396 }
397
398 int gr_get_font_height()
399 {
400         if (Current_font)       {
401                 return Current_font->h;
402         } else {
403                 return 16;
404         }
405 }
406
407 void gr_get_string_size(int *w1, int *h1, char *text, int len)
408 {
409         int longest_width;
410         int width,spacing;
411         int w, h;
412
413         if (!Current_font)      {
414                 if ( w1)
415                         *w1 = 16;
416
417                 if ( h1 )
418                         *h1 = 16;
419
420                 return;
421         }       
422
423         w = 0;
424         h = 0;
425         longest_width = 0;
426
427         if (text != NULL ) {
428                 h += Current_font->h;
429                 while (*text && (len>0) ) {
430
431                         // Process one or more 
432                         while ((*text == '\n') && (len>0) ) {
433                                 text++;
434                                 len--;
435                                 if ( *text )    {
436                                         h += Current_font->h;
437                                 }
438                                 w = 0;
439                         }
440
441                         if (*text == 0) {
442                                 break;
443                         }
444
445                         get_char_width(text[0], text[1], &width, &spacing);
446                         w += spacing;
447                         if (w > longest_width)
448                                 longest_width = w;
449
450                         text++;
451                         len--;
452                 }
453         }
454
455         if ( h1 )
456                 *h1 = h;
457
458         if ( w1 )
459                 *w1 = longest_width;
460 }
461
462
463 MONITOR( FontChars );   
464
465
466 /*
467
468 void gr8_char(int x,int y,int letter)
469 {
470         font_char *ch;
471         
472         ch = &Current_font->char_data[letter];
473
474         gr_aabitmap_ex( x, y, ch->byte_width, Current_font->h, Current_font->u[letter], Current_font->v[letter] );
475
476 //      mprintf(( "String = %s\n", text ));
477 }
478
479
480 void gr8_string( int sx, int sy, char *s )
481 {
482         int width, spacing, letter;
483         int x, y;
484
485         if ( !Current_font ) return;
486         if ( !s ) return;
487         
488         gr_set_bitmap(Current_font->bitmap);
489
490         x = sx;
491         y = sy;
492
493         if (sx==0x8000) {                       //centered
494                 x = get_centered_x(s);
495         } else {
496                 x = sx;
497         }
498
499         while (*s)      {
500                 while (*s== '\n' )      {
501                         s++;
502                         y += Current_font->h;
503                         if (sx==0x8000) {                       //centered
504                                 x = get_centered_x(s);
505                         } else {
506                                 x = sx;
507                         }
508                 }
509                 if (*s == 0 ) break;
510
511                 letter = get_char_width(s[0],s[1],&width,&spacing);
512
513                 if (letter<0) { //not in font, draw as space
514                         x += spacing;
515                         s++;
516                         continue;
517                 }
518                 gr8_char( x, y, letter );
519         
520                 x += spacing;
521                 s++;
522         }
523 }
524 */
525
526 void gr8_string(int sx, int sy, char *s )
527 {
528         int row,width, spacing, letter;
529         int x, y;
530
531         if ( !Current_font ) return;
532         if ( !s ) return;
533
534         x = sx;
535         y = sy;
536
537         if (sx==0x8000) {                       //centered
538                 x = get_centered_x(s);
539         } else {
540                 x = sx;
541         }
542
543         spacing = 0;
544
545         gr_lock();
546
547
548         while (*s)      {
549                 MONITOR_INC( FontChars, 1 );    
550
551                 x += spacing;
552                 while (*s== '\n' )      {
553                         s++;
554                         y += Current_font->h;
555                         if (sx==0x8000) {                       //centered
556                                 x = get_centered_x(s);
557                         } else {
558                                 x = sx;
559                         }
560                 }
561                 if (*s == 0 ) break;
562
563                 letter = get_char_width(s[0],s[1],&width,&spacing);
564                 s++;
565
566                 //If not in font, draw as space
567                 if (letter<0) continue;
568
569                 int xd, yd, xc, yc;
570                 int wc, hc;
571
572                 // Check if this character is totally clipped
573                 if ( x + width < gr_screen.clip_left ) continue;
574                 if ( y + Current_font->h < gr_screen.clip_top ) continue;
575                 if ( x > gr_screen.clip_right ) continue;
576                 if ( y > gr_screen.clip_bottom ) continue;
577
578                 xd = yd = 0;
579                 if ( x < gr_screen.clip_left ) xd = gr_screen.clip_left - x;
580                 if ( y < gr_screen.clip_top ) yd = gr_screen.clip_top - y;
581                 xc = x+xd;
582                 yc = y+yd;
583
584                 wc = width - xd; hc = Current_font->h - yd;
585                 if ( xc + wc > gr_screen.clip_right ) wc = gr_screen.clip_right - xc;
586                 if ( yc + hc > gr_screen.clip_bottom ) hc = gr_screen.clip_bottom - yc;
587
588                 if ( wc < 1 ) continue;
589                 if ( hc < 1 ) continue;
590
591                 ubyte *fp = Current_font->pixel_data + Current_font->char_data[letter].offset + xd + yd*width;
592                 ubyte *dptr = GR_SCREEN_PTR(ubyte, xc, yc);                     
593
594 #ifndef HARDWARE_ONLY
595                 if ( Current_alphacolor )       {
596                         for (row=0; row<hc; row++)      {
597                                 #ifdef USE_INLINE_ASM
598                                         ubyte *lookup = &Current_alphacolor->table.lookup[0][0];
599                                                 _asm mov edx, lookup
600                                                 _asm xor eax, eax
601                                                 _asm mov ecx, wc
602                                                 _asm xor ebx, ebx
603                                                 _asm mov edi, dptr
604                                                 _asm mov esi, fp
605                                                 _asm shr ecx, 1
606                                                 _asm jz  OnlyOne
607                                                 _asm pushf
608                                         InnerFontLoop:
609                                                 _asm mov al, [edi]
610                                                 _asm mov bl, [edi+1]
611                                                 _asm add edi,2
612
613                                                 _asm mov ah, [esi]
614                                                 _asm mov bh, [esi+1]
615                                                 _asm add esi,2
616
617                                                 _asm mov al, [edx+eax]
618                                                 _asm mov ah, [edx+ebx]
619
620                                                 _asm mov [edi-2], ax
621
622                                                 _asm dec ecx
623                                                 _asm jnz InnerFontLoop
624
625                                                 _asm popf
626                                                 _asm jnc NotOdd
627
628                                         OnlyOne:
629                                                 _asm mov al, [edi]
630                                                 _asm mov ah, [esi]
631                                                 _asm mov al, [edx+eax]
632                                                 _asm mov [edi], al
633
634                                         NotOdd:
635                                         dptr += gr_screen.rowsize;
636                                         fp += width;
637                                 #else
638                                         int i;
639                                         for (i=0; i< wc; i++ )  {
640                                                 *dptr++ = Current_alphacolor->table.lookup[*fp++][*dptr];
641                                         }
642                                         fp += width - wc;
643                                         dptr += gr_screen.rowsize - wc;
644                                 #endif
645                         }
646                 } else {                // No alpha color
647 #endif
648                         for (row=0; row<hc; row++)      {
649                                 int i;
650                                 for (i=0; i< wc; i++ )  {
651                                         if (*fp > 5 )
652                                                 *dptr = gr_screen.current_color.raw8;
653                                         dptr++;
654                                         fp++;
655                                 }
656                                 fp += width - wc;
657                                 dptr += gr_screen.rowsize - wc;
658                         }
659                 // }
660         }
661         gr_unlock();
662 }
663
664 #ifndef PLAT_UNIX
665 HFONT MyhFont = NULL;
666 extern HDC hDibDC;
667 #endif
668
669 void gr_string_win(int x, int y, char *s)
670 {
671 #ifdef PLAT_UNIX
672 //      STUB_FUNCTION;
673 #else
674         char *ptr;
675         SIZE size;
676
677         if ( MyhFont==NULL )    {
678                 MyhFont = CreateFont(14, 0, 0, 0,                               // height,width,?,?
679                                 700,
680                                 FALSE,
681                                 FALSE,
682                                 FALSE,                                                                                  // strikeout?
683                                 ANSI_CHARSET,                                                                   // character set
684                                 OUT_DEVICE_PRECIS,
685                                 CLIP_DEFAULT_PRECIS,
686                                 DEFAULT_QUALITY,
687                                 DEFAULT_PITCH | FF_DONTCARE,
688 //                              NULL );
689 //                              "Times New Roman" );
690 //XSTR:OFF
691                                 "Ariel" );
692 //XSTR:ON
693         }
694
695         SelectObject( hDibDC, MyhFont );
696
697         if ( gr_screen.bits_per_pixel==8 )
698                 SetTextColor(hDibDC, PALETTEINDEX(gr_screen.current_color.raw8));
699         else
700                 SetTextColor(hDibDC, RGB(gr_screen.current_color.red,gr_screen.current_color.green,gr_screen.current_color.blue));
701
702         SetBkMode(hDibDC,TRANSPARENT);
703
704
705         HRGN hclip;
706         hclip = CreateRectRgn( gr_screen.offset_x, 
707                                                                   gr_screen.offset_y, 
708                                                                   gr_screen.offset_x+gr_screen.clip_width-1, 
709                                                                   gr_screen.offset_y+gr_screen.clip_height-1 );
710
711         SelectClipRgn(hDibDC, hclip );
712         x += gr_screen.offset_x;
713         y += gr_screen.offset_y;
714         //ptr = strchr(s,'\n);
715         while ((ptr = strchr(s, '\n'))!=NULL) {
716                 TextOut(hDibDC, x, y, s, ptr - s);
717                 GetTextExtentPoint32(hDibDC, s, ptr - s, &size);
718                 y += size.cy;
719                 s = ptr + 1;
720         }
721
722         TextOut(hDibDC, x, y, s, strlen(s));
723         SelectClipRgn(hDibDC, NULL);
724         DeleteObject(hclip);
725 #endif
726 }
727
728 void gr_get_string_size_win(int *w, int *h, char *text)
729 {
730 #ifdef PLAT_UNIX
731 //      STUB_FUNCTION;
732 #else
733         char *ptr;
734         SIZE size;
735
736         ptr = strchr(text, '\n');
737
738         if (MyhFont==NULL)      {
739                 if (w) *w = 0;
740                 if (h) *h = 0;
741                 return;
742         }
743
744         SelectObject( hDibDC, MyhFont );
745
746         if (!ptr)       {
747                 GetTextExtentPoint32( hDibDC, text, strlen(text), &size);
748                 if (w) *w = size.cx;
749                 if (h) *h = size.cy;
750                 return;
751         }
752
753         GetTextExtentPoint32(hDibDC, text, ptr - text, &size);
754         gr_get_string_size_win(w, h, ptr+1);
755         if (w && (size.cx > *w) )
756                 *w = size.cx;
757
758         if (h)
759                 *h += size.cy;
760 #endif
761 }
762
763 char grx_printf_text[2048];     
764
765 void _cdecl gr_printf( int x, int y, char * format, ... )
766 {
767         va_list args;
768
769         if ( !Current_font ) return;
770         
771         va_start(args, format);
772         vsprintf(grx_printf_text,format,args);
773         va_end(args);
774
775         gr_string(x,y,grx_printf_text);
776 }
777
778 void gr_font_close()
779 {
780         font *fnt;
781         int i;
782
783         fnt = Fonts;
784
785         for (i=0; i<Num_fonts; i++) {
786                 if (fnt->kern_data) {
787                         free(fnt->kern_data);
788                         fnt->kern_data = NULL;
789                 }
790
791                 if (fnt->char_data) {
792                         free(fnt->char_data);
793                         fnt->char_data = NULL;
794                 }
795
796                 if (fnt->pixel_data) {
797                         free(fnt->pixel_data);
798                         fnt->pixel_data = NULL;
799                 }
800
801                 if (fnt->bm_data) {
802                         free(fnt->bm_data);
803                         fnt->bm_data = NULL;
804                 }
805
806
807                 if (fnt->bm_u) {
808                         free(fnt->bm_u);
809                         fnt->bm_u = NULL;
810                 }
811
812                 if (fnt->bm_v) {
813                         free(fnt->bm_v);
814                         fnt->bm_v = NULL;
815                 }
816
817                 fnt++;
818         }
819 }
820
821 // Returns -1 if couldn't init font, otherwise returns the
822 // font id number.
823 int gr_create_font(char * typeface)
824 {
825         CFILE *fp;
826         font *fnt;
827         int n, fontnum;
828
829         fnt = Fonts;
830         n = -1;
831         for (fontnum=0; fontnum<Num_fonts; fontnum++ )  {
832                 if (fnt->id != 0 )      {
833                         if ( !_strnicmp( fnt->filename, typeface, MAX_FILENAME_LEN ) )  {
834                                 return fontnum;
835                         }
836                 } else {
837                         if ( n < 0 )    {
838                                 n = fontnum;
839                         }                               
840                 }
841                 fnt++;
842         }
843
844         if ( fontnum==MAX_FONTS )       {
845                 Error( LOCATION, "Too many fonts!\nSee John, or change MAX_FONTS in Graphics\\Font.h\n" );
846         }
847
848         if ( fontnum == Num_fonts )     {
849                 Num_fonts++;
850         }
851         
852         bool localize = true;
853
854         fp = cfopen( typeface, "rb", CFILE_NORMAL, CF_TYPE_ANY, localize );
855         if ( fp == NULL ) return -1;
856
857         strncpy( fnt->filename, typeface, MAX_FILENAME_LEN );
858         cfread( &fnt->id, 4, 1, fp );
859         cfread( &fnt->version, sizeof(int), 1, fp );
860         cfread( &fnt->num_chars, sizeof(int), 1, fp );
861         cfread( &fnt->first_ascii, sizeof(int), 1, fp );
862         cfread( &fnt->w, sizeof(int), 1, fp );
863         cfread( &fnt->h, sizeof(int), 1, fp );
864         cfread( &fnt->num_kern_pairs, sizeof(int), 1, fp );
865         cfread( &fnt->kern_data_size, sizeof(int), 1, fp );
866         cfread( &fnt->char_data_size, sizeof(int), 1, fp );
867         cfread( &fnt->pixel_data_size, sizeof(int), 1, fp );
868
869         if ( fnt->kern_data_size )      {
870                 fnt->kern_data = (font_kernpair *)malloc( fnt->kern_data_size );
871                 Assert(fnt->kern_data!=NULL);
872                 cfread( fnt->kern_data, fnt->kern_data_size, 1, fp );
873         } else {
874                 fnt->kern_data = NULL;
875         }
876         if ( fnt->char_data_size )      {
877                 fnt->char_data = (font_char *)malloc( fnt->char_data_size );
878                 Assert( fnt->char_data != NULL );
879                 cfread( fnt->char_data, fnt->char_data_size, 1, fp );
880         } else {
881                 fnt->char_data = NULL;
882         }
883         if ( fnt->pixel_data_size )     {
884                 fnt->pixel_data = (ubyte *)malloc( fnt->pixel_data_size );
885                 Assert(fnt->pixel_data!=NULL);
886                 cfread( fnt->pixel_data, fnt->pixel_data_size, 1, fp );
887         } else {
888                 fnt->pixel_data = NULL;
889         }
890         cfclose(fp);
891
892         // Create a bitmap for hardware cards.
893         // JAS:  Try to squeeze this into the smallest square power of two texture.
894         // This should probably be done at font generation time, not here.
895         int w, h;
896         if ( fnt->pixel_data_size < 64*64 )     {
897                 w = h = 64;
898         } else if ( fnt->pixel_data_size < 128*128 )    {
899                 w = h = 128;
900         } else {
901                 w = h = 256;
902         }
903
904         fnt->bm_w = w;
905         fnt->bm_h = h;
906         fnt->bm_data = (ubyte *)malloc(fnt->bm_w*fnt->bm_h);
907         fnt->bm_u = (int *)malloc(sizeof(int)*fnt->num_chars);
908         fnt->bm_v = (int *)malloc(sizeof(int)*fnt->num_chars);
909
910         int i,x,y;
911         x = y = 0;
912         for (i=0; i<fnt->num_chars; i++ )       {
913                 ubyte * fp;
914                 int x1, y1;
915                 fp = &fnt->pixel_data[fnt->char_data[i].offset];
916                 if ( x + fnt->char_data[i].byte_width >= fnt->bm_w )    {
917                         x = 0;
918                         y += fnt->h;
919                         if ( y+fnt->h > fnt->bm_h ) {
920                                 Error( LOCATION, "Font too big!\n" );
921                         }
922                 }
923                 fnt->bm_u[i] = x;
924                 fnt->bm_v[i] = y;
925
926                 for( y1=0; y1<fnt->h; y1++ )    {
927                         for (x1=0; x1<fnt->char_data[i].byte_width; x1++ )      {
928                                 uint c = *fp++;
929                                 if ( c > 14 ) c = 14;
930                                 fnt->bm_data[(x+x1)+(y+y1)*fnt->bm_w] = (unsigned char)(c);     
931                         }
932                 }
933                 x += fnt->char_data[i].byte_width;
934         }
935
936         fnt->bitmap_id = bm_create( 8, fnt->bm_w, fnt->bm_h, fnt->bm_data, BMP_AABITMAP );
937
938         return fontnum;
939 }
940
941 void grx_set_font(int fontnum)
942 {
943         if ( fontnum < 0 ) {
944                 Current_font = NULL;
945                 return;
946         }
947
948         if ( fontnum >= 0 ) {
949                 Current_font = &Fonts[fontnum];
950         }
951 }
952
953 void gr_font_init()
954 {
955         gr_init_font( NOX("font01.vf") );
956         gr_init_font( NOX("font02.vf") );
957 #ifndef MAKE_FS1
958         gr_init_font( NOX("font03.vf") );
959 #endif
960 }
961
962 // Returns -1 if couldn't init font, otherwise returns the
963 // font id number.
964 int gr_init_font(char * typeface)
965 {
966         int Loaded_fontnum;
967
968         Loaded_fontnum = gr_create_font(typeface);
969
970         Assert( Loaded_fontnum > -1 );
971
972         gr_set_font( Loaded_fontnum );
973
974         return Loaded_fontnum;
975 }
976