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 the
6  * source.
7  *
8 */
9 
10 
11 
12 #ifdef _WIN32
13 #include <windows.h>
14 #include <windowsx.h>
15 #endif
16 
17 #include <stdio.h>
18 #include <stdarg.h>
19 
20 #include "graphics/2d.h"
21 #include "cfile/cfile.h"
22 #include "graphics/font.h"
23 #include "palman/palman.h"
24 #include "io/key.h"
25 #include "bmpman/bmpman.h"
26 #include "localization/localize.h"
27 #include "parse/parselo.h"
28 #include "globalincs/systemvars.h"
29 #include "globalincs/def_files.h"
30 
31 
32 
33 int Num_fonts = 0;
34 font Fonts[MAX_FONTS];
35 font *Current_font = NULL;
36 
37 
38 /**
39  * Crops a string if required to force it to not exceed max_width pixels when printed.
40  * Does this by dropping characters at the end of the string and adding '...' to the end.
41  *
42  * @param str		string to crop.  Modifies this string directly
43  * @param max_str	max characters allowed in str
44  * @param max_width number of pixels to limit string to (less than or equal to).
45  * @return			Returns same pointer passed in for str.
46  */
gr_force_fit_string(char * str,int max_str,int max_width)47 char *gr_force_fit_string(char *str, int max_str, int max_width)
48 {
49 	int w;
50 
51 	gr_get_string_size(&w, NULL, str);
52 	if (w > max_width) {
53 		if ((int) strlen(str) > max_str - 3) {
54 			Assert(max_str >= 3);
55 			str[max_str - 3] = 0;
56 		}
57 
58 		strcpy(str + strlen(str) - 1, "...");
59 		gr_get_string_size(&w, NULL, str);
60 		while (w > max_width) {
61 			Assert(strlen(str) >= 4);  // if this is hit, a bad max_width was passed in and the calling function needs fixing.
62 			strcpy(str + strlen(str) - 4, "...");
63 			gr_get_string_size(&w, NULL, str);
64 		}
65 	}
66 
67 	return str;
68 }
69 
70 /**
71  * Takes the character BEFORE being offset into current font
72  * @return the letter code
73  */
get_char_width(ubyte c1,ubyte c2,int * width,int * spacing)74 int get_char_width(ubyte c1,ubyte c2,int *width,int *spacing)
75 {
76 	int i, letter;
77 
78 	Assert ( Current_font != NULL );
79 	letter = c1-Current_font->first_ascii;
80 
81 	if (letter<0 || letter>=Current_font->num_chars) {				//not in font, draw as space
82 		*width=0;
83 		*spacing = Current_font->w;
84 		return -1;
85 	}
86 
87 	*width = Current_font->char_data[letter].byte_width;
88 	*spacing = Current_font->char_data[letter].spacing;
89 
90 	i = Current_font->char_data[letter].kerning_entry;
91 	if ( i > -1)  {
92 		if (!(c2==0 || c2=='\n')) {
93 			int letter2;
94 
95 			letter2 = c2-Current_font->first_ascii;
96 
97 			if ((letter2>=0) && (letter2<Current_font->num_chars) ) {				//not in font, draw as space
98 				font_kernpair	*k = &Current_font->kern_data[i];
99 				while( (k->c1 == (char)letter) && (k->c2<(char)letter2) && (i<Current_font->num_kern_pairs-1) )	{
100 					i++;
101 					k++;
102 				}
103 				if ( k->c2 == (char)letter2 )	{
104 					*spacing += k->offset;
105 				}
106 			}
107 		}
108 	}
109 	return letter;
110 }
111 
112 // NOTE: this returns an unscaled size for non-standard resolutions
get_centered_x(const char * s,bool scaled)113 int get_centered_x(const char *s, bool scaled)
114 {
115 	int w,w2,s2;
116 
117 	for (w=0;*s!=0 && *s!='\n';s++) {
118 		get_char_width(s[0],s[1],&w2,&s2);
119 		w += s2;
120 	}
121 
122 	return (((scaled ? gr_screen.clip_width : gr_screen.clip_width_unscaled) - w) / 2);
123 }
124 
125 /**
126  * Draws a character centered on x
127  */
gr_char_centered(int x,int y,char chr,int resize_mode)128 void gr_char_centered(int x, int y, char chr, int resize_mode)
129 {
130 	char str[2];
131 	int w, sc;
132 
133 	sc = Lcl_special_chars;
134 	if (chr == '1')
135 		chr = (char)(sc + 1);
136 
137 	str[0] = chr;
138 	str[1] = 0;
139 	gr_get_string_size(&w, NULL, str);
140 	gr_string(x - w / 2, y, str, resize_mode);
141 }
142 
gr_print_timestamp(int x,int y,fix timestamp,int resize_mode)143 void gr_print_timestamp(int x, int y, fix timestamp, int resize_mode)
144 {
145 	char h[2], m[3], s[3];
146 	int w, c;
147 
148 	int time = (int)f2fl(timestamp);  // convert to seconds
149 
150 	// format the time information into strings
151 	sprintf(h, "%.1d", (time / 3600));
152 	sprintf(m, "%.2d", (time / 60) % 60);
153 	sprintf(s, "%.2d", time % 60);
154 
155 	gr_get_string_size(&w, NULL, "0");
156 	gr_get_string_size(&c, NULL, ":");
157 
158 	gr_string(x + w, y, ":", resize_mode);
159 	gr_string(x + w * 3 + c, y, ":", resize_mode);
160 
161 	x += w / 2;
162 	gr_char_centered(x, y, h[0], resize_mode);
163 	x += w + c;
164 	gr_char_centered(x, y, m[0], resize_mode);
165 	x += w;
166 	gr_char_centered(x, y, m[1], resize_mode);
167 	x += w + c;
168 	gr_char_centered(x, y, s[0], resize_mode);
169 	x += w;
170 	gr_char_centered(x, y, s[1], resize_mode);
171 }
172 
gr_get_font_height()173 int gr_get_font_height()
174 {
175 	if (Current_font)	{
176 		return Current_font->h;
177 	} else {
178 		return 16;
179 	}
180 }
181 
gr_get_string_size(int * w1,int * h1,const char * text,int len)182 void gr_get_string_size(int *w1, int *h1, const char *text, int len)
183 {
184 	int longest_width;
185 	int width,spacing;
186 	int w, h;
187 
188 	if (!Current_font)	{
189 		if ( w1)
190 			*w1 = 16;
191 
192 		if ( h1 )
193 			*h1 = 16;
194 
195 		return;
196 	}
197 
198 	w = 0;
199 	h = 0;
200 	longest_width = 0;
201 
202 	if (text != NULL ) {
203 		h += Current_font->h;
204 		while (*text && (len>0) ) {
205 
206 			// Process one or more
207 			while ((*text == '\n') && (len>0) ) {
208 				text++;
209 				len--;
210 				if ( *text )	{
211 					h += Current_font->h;
212 				}
213 				w = 0;
214 			}
215 
216 			if (*text == 0)	{
217 				break;
218 			}
219 
220 			get_char_width(text[0], text[1], &width, &spacing);
221 			w += spacing;
222 			if (w > longest_width)
223 				longest_width = w;
224 
225 			text++;
226 			len--;
227 		}
228 	}
229 
230 	if ( h1 )
231 		*h1 = h;
232 
233 	if ( w1 )
234 		*w1 = longest_width;
235 }
236 
237 
238 MONITOR( FontChars )
239 
240 #ifdef _WIN32
241 HFONT MyhFont = NULL;
242 HDC hDibDC = NULL;
243 
gr_string_win(int x,int y,const char * s)244 void gr_string_win(int x, int y, const char *s)
245 {
246 	int old_bitmap = gr_screen.current_bitmap;
247 	gr_set_font(FONT1);
248    	gr_string(x,y,s);
249 	gr_screen.current_bitmap = old_bitmap;
250 }
251 
gr_get_string_size_win(int * w,int * h,const char * text)252 void gr_get_string_size_win(int *w, int *h, const char *text)
253 {
254 	const char *ptr;
255 	SIZE size;
256 
257 	ptr = strchr(text, '\n');
258 
259 	if (MyhFont==NULL)	{
260 		if (w) *w = 0;
261 		if (h) *h = 0;
262 		return;
263 	}
264 
265 	SelectObject( hDibDC, MyhFont );
266 
267 	if (!ptr)	{
268 		GetTextExtentPoint32( hDibDC, text, strlen(text), &size);
269 		if (w) *w = size.cx;
270 		if (h) *h = size.cy;
271 		return;
272 	}
273 
274 	GetTextExtentPoint32(hDibDC, text, ptr - text, &size);
275 	gr_get_string_size_win(w, h, ptr+1);
276 	if (w && (size.cx > *w) )
277 		*w = size.cx;
278 
279 	if (h)
280 		*h += size.cy;
281 }
282 #endif   // ifdef _WIN32
283 
284 char grx_printf_text[2048];
285 
gr_printf(int x,int y,const char * format,...)286 void _cdecl gr_printf( int x, int y, const char * format, ... )
287 {
288 	va_list args;
289 
290 	if ( !Current_font ) return;
291 
292 	va_start(args, format);
293 	vsprintf(grx_printf_text,format,args);
294 	va_end(args);
295 
296 	gr_string(x,y,grx_printf_text);
297 }
298 
gr_printf_menu(int x,int y,const char * format,...)299 void _cdecl gr_printf_menu( int x, int y, const char * format, ... )
300 {
301 	va_list args;
302 
303 	if ( !Current_font ) return;
304 
305 	va_start(args, format);
306 	vsprintf(grx_printf_text,format,args);
307 	va_end(args);
308 
309 	gr_string(x,y,grx_printf_text,GR_RESIZE_MENU);
310 }
311 
gr_printf_no_resize(int x,int y,const char * format,...)312 void _cdecl gr_printf_no_resize( int x, int y, const char * format, ... )
313 {
314 	va_list args;
315 
316 	if ( !Current_font ) return;
317 
318 	va_start(args, format);
319 	vsprintf(grx_printf_text,format,args);
320 	va_end(args);
321 
322 	gr_string(x,y,grx_printf_text,GR_RESIZE_NONE);
323 }
324 
gr_font_close()325 void gr_font_close()
326 {
327 	font *fnt;
328 	int i;
329 
330 	fnt = Fonts;
331 
332 	for (i=0; i<Num_fonts; i++) {
333 		if (fnt->kern_data) {
334 			vm_free(fnt->kern_data);
335 			fnt->kern_data = NULL;
336 		}
337 
338 		if (fnt->char_data) {
339 			vm_free(fnt->char_data);
340 			fnt->char_data = NULL;
341 		}
342 
343 		if (fnt->pixel_data) {
344 			vm_free(fnt->pixel_data);
345 			fnt->pixel_data = NULL;
346 		}
347 
348 		if (fnt->bm_data) {
349 			vm_free(fnt->bm_data);
350 			fnt->bm_data = NULL;
351 		}
352 
353 		if (fnt->bm_u) {
354 			vm_free(fnt->bm_u);
355 			fnt->bm_u = NULL;
356 		}
357 
358 		if (fnt->bm_v) {
359 			vm_free(fnt->bm_v);
360 			fnt->bm_v = NULL;
361 		}
362 
363 		fnt++;
364 	}
365 }
366 
367 /**
368  * @return -1 if couldn't init font, otherwise returns the font id number.
369  */
gr_create_font(char * typeface)370 int gr_create_font(char * typeface)
371 {
372 	CFILE *fp;
373 	font *fnt;
374 	int n, fontnum;
375 
376 	fnt = Fonts;
377 	n = -1;
378 	for (fontnum=0; fontnum<Num_fonts; fontnum++ )	{
379 		if (fnt->id != 0 )	{
380 			if ( !_strnicmp( fnt->filename, typeface, MAX_FILENAME_LEN ) )	{
381 				return fontnum;
382 			}
383 		} else {
384 			if ( n < 0 )	{
385 				n = fontnum;
386 			}
387 		}
388 		fnt++;
389 	}
390 
391 	if ( fontnum == MAX_FONTS )	{
392 		Warning( LOCATION, "Too many fonts!\nSee John, or change MAX_FONTS in Graphics\\Font.h\n" );
393 		return -1;
394 	}
395 
396 	if ( fontnum == Num_fonts )	{
397 		Num_fonts++;
398 	}
399 
400 	bool localize = true;
401 
402 	fp = cfopen( typeface, "rb", CFILE_NORMAL, CF_TYPE_ANY, localize );
403 	if ( fp == NULL ) return -1;
404 
405 	strcpy_s( fnt->filename, typeface );
406 	cfread( &fnt->id, 4, 1, fp );
407 	cfread( &fnt->version, sizeof(int), 1, fp );
408 	cfread( &fnt->num_chars, sizeof(int), 1, fp );
409 	cfread( &fnt->first_ascii, sizeof(int), 1, fp );
410 	cfread( &fnt->w, sizeof(int), 1, fp );
411 	cfread( &fnt->h, sizeof(int), 1, fp );
412 	cfread( &fnt->num_kern_pairs, sizeof(int), 1, fp );
413 	cfread( &fnt->kern_data_size, sizeof(int), 1, fp );
414 	cfread( &fnt->char_data_size, sizeof(int), 1, fp );
415 	cfread( &fnt->pixel_data_size, sizeof(int), 1, fp );
416 
417 	fnt->id = INTEL_SHORT( fnt->id ); //-V570
418 	fnt->version = INTEL_INT( fnt->version ); //-V570
419 	fnt->num_chars = INTEL_INT( fnt->num_chars ); //-V570
420 	fnt->first_ascii = INTEL_INT( fnt->first_ascii ); //-V570
421 	fnt->w = INTEL_INT( fnt->w ); //-V570
422 	fnt->h = INTEL_INT( fnt->h ); //-V570
423 	fnt->num_kern_pairs = INTEL_INT( fnt->num_kern_pairs ); //-V570
424 	fnt->kern_data_size = INTEL_INT( fnt->kern_data_size ); //-V570
425 	fnt->char_data_size = INTEL_INT( fnt->char_data_size ); //-V570
426 	fnt->pixel_data_size = INTEL_INT( fnt->pixel_data_size ); //-V570
427 
428 	if ( fnt->kern_data_size )	{
429 		fnt->kern_data = (font_kernpair *)vm_malloc( fnt->kern_data_size );
430 		Assert(fnt->kern_data!=NULL);
431 		cfread( fnt->kern_data, fnt->kern_data_size, 1, fp );
432 	} else {
433 		fnt->kern_data = NULL;
434 	}
435 	if ( fnt->char_data_size )	{
436 		fnt->char_data = (font_char *)vm_malloc( fnt->char_data_size );
437 		Assert( fnt->char_data != NULL );
438 		cfread( fnt->char_data, fnt->char_data_size, 1, fp );
439 
440 		for (int i=0; i<fnt->num_chars; i++) {
441 			fnt->char_data[i].spacing = INTEL_INT( fnt->char_data[i].spacing ); //-V570
442 			fnt->char_data[i].byte_width = INTEL_INT( fnt->char_data[i].byte_width ); //-V570
443 			fnt->char_data[i].offset = INTEL_INT( fnt->char_data[i].offset ); //-V570
444 			fnt->char_data[i].kerning_entry = INTEL_INT( fnt->char_data[i].kerning_entry ); //-V570
445 			fnt->char_data[i].user_data = INTEL_SHORT( fnt->char_data[i].user_data ); //-V570
446 		}
447 	} else {
448 		fnt->char_data = NULL;
449 	}
450 	if ( fnt->pixel_data_size )	{
451 		fnt->pixel_data = (ubyte *)vm_malloc( fnt->pixel_data_size );
452 		Assert(fnt->pixel_data!=NULL);
453 		cfread( fnt->pixel_data, fnt->pixel_data_size, 1, fp );
454 	} else {
455 		fnt->pixel_data = NULL;
456 	}
457 	cfclose(fp);
458 
459 	// Create a bitmap for hardware cards.
460 	// JAS:  Try to squeeze this into the smallest square power of two texture.
461 	// This should probably be done at font generation time, not here.
462 	int w, h;
463 	if ( fnt->pixel_data_size*4 < 64*64 ) {
464 		w = h = 64;
465 	} else if ( fnt->pixel_data_size*4 < 128*128 ) {
466 		w = h = 128;
467 	} else if ( fnt->pixel_data_size*4 < 256*256 ) {
468 		w = h = 256;
469 	} else if ( fnt->pixel_data_size*4 < 512*512 ) {
470 		w = h = 512;
471 	} else {
472 		w = h = 1024;
473 	}
474 
475 	fnt->bm_w = w;
476 	fnt->bm_h = h;
477 	fnt->bm_data = (ubyte *)vm_malloc(fnt->bm_w*fnt->bm_h);
478 	fnt->bm_u = (int *)vm_malloc(sizeof(int)*fnt->num_chars);
479 	fnt->bm_v = (int *)vm_malloc(sizeof(int)*fnt->num_chars);
480 
481 	memset( fnt->bm_data, 0, fnt->bm_w * fnt->bm_h );
482 
483 	int i,x,y;
484 	x = y = 0;
485 	for (i=0; i<fnt->num_chars; i++ )	{
486 		ubyte * ubp;
487 		int x1, y1;
488 		ubp = &fnt->pixel_data[fnt->char_data[i].offset];
489 		if ( x + fnt->char_data[i].byte_width >= fnt->bm_w )	{
490 			x = 0;
491 			y += fnt->h + 2;
492 			if ( y+fnt->h > fnt->bm_h ) {
493 				Error( LOCATION, "Font too big!\n" );
494 			}
495 		}
496 		fnt->bm_u[i] = x;
497 		fnt->bm_v[i] = y;
498 
499 		for( y1=0; y1<fnt->h; y1++ )	{
500 			for (x1=0; x1<fnt->char_data[i].byte_width; x1++ )	{
501 				uint c = *ubp++;
502 				if ( c > 14 ) c = 14;
503 				fnt->bm_data[(x+x1)+(y+y1)*fnt->bm_w] = (unsigned char)(c);
504 			}
505 		}
506 		x += fnt->char_data[i].byte_width + 2;
507 	}
508 
509 	fnt->bitmap_id = bm_create( 8, fnt->bm_w, fnt->bm_h, fnt->bm_data, BMP_AABITMAP );
510 
511 	return fontnum;
512 }
513 
gr_get_current_fontnum()514 int gr_get_current_fontnum()
515 {
516 	if (Current_font == NULL) {
517 		return -1;
518 	} else {
519 		return (Current_font - &Fonts[0]);
520 	}
521 }
522 
gr_get_fontnum(char * filename)523 int gr_get_fontnum(char *filename)
524 {
525 	int i;
526 	for(i = 0; i < Num_fonts; i++)
527 	{
528 		if(!strextcmp(Fonts[i].filename, filename))
529 			return i;
530 	}
531 
532 	return -1;
533 }
534 
gr_set_font(int fontnum)535 void gr_set_font(int fontnum)
536 {
537 	if ( fontnum < 0 ) {
538 		Current_font = NULL;
539 		return;
540 	}
541 
542 	if ( fontnum >= 0 && fontnum < Num_fonts) {
543 		Current_font = &Fonts[fontnum];
544 	}
545 }
546 
parse_fonts_tbl(char * only_parse_first_font,size_t only_parse_first_font_size)547 void parse_fonts_tbl(char *only_parse_first_font, size_t only_parse_first_font_size)
548 {
549 	int rval;
550 	char *filename;
551 
552 	// choose file name
553 	// (this can be done within the function, as opposed to being passed as a parameter,
554 	// because fonts.tbl doesn't have a modular counterpart)
555 	if ( cf_exists_full("fonts.tbl", CF_TYPE_TABLES) ) {
556 		filename = "fonts.tbl";
557 	} else {
558 		filename = NULL;
559 	}
560 
561 	if ((rval = setjmp(parse_abort)) != 0) {
562 		mprintf(("TABLES: Unable to parse '%s'!  Error code = %i.\n", (filename) ? filename : NOX("<default fonts.tbl>"), rval));
563 		return;
564 	}
565 
566 	if (filename != NULL) {
567 		read_file_text(filename, CF_TYPE_TABLES);
568 	} else {
569 		read_file_text_from_array(defaults_get_file("fonts.tbl"));
570 	}
571 
572 	reset_parse();
573 
574 	// start parsing
575 	required_string("#Fonts");
576 
577 	// read fonts
578 	while (required_string_either("#End","$Font:")) {
579 		char font_filename[MAX_FILENAME_LEN];
580 
581 		// grab font
582 		required_string("$Font:");
583 		stuff_string(font_filename, F_NAME, MAX_FILENAME_LEN);
584 
585 		// if we only need the first font, copy it and bail
586 		if (only_parse_first_font != NULL) {
587 			strcpy_s(only_parse_first_font, only_parse_first_font_size, font_filename);
588 			return;
589 		}
590 
591 		// create font
592 		int font_id = gr_create_font(font_filename);
593 		if (font_id < 0) {
594 			Warning(LOCATION, "Could not create font from typeface '%s'!", font_filename);
595 		}
596 	}
597 
598 	// done parsing
599 	required_string("#End");
600 
601 	// double check
602 	if (Num_fonts < 3) {
603 		Error(LOCATION, "There must be at least three fonts in %s!", (filename) ? filename : NOX("<default fonts.tbl>"));
604 	}
605 }
606 
gr_stuff_first_font(char * first_font,size_t first_font_size)607 void gr_stuff_first_font(char *first_font, size_t first_font_size )
608 {
609 	parse_fonts_tbl( first_font, first_font_size );
610 }
611 
gr_font_init()612 void gr_font_init()
613 {
614 	parse_fonts_tbl( NULL, 0 );
615 	gr_set_font(0);
616 }
617