1 /*
2  * fntconv.c : convert Gemdos fonts between FNT, TXT and C formats
3  *
4  * Copyright (c) 2001 Laurent Vogel
5  * Copyright (c) 2017 Thorsten Otto
6  *
7  * Authors:
8  *  LVL     Laurent Vogel
9  *  THO     Thorsten Otto
10  *
11  * This file is distributed under the GPL, version 2 or at your
12  * option any later version.  See doc/license.txt for details.
13  */
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <stdarg.h>
19 #include <getopt.h>
20 
21 typedef unsigned char UBYTE;
22 typedef signed char BYTE;
23 typedef unsigned short UWORD;
24 typedef short WORD;
25 typedef unsigned long ULONG;
26 typedef long LONG;
27 
28 #define FILE_C 1
29 #define FILE_TXT 2
30 #define FILE_FNT 3
31 #define FILE_BMP 4
32 static int convert_from;
33 static int convert_to;
34 static int scale = 1;
35 static int grid = 0;
36 
37 #define MAX_ADE 0x7fffl
38 
39 static int all_chars = 0;
40 static int for_aranym = 0;
41 static int do_off_table = 1;
42 static const char *varname = "THISFONT";
43 
44 
45 /*
46  * internal error routine
47  */
48 
fatal(const char * s,...)49 static void fatal(const char *s, ...)
50 {
51 	va_list ap;
52 
53 	va_start(ap, s);
54 	vfprintf(stderr, s, ap);
55 	fprintf(stderr, "\naborted.\n");
56 	va_end(ap);
57 	exit(EXIT_FAILURE);
58 }
59 
60 
61 /*
62  * memory
63  */
xmalloc(size_t s)64 static void *xmalloc(size_t s)
65 {
66 	void *a = calloc(1, s);
67 
68 	if (a == 0)
69 		fatal("memory");
70 	return a;
71 }
72 
73 /*
74  * xstrdup
75  */
76 
xstrdup(const char * s)77 static char *xstrdup(const char *s)
78 {
79 	size_t len = strlen(s);
80 	char *a = xmalloc(len + 1);
81 
82 	strcpy(a, s);
83 	return a;
84 }
85 
86 
87 /*
88  * little/big endian conversion
89  */
90 
get_b_long(void * addr)91 static LONG get_b_long(void *addr)
92 {
93 	UBYTE *uaddr = (UBYTE *) addr;
94 
95 	return (uaddr[0] << 24) + (uaddr[1] << 16) + (uaddr[2] << 8) + uaddr[3];
96 }
97 
98 
set_b_long(void * addr,LONG value)99 static void set_b_long(void *addr, LONG value)
100 {
101 	UBYTE *uaddr = (UBYTE *) addr;
102 
103 	uaddr[0] = value >> 24;
104 	uaddr[1] = value >> 16;
105 	uaddr[2] = value >> 8;
106 	uaddr[3] = value;
107 }
108 
109 
get_b_word(void * addr)110 static WORD get_b_word(void *addr)
111 {
112 	UBYTE *uaddr = (UBYTE *) addr;
113 
114 	return (uaddr[0] << 8) + uaddr[1];
115 }
116 
117 
set_b_word(void * addr,WORD value)118 static void set_b_word(void *addr, WORD value)
119 {
120 	UBYTE *uaddr = (UBYTE *) addr;
121 
122 	uaddr[0] = value >> 8;
123 	uaddr[1] = value;
124 }
125 
get_l_long(void * addr)126 static LONG get_l_long(void *addr)
127 {
128 	UBYTE *uaddr = (UBYTE *) addr;
129 
130 	return (uaddr[3] << 24) + (uaddr[2] << 16) + (uaddr[1] << 8) + uaddr[0];
131 }
132 
133 
set_l_long(void * addr,LONG value)134 static void set_l_long(void *addr, LONG value)
135 {
136 	UBYTE *uaddr = (UBYTE *) addr;
137 
138 	uaddr[3] = value >> 24;
139 	uaddr[2] = value >> 16;
140 	uaddr[1] = value >> 8;
141 	uaddr[0] = value;
142 }
143 
144 
set_l_word(void * addr,WORD value)145 static void set_l_word(void *addr, WORD value)
146 {
147 	UBYTE *uaddr = (UBYTE *) addr;
148 
149 	uaddr[1] = value >> 8;
150 	uaddr[0] = value;
151 }
152 
153 
get_l_word(void * addr)154 static WORD get_l_word(void *addr)
155 {
156 	UBYTE *uaddr = (UBYTE *) addr;
157 
158 	return (uaddr[1] << 8) + uaddr[0];
159 }
160 
161 
162 #if 0
163 static void set_l_word(void *addr, WORD value)
164 {
165 	UBYTE *uaddr = (UBYTE *) addr;
166 
167 	uaddr[1] = value >> 8;
168 	uaddr[0] = value;
169 }
170 #endif
171 
172 
173 /*
174  * fontdef.h - font-header definitions
175  *
176  */
177 
178 /* fh_flags   */
179 
180 #define F_DEFAULT    1					/* this is the default font (face and size) */
181 #define F_HORZ_OFF   2					/* there are left and right offset tables */
182 #define F_STDFORM    4					/* is the font in standard (bigendian) format */
183 #define F_MONOSPACE  8					/* is the font monospaced */
184 #define F_EXT_HDR    32					/* extended header */
185 
186 /* flags that this tool supports */
187 #define F_SUPPORTED (F_STDFORM|F_MONOSPACE|F_DEFAULT|F_HORZ_OFF|F_EXT_HDR)
188 
189 
190 #define F_EXT_HDR_SIZE 62				/* size of an additional extended header */
191 
192 /* style bits */
193 
194 #define F_THICKEN 1
195 #define F_LIGHT	2
196 #define F_SKEW	4
197 #define F_UNDER	8
198 #define F_OUTLINE 16
199 #define F_SHADOW	32
200 
201 struct font_file_hdr
202 {										/* describes a .FNT file header */
203 	UBYTE font_id[2];
204 	UBYTE point[2];
205 	char name[32];
206 	UBYTE first_ade[2];
207 	UBYTE last_ade[2];
208 	UBYTE top[2];
209 	UBYTE ascent[2];
210 	UBYTE half[2];
211 	UBYTE descent[2];
212 	UBYTE bottom[2];
213 	UBYTE max_char_width[2];
214 	UBYTE max_cell_width[2];
215 	UBYTE left_offset[2];				/* amount character slants left when skewed */
216 	UBYTE right_offset[2];				/* amount character slants right */
217 	UBYTE thicken[2];					/* number of pixels to smear */
218 	UBYTE ul_size[2];					/* size of the underline */
219 	UBYTE lighten[2];					/* mask to and with to lighten  */
220 	UBYTE skew[2];						/* mask for skewing */
221 	UBYTE flags[2];
222 
223 	UBYTE hor_table[4];					/* offset of horizontal offsets */
224 	UBYTE off_table[4];					/* offset of character offsets  */
225 	UBYTE dat_table[4];					/* offset of character definitions */
226 	UBYTE form_width[2];
227 	UBYTE form_height[2];
228 	UBYTE next_font[4];
229 };
230 
231 struct font
232 {										/* describes a font in memory */
233 	WORD font_id;
234 	WORD point;
235 	char name[32];
236 	UWORD first_ade;
237 	UWORD last_ade;
238 	UWORD top;
239 	UWORD ascent;
240 	UWORD half;
241 	UWORD descent;
242 	UWORD bottom;
243 	UWORD max_char_width;
244 	UWORD max_cell_width;
245 	UWORD left_offset;					/* amount character slants left when skewed */
246 	UWORD right_offset;					/* amount character slants right */
247 	UWORD thicken;						/* number of pixels to smear */
248 	UWORD ul_size;						/* size of the underline */
249 	UWORD lighten;						/* mask to and with to lighten  */
250 	UWORD skew;							/* mask for skewing */
251 	UWORD flags;
252 
253 	UBYTE *hor_table;					/* horizontal offsets, 2 bytes per character */
254 	unsigned long *off_table;			/* character offsets, 0xFFFF if no char present.  */
255 	UBYTE *dat_table;					/* character definitions */
256 	unsigned long form_width;
257 	UWORD form_height;
258 };
259 
260 #define F_NO_CHAR 0xFFFFu
261 #define F_NO_CHARL 0xFFFFFFFFul
262 
263 
264 
open_output(const char ** filename,const char * mode)265 static FILE *open_output(const char **filename, const char *mode)
266 {
267 	FILE *f;
268 
269 	if (*filename == NULL || strcmp(*filename, "-") == 0)
270 	{
271 		f = fdopen(fileno(stdout), mode);
272 		*filename = "<stdout>";
273 	} else
274 	{
275 		f = fopen(*filename, mode);
276 	}
277 	if (f == NULL)
278 		fatal("can't create %s", *filename);
279 	return f;
280 }
281 
282 
open_input(const char ** filename,const char * mode)283 static FILE *open_input(const char **filename, const char *mode)
284 {
285 	FILE *f;
286 
287 	if (*filename == NULL || strcmp(*filename, "-") == 0)
288 	{
289 		f = fdopen(fileno(stdin), mode);
290 		*filename = "<stdin>";
291 	} else
292 	{
293 		f = fopen(*filename, mode);
294 	}
295 	if (f == NULL)
296 		fatal("can't open %s", *filename);
297 	return f;
298 }
299 
300 
301 /*
302  * text input files
303  */
304 
305 #define BACKSIZ 10
306 #define READSIZ 512
307 
308 typedef struct ifile
309 {
310 	long lineno;
311 	char *fname;
312 	FILE *fh;
313 	UBYTE buf[BACKSIZ + READSIZ];
314 	long size;
315 	long index;
316 	int ateof;
317 } IFILE;
318 
irefill(IFILE * f)319 static void irefill(IFILE *f)
320 {
321 	if (f->size > BACKSIZ)
322 	{
323 		memmove(f->buf, f->buf + f->size - BACKSIZ, BACKSIZ);
324 		f->size = BACKSIZ;
325 		f->index = f->size;
326 	}
327 	f->size += fread(f->buf + f->size, 1, READSIZ, f->fh);
328 }
329 
330 
ifopen(const char * fname)331 static IFILE *ifopen(const char *fname)
332 {
333 	IFILE *f = xmalloc(sizeof(IFILE));
334 
335 	f->fh = open_input(&fname, "rb");
336 	if (f->fh == NULL)
337 	{
338 		free(f);
339 		return NULL;
340 	}
341 	f->fname = xstrdup(fname);
342 	f->size = 0;
343 	f->index = 0;
344 	f->ateof = 0;
345 	f->lineno = 1;
346 	return f;
347 }
348 
349 
ifclose(IFILE * f)350 static void ifclose(IFILE *f)
351 {
352 	fclose(f->fh);
353 	free(f);
354 }
355 
356 
iback(IFILE * f)357 static void iback(IFILE *f)
358 {
359 	if (f->index == 0)
360 	{
361 		fatal("too far backward");
362 	} else
363 	{
364 		f->index--;
365 	}
366 }
367 
368 
igetc(IFILE * f)369 static int igetc(IFILE *f)
370 {
371 	if (f->index >= f->size)
372 	{
373 		irefill(f);
374 		if (f->index >= f->size)
375 		{
376 			f->ateof = 1;
377 			return EOF;
378 		}
379 	}
380 	return f->buf[f->index++];
381 }
382 
383 
384 /* returns the next logical char, in sh syntax */
inextsh(IFILE * f)385 static int inextsh(IFILE *f)
386 {
387 	int ret;
388 
389 	ret = igetc(f);
390 	if (ret == 015)
391 	{
392 		ret = igetc(f);
393 		if (ret == 012)
394 		{
395 			f->lineno++;
396 			return '\n';
397 		} else
398 		{
399 			iback(f);
400 			f->lineno++;
401 			return '\n';
402 		}
403 	} else if (ret == 012)
404 	{
405 		f->lineno++;
406 		return '\n';
407 	} else
408 	{
409 		return ret;
410 	}
411 }
412 
413 
414 /* read a line, ignoring comments, initial white, trailing white,
415  * empty lines and long lines
416  */
igetline(IFILE * f,char * buf,int max)417 static int igetline(IFILE *f, char *buf, int max)
418 {
419 	char *b;
420 	char *bmax = buf + max - 1;
421 	int c;
422 
423   again:
424 	c = inextsh(f);
425 	b = buf;
426 	if (c == '#')
427 	{
428 	  ignore:
429 		while (c != EOF && c != '\n')
430 		{
431 			c = inextsh(f);
432 		}
433 		goto again;
434 	}
435 	while (c == ' ' || c == '\t')
436 	{
437 		c = inextsh(f);
438 	}
439 	while (c != EOF && c != '\n')
440 	{
441 		if (b >= bmax)
442 		{
443 			fprintf(stderr, "file %s, line %ld too long\n", f->fname, f->lineno);
444 			goto ignore;
445 		}
446 		*b++ = c;
447 		c = inextsh(f);
448 	}
449 	/* remove trailing white */
450 	if (b == buf)
451 		return 0;						/* EOF */
452 	b--;
453 	while (b >= buf && (*b == ' ' || *b == '\t'))
454 	{
455 		b--;
456 	}
457 	b++;
458 	*b = 0;
459 	return 1;
460 }
461 
462 
463 /*
464  * functions to try read some patterns.
465  * they look in a string, return 1 if read, 0 if not read.
466  * if the pattern was read, the string pointer is set to the
467  * character immediately after the last character of the pattern.
468  */
469 
470 /* backslash sequences in C strings */
try_backslash(char ** cc,long * val)471 static int try_backslash(char **cc, long *val)
472 {
473 	long ret;
474 	char *c = *cc;
475 
476 	if (*c++ != '\\')
477 		return 0;
478 	switch (*c)
479 	{
480 	case 0:
481 		return 0;
482 	case 'a':
483 		ret = '\a';
484 		c++;
485 		break;
486 	case 'b':
487 		ret = '\b';
488 		c++;
489 		break;
490 	case 'f':
491 		ret = '\f';
492 		c++;
493 		break;
494 	case 'n':
495 		ret = '\n';
496 		c++;
497 		break;
498 	case 'r':
499 		ret = '\r';
500 		c++;
501 		break;
502 	case 't':
503 		ret = '\t';
504 		c++;
505 		break;
506 	case 'v':
507 		ret = '\v';
508 		c++;
509 		break;
510 	case '\\':
511 	case '\'':
512 	case '\"':
513 		ret = *c++;
514 		break;
515 	default:
516 		if (*c >= '0' && *c <= '7')
517 		{
518 			ret = *c++ - '0';
519 			if (*c >= '0' && *c <= '7')
520 			{
521 				ret <<= 3;
522 				ret |= *c++ - '0';
523 				if (*c >= '0' && *c <= '7')
524 				{
525 					ret <<= 3;
526 					ret |= *c++ - '0';
527 				}
528 			}
529 		} else
530 		{
531 			ret = *c++;
532 		}
533 		break;
534 	}
535 	*cc = c;
536 	*val = ret;
537 	return 1;
538 }
539 
540 
try_unsigned(char ** cc,long * val)541 static int try_unsigned(char **cc, long *val)
542 {
543 	long ret;
544 	char *c = *cc;
545 
546 	if (*c == '0')
547 	{
548 		c++;
549 		if (*c == 'x')
550 		{
551 			c++;
552 			ret = 0;
553 			if (*c == 0)
554 				return 0;
555 			while (*c)
556 			{
557 				if (*c >= '0' && *c <= '9')
558 				{
559 					ret <<= 4;
560 					ret |= (*c - '0');
561 				} else if (*c >= 'a' && *c <= 'f')
562 				{
563 					ret <<= 4;
564 					ret |= (*c - 'a' + 10);
565 				} else if (*c >= 'A' && *c <= 'F')
566 				{
567 					ret <<= 4;
568 					ret |= (*c - 'A' + 10);
569 				} else
570 					break;
571 				c++;
572 			}
573 		} else
574 		{
575 			ret = 0;
576 			while (*c >= '0' && *c <= '7')
577 			{
578 				ret <<= 3;
579 				ret |= (*c++ - '0');
580 			}
581 		}
582 	} else if (*c >= '1' && *c <= '9')
583 	{
584 
585 		ret = 0;
586 		while (*c >= '0' && *c <= '9')
587 		{
588 			ret *= 10;
589 			ret += (*c++ - '0');
590 		}
591 	} else if (*c == '\'')
592 	{
593 		c++;
594 		if (try_backslash(&c, &ret))
595 		{
596 			if (*c++ != '\'')
597 				return 0;
598 		} else if (*c == '\'' || *c < 32 || *c >= 127)
599 		{
600 			return 0;
601 		} else
602 		{
603 			ret = (*c++) & 0xFF;
604 			if (*c++ != '\'')
605 				return 0;
606 		}
607 	} else
608 		return 0;
609 	*cc = c;
610 	*val = ret;
611 	return 1;
612 }
613 
614 
615 #if 0
616 static int try_signed(char **cc, long *val)
617 {
618 	char *c = *cc;
619 
620 	if (*c == '-')
621 	{
622 		c++;
623 		if (try_unsigned(&c, val))
624 		{
625 			*val = -*val;
626 			*cc = c;
627 			return 1;
628 		} else
629 		{
630 			return 0;
631 		}
632 	} else
633 	{
634 		return try_unsigned(cc, val);
635 	}
636 }
637 #endif
638 
639 
try_given_string(char ** cc,char * s)640 static int try_given_string(char **cc, char *s)
641 {
642 	size_t n = strlen(s);
643 
644 	if (!strncmp(*cc, s, n))
645 	{
646 		*cc += n;
647 		return 1;
648 	} else
649 	{
650 		return 0;
651 	}
652 }
653 
654 
try_c_string(char ** cc,char * s,int max)655 static int try_c_string(char **cc, char *s, int max)
656 {
657 	char *c = *cc;
658 	char *smax = s + max - 1;
659 	long u;
660 
661 	if (*c != '"')
662 	{
663 		fprintf(stderr, "c='%c'\n", *c);
664 		return 0;
665 	}
666 	c++;
667 	while (*c != '"')
668 	{
669 		if (*c == 0)
670 		{
671 			fprintf(stderr, "c='%c'\n", *c);
672 			return 0;
673 		}
674 		if (s >= smax)
675 		{
676 			fprintf(stderr, "c='%c'\n", *c);
677 			return 0;
678 		}
679 		if (try_backslash(&c, &u))
680 		{
681 			*s++ = u;
682 		} else
683 		{
684 			*s++ = *c++;
685 		}
686 	}
687 	c++;
688 	*s++ = 0;
689 	*cc = c;
690 	return 1;
691 }
692 
693 
try_white(char ** cc)694 static int try_white(char **cc)
695 {
696 	char *c = *cc;
697 
698 	if (*c == ' ' || *c == '\t')
699 	{
700 		c++;
701 		while (*c == ' ' || *c == '\t')
702 			c++;
703 		*cc = c;
704 		return 1;
705 	} else
706 		return 0;
707 }
708 
709 
try_eol(char ** cc)710 static int try_eol(char **cc)
711 {
712 	return (**cc == 0) ? 1 : 0;
713 }
714 
715 
716 /*
717  * simple bitmap read/write
718  */
719 
get_bit(UBYTE * addr,long i)720 static int get_bit(UBYTE *addr, long i)
721 {
722 	return (addr[i / 8] & (1 << (7 - (i & 7)))) ? 1 : 0;
723 }
724 
set_bit(UBYTE * addr,long i)725 static void set_bit(UBYTE *addr, long i)
726 {
727 	addr[i / 8] |= (1 << (7 - (i & 7)));
728 }
729 
730 
get_width(struct font * p,unsigned short ch)731 static unsigned long get_width(struct font *p, unsigned short ch)
732 {
733 	unsigned short next;
734 	unsigned short bmnum = p->last_ade - p->first_ade + 1;
735 	unsigned long off;
736 
737 	off = p->off_table[ch];
738 	if (off != F_NO_CHARL)
739 	{
740 		for (next = ch + 1; next <= bmnum; next++)
741 		{
742 			unsigned long nextoff = p->off_table[next];
743 			if (nextoff != F_NO_CHARL)
744 				return nextoff - off;
745 		}
746 		off = F_NO_CHARL;
747 	}
748 	return off;
749 }
750 
751 
check_monospaced(struct font * p,const char * filename)752 static void check_monospaced(struct font *p, const char *filename)
753 {
754 	int i, bmnum;
755 	int mono = 1;
756 	unsigned long width, firstwidth;
757 
758 	bmnum = p->last_ade - p->first_ade + 1;
759 	firstwidth = get_width(p, 0);
760 	for (i = 0; i < bmnum; i++)
761 	{
762 		width = get_width(p, i);
763 		if (width == F_NO_CHARL)
764 			continue;
765 		if (width != 0 && width != firstwidth)
766 		{
767 			mono = 0;
768 			if (p->flags & F_MONOSPACE)
769 				fprintf(stderr, "%s: warning: font says it is monospaced, but isn't\n", filename);
770 			p->flags &= ~F_MONOSPACE;
771 			return;
772 		}
773 	}
774 	if (mono && !(p->flags & F_MONOSPACE))
775 	{
776 		fprintf(stderr, "%s: warning: font does not say it is monospaced, but is\n", filename);
777 		p->flags |= F_MONOSPACE;
778 	}
779 }
780 
781 
782 /*
783  * read functions
784  */
785 
read_txt(const char * fname)786 static struct font *read_txt(const char *fname)
787 {
788 	IFILE *f;
789 	int ch, i, j, k, lastch;
790 	unsigned long off;
791 	int height;
792 	unsigned long width = 0;
793 	int w;
794 	UBYTE *bms;
795 	int bmsize, bmnum;
796 	int first, last;
797 	UBYTE *b;
798 	char *c;
799 	long u;
800 	char line[200];
801 	struct font *p;
802 	int max = 80;
803 
804 	p = xmalloc(sizeof(*p));
805 	if (p == NULL)
806 		fatal("memory");
807 	f = ifopen(fname);
808 
809 #define EXPECT(a) \
810   if(!igetline(f, line, max) || strcmp(line, a) != 0) \
811   { \
812     fprintf(stderr, "\"%s\" expected\n", a); \
813   	goto fail; \
814   }
815 
816 	EXPECT("GDOSFONT");
817 	EXPECT("version 1.0");
818 
819 #define EXPECTNUM(a) c=line; \
820   if(!igetline(f, line, max) || !try_given_string(&c, #a) \
821     || !try_white(&c) || !try_unsigned(&c, &u) || !try_eol(&c)) \
822   { \
823     fprintf(stderr, "\"%s\" with number expected\n", #a); \
824     goto fail; \
825   } \
826   p->a = u;
827 
828 	EXPECTNUM(font_id);
829 	EXPECTNUM(point);
830 
831 	c = line;
832 	if (!igetline(f, line, max) || !try_given_string(&c, "name")
833 		|| !try_white(&c) || !try_c_string(&c, p->name, 32) || !try_eol(&c))
834     {
835     	fprintf(stderr, "\"%s\" expected\n", "name");
836 		goto fail;
837 	}
838 
839 	EXPECTNUM(first_ade);
840 	EXPECTNUM(last_ade);
841 	EXPECTNUM(top);
842 	EXPECTNUM(ascent);
843 	EXPECTNUM(half);
844 	EXPECTNUM(descent);
845 	EXPECTNUM(bottom);
846 	EXPECTNUM(max_char_width);
847 	EXPECTNUM(max_cell_width);
848 	EXPECTNUM(left_offset);
849 	EXPECTNUM(right_offset);
850 	EXPECTNUM(thicken);
851 	EXPECTNUM(ul_size);
852 	EXPECTNUM(lighten);
853 	EXPECTNUM(skew);
854 	EXPECTNUM(flags);
855 	EXPECTNUM(form_height);
856 
857 #undef EXPECTNUM
858 
859 	if (p->flags & F_HORZ_OFF)
860 	{
861 		fatal("horizontal offsets not handled");
862 	}
863 
864 	first = p->first_ade;
865 	last = p->last_ade;
866 	height = p->form_height;
867 	if (first < 0 || last < 0 || (long)first > MAX_ADE || first > last)
868 	{
869 		fatal("wrong char range : first = %d, last = %d", first, last);
870 	}
871 	if (p->max_cell_width >= 40 || p->form_height >= 40)
872 	{
873 		fatal("unreasonable font size %dx%d", p->max_cell_width, height);
874 	}
875 
876 	/* allocate a big buffer to hold all bitmaps */
877 	bmnum = last - first + 1;
878 	bmsize = (p->max_cell_width * height + 7) >> 3;
879 	bms = xmalloc((size_t)bmsize * bmnum);
880 	p->off_table = xmalloc(sizeof(*p->off_table) * (bmnum + 1));
881 	for (i = 0; i < bmnum; i++)
882 	{
883 		p->off_table[i] = F_NO_CHARL;
884 	}
885 
886 	lastch = 0;
887 	for (;;)
888 	{									/* for each char */
889 		c = line;
890 		if (!igetline(f, line, max))
891 		{
892 	    	fprintf(stderr, "unexpected EOF\n");
893 			goto fail;
894 		}
895 		if (strcmp(line, "endfont") == 0)
896 			break;
897 		if (!try_given_string(&c, "char") || !try_white(&c) || !try_unsigned(&c, &u) || !try_eol(&c))
898 		{
899 	    	fprintf(stderr, "\"%s\" with number expected\n", "char");
900 			goto fail;
901 		}
902 		ch = (int)u;
903 		if (ch < first || ch > last)
904 		{
905 			fprintf(stderr, "wrong character number 0x%x\n", ch);
906 			goto fail;
907 		}
908 		if (ch < lastch)
909 			fprintf(stderr, "%s:%ld: warning: char 0x%x less previous char 0x%x\n", fname, f->lineno, ch, lastch);
910 		lastch = ch;
911 
912 		ch -= first;
913 		if (p->off_table[ch] != F_NO_CHARL)
914 		{
915 			fprintf(stderr, "%s:%ld: character number 0x%x was already defined\n", fname, f->lineno, lastch);
916 			goto fail;
917 		}
918 		b = bms + ch * bmsize;
919 
920 		k = 0;
921 		for (i = 0; i < height; i++)
922 		{
923 			if (!igetline(f, line, max))
924 			{
925 		    	fprintf(stderr, "unexpected EOF\n");
926 				goto fail;
927 			}
928 			for (c = line, w = 0; *c; c++, w++)
929 			{
930 				if (w >= p->max_cell_width)
931 				{
932 					fprintf(stderr, "bitmap line to long at line %ld.", f->lineno);
933 					goto fail;
934 				} else if (*c == 'X')
935 				{
936 					set_bit(b, k);
937 					k++;
938 				} else if (*c == '.')
939 				{
940 					k++;
941 				} else
942 				{
943 					fprintf(stderr, "illegal character '%c' in bitmap definition\n", *c);
944 					goto fail;
945 				}
946 			}
947 			if (i == 0)
948 			{
949 				width = w;
950 			} else if (w != width)
951 			{
952 				fprintf(stderr, "bitmap lines of different lengths\n");
953 				goto fail;
954 			}
955 		}
956 		EXPECT("endchar");
957 		p->off_table[ch] = width;			/* != F_NO_CHAR, real value filled later */
958 		if ((all_chars || (p->flags & F_MONOSPACE)) && width != p->max_cell_width)
959 		{
960 			fprintf(stderr, "%s: 0x%x: width %ld != cell width %d\n", fname, lastch, width, p->max_cell_width);
961 			goto fail;
962 		}
963 	}
964 	ifclose(f);
965 #undef EXPECT
966 
967 	/* compute size of final form, and compute offs from widths */
968 	off = 0;
969 	for (i = 0; i < bmnum; i++)
970 	{
971 		width = p->off_table[i];
972 		p->off_table[i] = off;
973 		if (width != F_NO_CHARL)
974 		{
975 			off += width;
976 		} else
977 		{
978 			if (i < 256 && !(i >= 0x80 && i <= 0x9f))
979 			{
980 				fprintf(stderr, "%s: warning: %x undefined\n", fname, i);
981 			}
982 			if (all_chars)
983 			{
984 				off += p->max_cell_width;
985 			}
986 		}
987 	}
988 	p->off_table[bmnum] = off;
989 	if (convert_to == FILE_FNT && off >= 0x10000L)
990 	{
991 		fprintf(stderr, "%s: last offset %ld too large for generating GEM font\n", fname, off);
992 		exit(EXIT_FAILURE);
993 	}
994 	p->form_width = ((off + 15) >> 4) << 1;
995 	p->dat_table = xmalloc((size_t)height * p->form_width);
996 
997 	check_monospaced(p, fname);
998 
999 	/* now, pack the bitmaps in the destination form */
1000 	for (i = 0; i < bmnum; i++)
1001 	{
1002 		off = p->off_table[i];
1003 		width = get_width(p, i);
1004 		if (width == F_NO_CHARL)
1005 			continue;
1006 		b = bms + bmsize * i;
1007 		k = 0;
1008 		for (j = 0; j < height; j++)
1009 		{
1010 			for (w = 0; w < width; w++)
1011 			{
1012 				if (get_bit(b, k))
1013 				{
1014 					set_bit(p->dat_table + j * p->form_width, off + w);
1015 				}
1016 				k++;
1017 			}
1018 		}
1019 	}
1020 
1021 	/* free temporary form */
1022 	free(bms);
1023 
1024 	return p;
1025   fail:
1026 	fprintf(stderr, "fatal error file %s line %ld\n", f->fname, f->lineno - 1);
1027 	ifclose(f);
1028 	exit(EXIT_FAILURE);
1029 	return NULL;
1030 }
1031 
1032 
read_fnt(const char * fname)1033 static struct font *read_fnt(const char *fname)
1034 {
1035 	struct font *p;
1036 	FILE *f;
1037 	struct font_file_hdr h;
1038 	long count;
1039 	long off_hor_table;
1040 	long off_off_table;
1041 	long off_dat_table;
1042 	int bigendian = 0;
1043 	int bmnum;
1044 
1045 	p = malloc(sizeof(struct font));
1046 	if (p == NULL)
1047 		fatal("memory");
1048 	f = open_input(&fname, "rb");
1049 
1050 	count = fread(&h, 1, sizeof(h), f);
1051 	if (count != sizeof(h))
1052 		fatal("short fread");
1053 
1054 	/* determine byte order */
1055 	if (get_l_word(h.point) >= 256)
1056 	{
1057 		bigendian = 1;
1058 	} else
1059 	{
1060 		bigendian = 0;
1061 	}
1062 
1063 	/* convert the header */
1064 #define GET_WORD(a) \
1065   if(bigendian) p->a = get_b_word(h.a); else p->a = get_l_word(h.a);
1066 
1067 	GET_WORD(font_id);
1068 	GET_WORD(point);
1069 
1070 	memcpy(p->name, h.name, sizeof(h.name));
1071 
1072 	GET_WORD(first_ade);
1073 	GET_WORD(last_ade);
1074 	GET_WORD(top);
1075 	GET_WORD(ascent);
1076 	GET_WORD(half);
1077 	GET_WORD(descent);
1078 	GET_WORD(bottom);
1079 	GET_WORD(max_char_width);
1080 	GET_WORD(max_cell_width);
1081 	GET_WORD(left_offset);
1082 	GET_WORD(right_offset);
1083 	GET_WORD(thicken);
1084 	GET_WORD(ul_size);
1085 	GET_WORD(lighten);
1086 	GET_WORD(skew);
1087 	GET_WORD(flags);
1088 
1089 	if (bigendian)
1090 	{
1091 		off_hor_table = get_b_long(h.hor_table);
1092 		off_off_table = get_b_long(h.off_table);
1093 		off_dat_table = get_b_long(h.dat_table);
1094 	} else
1095 	{
1096 		off_hor_table = get_l_long(h.hor_table);
1097 		off_off_table = get_l_long(h.off_table);
1098 		off_dat_table = get_l_long(h.dat_table);
1099 	}
1100 
1101 	GET_WORD(form_width);
1102 	GET_WORD(form_height);
1103 
1104 #undef GET_WORD
1105 
1106 	/* make some checks */
1107 	if (p->last_ade > MAX_ADE || p->first_ade > p->last_ade)
1108 	{
1109 		fatal("wrong char range : first = %d, last = %d", p->first_ade, p->last_ade);
1110 	}
1111 	if (p->max_cell_width >= 40 || p->form_height >= 40)
1112 	{
1113 		fatal("unreasonable font size %dx%d", p->max_cell_width, p->form_height);
1114 	}
1115 
1116 	if (fseek(f, off_off_table, SEEK_SET))
1117 		fatal("seek");
1118 	bmnum = p->last_ade - p->first_ade + 1;
1119 	p->off_table = xmalloc(sizeof(*p->off_table) * (bmnum + 1));
1120 
1121 	{
1122 		int i;
1123 		char buf[2];
1124 
1125 		for (i = 0; i <= bmnum; i++)
1126 		{
1127 			count = 2;
1128 			if ((long)fread(buf, 1, count, f) != count)
1129 				fatal("short read");
1130 			if (bigendian)
1131 			{
1132 				p->off_table[i] = get_b_word(buf);
1133 			} else
1134 			{
1135 				p->off_table[i] = get_l_word(buf);
1136 			}
1137 			if (p->off_table[i] == F_NO_CHAR)
1138 				p->off_table[i] = F_NO_CHARL;
1139 		}
1140 	}
1141 	if (fseek(f, off_dat_table, SEEK_SET))
1142 		fatal("seek");
1143 	count = p->form_height * p->form_width;
1144 	p->dat_table = malloc(count);
1145 	if (p->dat_table == NULL)
1146 		fatal("memory");
1147 	if ((long)fread(p->dat_table, 1, count, f) != count)
1148 		fatal("short read");
1149 
1150 	if (p->flags & F_HORZ_OFF)
1151 	{
1152 		long i;
1153 		int empty;
1154 
1155 		/*
1156 		 * check wether offset table is really present;
1157 		 * some fonts have this incorrectly set
1158 		 */
1159 		count = 2 * bmnum;
1160 		if (off_hor_table == 0 || off_hor_table == off_off_table)
1161 		{
1162 			fprintf(stderr, "%s: ignoring non-existing offset table\n", fname);
1163 			p->flags &= ~F_HORZ_OFF;
1164 		} else if ((off_off_table - off_hor_table) < count)
1165 		{
1166 			fprintf(stderr, "%s: ignoring offset table of size %ld, should be %ld\n", fname, off_off_table - off_hor_table, count);
1167 			p->flags &= ~F_HORZ_OFF;
1168 		} else
1169 		{
1170 			p->hor_table = xmalloc(count);
1171 			if (fseek(f, off_hor_table, SEEK_SET))
1172 				fatal("seek");
1173 			if ((long)fread(p->hor_table, 1, count, f) != count)
1174 				fatal("short read");
1175 			empty = 1;
1176 			for (i = 0; i < count; i++)
1177 			{
1178 				if (p->hor_table[i] != 0)
1179 					empty = 0;
1180 			}
1181 			if (empty)
1182 			{
1183 				fprintf(stderr, "%s: warning: empty horizontal offset table\n", fname);
1184 				p->flags &= ~F_HORZ_OFF;
1185 			}
1186 		}
1187 	}
1188 	fclose(f);
1189 
1190 	check_monospaced(p, fname);
1191 
1192 	return p;
1193 }
1194 
1195 
write_fnt(struct font * p,const char * fname)1196 static void write_fnt(struct font *p, const char *fname)
1197 {
1198 	FILE *f;
1199 	char buf[2];
1200 	int i;
1201 	struct font_file_hdr h;
1202 	long count;
1203 	long off_hor_table;
1204 	long off_off_table;
1205 	long off_dat_table;
1206 	long off;
1207 	int bmnum;
1208 
1209 	bmnum = p->last_ade - p->first_ade + 1;
1210 
1211 	if (p->off_table[bmnum] >= 0x10000L)
1212 	{
1213 		fprintf(stderr, "%s: form width %ld too large for GEM font\n", fname, p->off_table[bmnum]);
1214 		exit(EXIT_FAILURE);
1215 	}
1216 
1217 	f = open_output(&fname, "wb");
1218 
1219 	p->flags |= F_STDFORM;
1220 
1221 #define SET_WORD(a) set_b_word(h.a, p->a)
1222 	SET_WORD(font_id);
1223 	SET_WORD(point);
1224 	memcpy(h.name, p->name, sizeof(h.name));
1225 
1226 	SET_WORD(first_ade);
1227 	SET_WORD(last_ade);
1228 	SET_WORD(top);
1229 	SET_WORD(ascent);
1230 	SET_WORD(half);
1231 	SET_WORD(descent);
1232 	SET_WORD(bottom);
1233 	SET_WORD(max_char_width);
1234 	SET_WORD(max_cell_width);
1235 	SET_WORD(left_offset);
1236 	SET_WORD(right_offset);
1237 	SET_WORD(thicken);
1238 	SET_WORD(ul_size);
1239 	SET_WORD(lighten);
1240 	SET_WORD(skew);
1241 	SET_WORD(flags);
1242 	SET_WORD(form_width);
1243 	SET_WORD(form_height);
1244 #undef SET_WORD
1245 
1246 	off = sizeof(struct font_file_hdr);
1247 	off_hor_table = off;
1248 	if (p->flags & F_HORZ_OFF)
1249 	{
1250 		off += bmnum * 2;
1251 	}
1252 	off_off_table = off;
1253 	off += (bmnum + 1) * 2;
1254 	off_dat_table = off;
1255 
1256 	set_b_long(h.hor_table, off_hor_table);
1257 	set_b_long(h.off_table, off_off_table);
1258 	set_b_long(h.dat_table, off_dat_table);
1259 	set_b_long(h.next_font, 0);
1260 
1261 	count = sizeof(h);
1262 	if (count != (long)fwrite(&h, 1, count, f))
1263 		fatal("write");
1264 	if (p->flags & F_HORZ_OFF)
1265 	{
1266 		i = bmnum * 2;
1267 		if (i != fwrite(p->hor_table, 1, i, f))
1268 			fatal("write");
1269 	}
1270 	for (i = 0; i <= bmnum; i++)
1271 	{
1272 		set_b_word(buf, p->off_table[i]);
1273 		if (2 != fwrite(buf, 1, 2, f))
1274 			fatal("write");
1275 	}
1276 	count = p->form_width * p->form_height;
1277 	if (count != (long)fwrite(p->dat_table, 1, count, f))
1278 		fatal("write");
1279 	if (fclose(f))
1280 		fatal("fclose");
1281 }
1282 
1283 
write_bmp(struct font * p,const char * fname)1284 static void write_bmp(struct font *p, const char *fname)
1285 {
1286 	FILE *f;
1287 	long bmpwidth;
1288 	long bmpstride;
1289 	long bmpheight;
1290 	int bmnum, i;
1291 	int charrows;
1292 	long datasize;
1293 	int cmapsize = 256 * 4;
1294 	unsigned char *bitmap;
1295 
1296 #define CHAR_COLUMNS 16
1297 
1298 	struct {
1299 		unsigned char magic[2];			/* BM */
1300 		unsigned char filesize[4];
1301 		unsigned char xHotSpot[2];
1302 		unsigned char yHotSpot[2];
1303 		unsigned char offbits[4];		/* offset to data */
1304 	} fileheader;
1305 	struct {
1306 		unsigned char bisize[4];
1307 		unsigned char width[4];
1308 		unsigned char height[4];
1309 		unsigned char planes[2];
1310 		unsigned char bitcount[2];
1311 		unsigned char compressed[4];
1312 		unsigned char datasize[4];
1313 		unsigned char pix_width[4];
1314 		unsigned char pix_height[4];
1315 		unsigned char clr_used[4];
1316 		unsigned char clr_important[4];
1317 	} bmpheader;
1318 	unsigned char palette[256][4];
1319 
1320 	if (p->flags & F_HORZ_OFF)
1321 	{
1322 		fatal("horizontal offsets not handled");
1323 	}
1324 
1325 	bmnum = p->last_ade + 1;
1326 	charrows = (bmnum + CHAR_COLUMNS - 1) / CHAR_COLUMNS;
1327 	bmpwidth = CHAR_COLUMNS * p->max_cell_width * scale + (CHAR_COLUMNS + 1) * grid;
1328 	bmpstride = (bmpwidth + 3) & ~3;
1329 	bmpheight = charrows * p->form_height * scale + (charrows + 1) * grid;
1330 	datasize = bmpstride * bmpheight;
1331 
1332 	set_b_word(fileheader.magic, 0x424d);
1333 	set_l_long(fileheader.filesize, sizeof(fileheader) + sizeof(bmpheader) + cmapsize + datasize);
1334 	set_l_word(fileheader.xHotSpot, 0);
1335 	set_l_word(fileheader.yHotSpot, 0);
1336 	set_l_long(fileheader.offbits, sizeof(fileheader) + sizeof(bmpheader) + cmapsize);
1337 
1338 	set_l_long(bmpheader.bisize, sizeof(bmpheader));
1339 	set_l_long(bmpheader.width, bmpwidth);
1340 	set_l_long(bmpheader.height, -bmpheight); /* we write it top-down */
1341 	set_l_word(bmpheader.planes, 1);
1342 	set_l_word(bmpheader.bitcount, 8);
1343 	set_l_long(bmpheader.compressed, 0);
1344 	set_l_long(bmpheader.datasize, datasize);
1345 	set_l_long(bmpheader.pix_width, 95);
1346 	set_l_long(bmpheader.pix_height, 95);
1347 	set_l_long(bmpheader.clr_used, cmapsize / 4);
1348 	set_l_long(bmpheader.clr_important, grid > 0 ? 3 : 2);
1349 	memset(palette, 0, sizeof(palette));
1350 	palette[0][0] = 255;
1351 	palette[0][1] = 255;
1352 	palette[0][2] = 255;
1353 	palette[1][0] = 0;
1354 	palette[1][1] = 0;
1355 	palette[1][2] = 0;
1356 	palette[2][0] = 0;
1357 	palette[2][1] = 0;
1358 	palette[2][2] = 255;
1359 
1360 	f = open_output(&fname, "wb");
1361 
1362 	bitmap = xmalloc(datasize);
1363 
1364 	if (grid > 0)
1365 	{
1366 		int x, y, sy, sx;
1367 
1368 		for (y = 0; y < (charrows + 1); y++)
1369 		{
1370 			for (sy = 0; sy < grid; sy++)
1371 				for (x = 0; x < bmpwidth; x++)
1372 					bitmap[((y * (p->form_height * scale + grid)) + sy) * bmpstride + x] = 2;
1373 		}
1374 		for (x = 0; x < (CHAR_COLUMNS + 1); x++)
1375 		{
1376 			for (sx = 0; sx < grid; sx++)
1377 				for (y = 0; y < bmpheight; y++)
1378 					bitmap[y * bmpstride + x * (p->max_cell_width * scale + grid) + sx] = 2;
1379 		}
1380 	}
1381 
1382 	for (i = 0; i < bmnum; i++)
1383 	{
1384 		unsigned long w;
1385 		unsigned long off;
1386 		int y0;
1387 		int x0;
1388 		int x, y, sx, sy;
1389 
1390 		if (i < p->first_ade)
1391 			continue;
1392 		off = p->off_table[i - p->first_ade];
1393 		w = get_width(p, i - p->first_ade);
1394 		if (w == F_NO_CHARL)
1395 			continue;
1396 		y0 = i / CHAR_COLUMNS;
1397 		x0 = i % CHAR_COLUMNS;
1398 		for (y = 0; y < p->form_height; y++)
1399 		{
1400 			for (x = 0; x < w; x++)
1401 			{
1402 				if (get_bit(p->dat_table + p->form_width * y, off + x))
1403 				{
1404 					for (sx = 0; sx < scale; sx++)
1405 						for (sy = 0; sy < scale; sy++)
1406 							bitmap[((y0 * p->form_height + y) * scale + y0 * grid + grid + sy) * bmpstride + ((x0 * p->max_cell_width + x) * scale) + x0 * grid + grid + sx] = 1;
1407 				}
1408 			}
1409 		}
1410 	}
1411 
1412 	if (fwrite(&fileheader, 1, sizeof(fileheader), f) != sizeof(fileheader) ||
1413 		fwrite(&bmpheader, 1, sizeof(bmpheader), f) != sizeof(bmpheader) ||
1414 		fwrite(palette, 1, cmapsize, f) != cmapsize ||
1415 		fwrite(bitmap, 1, datasize, f) != datasize)
1416 		fatal("write");
1417 
1418 	fclose(f);
1419 
1420 #undef CHAR_COLUMNS
1421 }
1422 
1423 
write_txt(struct font * p,const char * filename)1424 static void write_txt(struct font *p, const char *filename)
1425 {
1426 	FILE *f;
1427 	int i;
1428 
1429 	/* first, write header */
1430 
1431 	if (p->flags & F_HORZ_OFF)
1432 	{
1433 		fatal("horizontal offsets not handled");
1434 	}
1435 
1436 	f = open_output(&filename, "w");
1437 	fprintf(f, "GDOSFONT\n");
1438 	fprintf(f, "version 1.0\n");
1439 
1440 #define SET_WORD(a) fprintf(f, #a " %u\n", p->a)
1441 	SET_WORD(font_id);
1442 	SET_WORD(point);
1443 
1444 	fprintf(f, "name \"");
1445 	for (i = 0; i < 32; i++)
1446 	{
1447 		char c = p->name[i];
1448 
1449 		if (c == 0)
1450 			break;
1451 		if (c < 32 || c > 126 || c == '\\' || c == '"')
1452 		{
1453 			fprintf(f, "\\%03o", c);
1454 		} else
1455 		{
1456 			fprintf(f, "%c", c);
1457 		}
1458 	}
1459 	fprintf(f, "\"\n");
1460 
1461 	SET_WORD(first_ade);
1462 	SET_WORD(last_ade);
1463 	SET_WORD(top);
1464 	SET_WORD(ascent);
1465 	SET_WORD(half);
1466 	SET_WORD(descent);
1467 	SET_WORD(bottom);
1468 	SET_WORD(max_char_width);
1469 	SET_WORD(max_cell_width);
1470 	SET_WORD(left_offset);
1471 	SET_WORD(right_offset);
1472 	SET_WORD(thicken);
1473 	SET_WORD(ul_size);
1474 
1475 	fprintf(f, "lighten 0x%04x\n", p->lighten);
1476 	fprintf(f, "skew 0x%04x\n", p->skew);
1477 	fprintf(f, "flags 0x%02x\n", p->flags);
1478 
1479 	SET_WORD(form_height);
1480 #undef SET_WORD
1481 
1482 	/* then, output char bitmaps */
1483 	for (i = p->first_ade; i <= p->last_ade; i++)
1484 	{
1485 		int r, c;
1486 		unsigned long w;
1487 		unsigned long off;
1488 
1489 		off = p->off_table[i - p->first_ade];
1490 		w = get_width(p, i - p->first_ade);
1491 		if (w == F_NO_CHARL)
1492 			continue;
1493 		if ((off + w) > (8 * p->form_width))
1494 		{
1495 			fprintf(stderr, "char %d: offset %ld + width %ld out of range (%ld)\n", i, off, w, 8 * p->form_width);
1496 			continue;
1497 		}
1498 		if (i < 32 || i > 126)
1499 		{
1500 			fprintf(f, "char 0x%02x\n", i);
1501 		} else if (i == '\\' || i == '\'')
1502 		{
1503 			fprintf(f, "char '\\%c'\n", i);
1504 		} else
1505 		{
1506 			fprintf(f, "char '%c'\n", i);
1507 		}
1508 
1509 		for (r = 0; r < p->form_height; r++)
1510 		{
1511 			for (c = 0; c < w; c++)
1512 			{
1513 				if (get_bit(p->dat_table + p->form_width * r, off + c))
1514 				{
1515 					fprintf(f, "X");
1516 				} else
1517 				{
1518 					fprintf(f, ".");
1519 				}
1520 			}
1521 			fprintf(f, "\n");
1522 		}
1523 		fprintf(f, "endchar\n");
1524 	}
1525 	fprintf(f, "endfont\n");
1526 	fclose(f);
1527 }
1528 
1529 
write_c_emutos(struct font * p,const char * filename)1530 static void write_c_emutos(struct font *p, const char *filename)
1531 {
1532 	FILE *f;
1533 	unsigned long i;
1534 	int bmnum;
1535 	const char *off_table_name = "off_table";
1536 	const char *hor_table_name = "0";
1537 
1538 	bmnum = p->last_ade - p->first_ade + 1;
1539 	if (p->off_table[bmnum] >= 0x10000L)
1540 	{
1541 		fprintf(stderr, "%s: form width %ld too large for GEM font\n", filename, p->off_table[bmnum]);
1542 		exit(EXIT_FAILURE);
1543 	}
1544 
1545 	/* output is always done bigendian */
1546 
1547 	p->flags |= F_STDFORM;
1548 
1549 	/* first, write header */
1550 
1551 	f = open_output(&filename, "w");
1552 	fprintf(f, "\
1553 /*\n\
1554  * %s - a font in standard format\n\
1555  *\n\
1556  * Copyright (C) 2001-2018 The EmuTOS development team\n\
1557  *\n\
1558  * This file is distributed under the GPL, version 2 or at your\n\
1559  * option any later version.  See doc/license.txt for details.\n\
1560  *\n\
1561  * Automatically generated by fntconv.c\n\
1562  */\n", filename);
1563 	fprintf(f, "\
1564 \n\
1565 #include \"config.h\"\n\
1566 #include \"portab.h\"\n\
1567 #include \"fonthdr.h\"\n\
1568 \n");
1569 
1570 	if (p->first_ade == 0 && p->last_ade == 255 && (p->flags & F_MONOSPACE))
1571 	{
1572 		if (p->max_cell_width == 6 && p->form_height == 6)
1573 			off_table_name = "off_6x6_table";
1574 		else if (p->max_cell_width == 8 && p->form_height == 8)
1575 			off_table_name = "off_8x8_table";
1576 		else if (p->max_cell_width == 8 && p->form_height == 16)
1577 			off_table_name = "off_8x16_table";
1578 		else if (!do_off_table)
1579 			fatal("unsupported font size for omitting offset table");
1580 	}
1581 
1582 	if (do_off_table)
1583 	{
1584 		fprintf(f, "static const UWORD %s[] =\n{\n", off_table_name);
1585 		{
1586 			for (i = 0; i <= bmnum; i++)
1587 			{
1588 				if ((i & 7) == 0)
1589 					fprintf(f, "    ");
1590 				else
1591 					fprintf(f, " ");
1592 				fprintf(f, "0x%04lx", p->off_table[i]);
1593 				if (i != bmnum)
1594 					fprintf(f, ",");
1595 				if ((i & 7) == 7)
1596 					fprintf(f, "\n");
1597 			}
1598 		}
1599 		if ((i & 7) != 7)
1600 			fprintf(f, "\n");
1601 		fprintf(f, "};\n\n");
1602 	} else
1603 	{
1604 		if (p->first_ade == 0 && p->last_ade == 255 && (p->flags & F_MONOSPACE) &&
1605 			p->max_cell_width == 8 && p->form_height == 16)
1606 		{
1607 			fprintf(f, "extern const UWORD %s[];\n", "off_8x8_table");
1608 			fprintf(f, "#define %s %s\n", off_table_name, "off_8x8_table");
1609 		} else
1610 		{
1611 			fprintf(f, "extern const UWORD %s[];\n", off_table_name);
1612 		}
1613 		fprintf(f, "\n");
1614 	}
1615 
1616 	if (p->flags & F_HORZ_OFF)
1617 	{
1618 		hor_table_name = "hor_table";
1619 
1620 		fprintf(f, "static const UBYTE %s[] =\n{\n", hor_table_name);
1621 		{
1622 			unsigned long count = bmnum * 2;
1623 
1624 			for (i = 0; i < count; i++)
1625 			{
1626 				if ((i & 7) == 0)
1627 					fprintf(f, "    ");
1628 				else
1629 					fprintf(f, " ");
1630 				fprintf(f, "0x%02x", p->hor_table[i]);
1631 				if (i != (count - 1))
1632 					fprintf(f, ",");
1633 				if ((i & 7) == 7)
1634 					fprintf(f, "\n");
1635 			}
1636 		}
1637 		if ((i & 7) != 7)
1638 			fprintf(f, "\n");
1639 		fprintf(f, "};\n\n");
1640 	}
1641 
1642 	fprintf(f, "static const UWORD dat_table[] =\n{\n");
1643 	{
1644 		unsigned long h;
1645 		unsigned int a;
1646 
1647 		h = (p->form_height * p->form_width) / 2;
1648 		for (i = 0; i < h; i++)
1649 		{
1650 			if ((i & 7) == 0)
1651 				fprintf(f, "    ");
1652 			else
1653 				fprintf(f, " ");
1654 			a = (p->dat_table[2 * i] << 8) | (p->dat_table[2 * i + 1] & 0xFF);
1655 			fprintf(f, "0x%04x", a);
1656 			if (i != (h - 1))
1657 				fprintf(f, ",");
1658 			if ((i & 7) == 7)
1659 				fprintf(f, "\n");
1660 		}
1661 		if ((i & 7) != 0)
1662 			fprintf(f, "\n");
1663 	}
1664 	fprintf(f, "};\n");
1665 	fprintf(f, "\n");
1666 
1667 	fprintf(f, "const Fonthead %s = {\n", varname);
1668 
1669 #define SET_WORD(a) fprintf(f, "    %u,  /* " #a " */\n", (unsigned int)p->a)
1670 	SET_WORD(font_id);
1671 	SET_WORD(point);
1672 
1673 	fprintf(f, "    \"");
1674 	for (i = 0; i < 32; i++)
1675 	{
1676 		char c = p->name[i];
1677 
1678 		if (c == 0)
1679 			break;
1680 		if (c < 32 || c > 126 || c == '\\' || c == '"')
1681 		{
1682 			fprintf(f, "\\%03o", c);
1683 		} else
1684 		{
1685 			fprintf(f, "%c", c);
1686 		}
1687 	}
1688 	fprintf(f, "\",  /*   BYTE name[32]	*/\n");
1689 
1690 	SET_WORD(first_ade);
1691 	SET_WORD(last_ade);
1692 	SET_WORD(top);
1693 	SET_WORD(ascent);
1694 	SET_WORD(half);
1695 	SET_WORD(descent);
1696 	SET_WORD(bottom);
1697 	SET_WORD(max_char_width);
1698 	SET_WORD(max_cell_width);
1699 	SET_WORD(left_offset);
1700 	SET_WORD(right_offset);
1701 	SET_WORD(thicken);
1702 	SET_WORD(ul_size);
1703 
1704 	fprintf(f, "    0x%04x, /* lighten */\n", p->lighten);
1705 	fprintf(f, "    0x%04x, /* skew */\n", p->skew);
1706 
1707 	fprintf(f, "    F_STDFORM");
1708 	if (p->flags & F_MONOSPACE)
1709 		fprintf(f, " | F_MONOSPACE");
1710 	if (p->flags & F_DEFAULT)
1711 		fprintf(f, " | F_DEFAULT");
1712 	if (p->flags & F_HORZ_OFF)
1713 		fprintf(f, " | F_HORZ_OFF");
1714 	if (p->flags & F_EXT_HDR)
1715 		fprintf(f, " | F_EXT_HDR");
1716 	if ((p->flags & ~F_SUPPORTED) != 0)
1717 		fprintf(f, " | 0x%x", p->flags & ~F_SUPPORTED);
1718 	fprintf(f, ",  /* flags */\n");
1719 	fprintf(f, "    %s,			/*   UBYTE *hor_table	*/\n", hor_table_name);
1720 	fprintf(f, "    %s,		/*   UWORD *off_table	*/\n", off_table_name);
1721 	fprintf(f, "    dat_table,		/*   UBYTE *dat_table	*/\n");
1722 
1723 	SET_WORD(form_width);
1724 	SET_WORD(form_height);
1725 #undef SET_WORD
1726 	fprintf(f, "    0,  /* struct font * next_font */\n");
1727 	fprintf(f, "    0   /* UWORD next_seg */\n");
1728 	fprintf(f, "};\n");
1729 
1730 	fclose(f);
1731 }
1732 
1733 
write_c_aranym(struct font * p,const char * filename)1734 static void write_c_aranym(struct font *p, const char *filename)
1735 {
1736 	FILE *f;
1737 
1738 	if (p->flags & F_HORZ_OFF)
1739 	{
1740 		fatal("horizontal offsets not handled");
1741 	}
1742 
1743 	/* first, write header */
1744 
1745 	f = open_output(&filename, "w");
1746 	fprintf(f, "\
1747 /*\n\
1748  * font.h - 8x16 font for Atari ST encoding\n\
1749  * modified for the SDL-GUI (added a radio button and a checkbox)\n\
1750  * converted using ./fntconv -a -o font.h aranym10.txt\n\
1751  *\n\
1752  * The fntconv utility can be used to convert the font to text, edit it, and convert it back.\n\
1753  *\n\
1754  * Copyright (C) 2007  ARAnyM development team\n\
1755  * Copyright (C) 2001, 02 The EmuTOS development team\n\
1756  *\n\
1757  * This program is free software; you can redistribute it and/or modify\n\
1758  * it under the terms of the GNU General Public License as published by\n\
1759  * the Free Software Foundation; either version 2 of the License, or\n\
1760  * (at your option) any later version.\n\
1761 \n\
1762  * This program is distributed in the hope that it will be useful,\n\
1763  * but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
1764  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
1765  * GNU General Public License for more details.\n\
1766 \n\
1767  * You should have received a copy of the GNU General Public License\n\
1768  * along with this program; if not, write to the Free Software\n\
1769  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\
1770  */\n\
1771 \n\
1772 \n");
1773 	fprintf(f, "#define FONTWIDTH %d\n", p->max_cell_width);
1774 	fprintf(f, "#define FONTHEIGHT %d\n", p->form_height);
1775 	fprintf(f, "#define FONTCHARS %u\n", p->last_ade + 1);
1776 	fprintf(f, "#define FORM_WIDTH %lu\n", p->form_width);
1777 	fprintf(f, "\n");
1778 	fprintf(f, "static unsigned char const font_bits[] = {\n");
1779 
1780 	{
1781 		unsigned long i, h;
1782 		unsigned char a;
1783 
1784 		h = p->form_height * p->form_width;
1785 		for (i = 0; i < h; i++)
1786 		{
1787 			if ((i & 15) == 0)
1788 				fprintf(f, "    ");
1789 			else
1790 				fprintf(f, " ");
1791 			a = p->dat_table[i];
1792 			fprintf(f, "0x%02x", a);
1793 			if (i != (h - 1))
1794 				fprintf(f, ",");
1795 			if ((i & 15) == 15)
1796 				fprintf(f, "\n");
1797 		}
1798 		if ((i & 15) != 0)
1799 			fprintf(f, "\n");
1800 	}
1801 	fprintf(f, "};\n");
1802 
1803 	fclose(f);
1804 }
1805 
1806 
file_type(const char * c)1807 static int file_type(const char *c)
1808 {
1809 	int n;
1810 
1811 	if (c == NULL || strcmp(c, "-") == 0)
1812 		return FILE_TXT;
1813 	n = (int)strlen(c);
1814 	if (n >= 3 && c[n - 2] == '.' && (c[n - 1] == 'c' || c[n - 1] == 'C' || c[n - 1] == 'h' || c[n - 1] == 'H'))
1815 		return FILE_C;
1816 	if (n < 5 || c[n - 4] != '.')
1817 		return 0;
1818 	if (strcmp(c + n - 3, "txt") == 0 || strcmp(c + n - 3, "TXT") == 0)
1819 		return FILE_TXT;
1820 	if (strcmp(c + n - 3, "fnt") == 0 || strcmp(c + n - 3, "FNT") == 0)
1821 		return FILE_FNT;
1822 	if (strcmp(c + n - 3, "bmp") == 0 || strcmp(c + n - 3, "BMP") == 0)
1823 		return FILE_BMP;
1824 	return 0;
1825 }
1826 
1827 
1828 enum opt {
1829 	OPTION_FLAG_SET = 0,
1830 	OPTION_ERROR = '?',
1831 	OPTION_OUTPUT = 'o',
1832 	OPTION_ARANYM = 'a',
1833 	OPTION_ALLCHARS = 'A',
1834 	OPTION_NO_OFFTABLE = 'O',
1835 	OPTION_VARNAME = 'v',
1836 	OPTION_SCALE = 's',
1837 	OPTION_GRID = 'g',
1838 
1839 	OPTION_HELP = 'h',
1840 	OPTION_VERSION = 'V'
1841 };
1842 
1843 
1844 static struct option const long_options[] = {
1845 	{ "aranym", no_argument, NULL, OPTION_ARANYM },
1846 	{ "all-chars", no_argument, NULL, OPTION_ALLCHARS },
1847 	{ "output", required_argument, NULL, OPTION_OUTPUT },
1848 	{ "varname", required_argument, NULL, OPTION_VARNAME },
1849 	{ "no-offtable", no_argument, NULL, OPTION_NO_OFFTABLE },
1850 	{ "scale", required_argument, NULL, OPTION_SCALE },
1851 	{ "grid", required_argument, NULL, OPTION_GRID },
1852 	{ "help", no_argument, NULL, OPTION_HELP },
1853 	{ "version", no_argument, NULL, OPTION_VERSION },
1854 	{ NULL, no_argument, NULL, 0 }
1855 };
1856 
1857 
print_version(void)1858 static void print_version(void)
1859 {
1860 }
1861 
1862 
usage(FILE * f,int errcode)1863 static void usage(FILE *f, int errcode)
1864 {
1865 	fprintf(f, "\
1866 Usage: \n\
1867   fntconv -o <to> <from>\n\
1868     converts BDOS font between types C, TXT, FNT.\n\
1869     the file types are inferred from the file extensions.\n\
1870     (not all combinations are allowed.)\n\
1871 Options:\n\
1872   -o, --output <file>   write output to <file>\n\
1873   -a, --aranym          output C sourcecode for AraNYM\n\
1874   -A, --all-chars       output data also for non-existent chars\n\
1875   -v, --varname <name>  set the name of the font header variable\n\
1876   -O, --no-offtable     do not write the offsets table\n\
1877   -s, --scale <factor>  scale picture up (BMP only)\n\
1878   -g, --grid <width>    draw grid around characters (BMP only)\n\
1879 ");
1880 	exit(errcode);
1881 }
1882 
1883 
1884 
1885 
main(int argc,char ** argv)1886 int main(int argc, char **argv)
1887 {
1888 	struct font *p;
1889 	const char *from = NULL;
1890 	const char *to = NULL;
1891 	int c;
1892 
1893 	while ((c = getopt_long_only(argc, argv, "o:ag:s:AOhV", long_options, NULL)) != EOF)
1894 	{
1895 		const char *arg = optarg;
1896 		switch ((enum opt) c)
1897 		{
1898 		case OPTION_OUTPUT:
1899 			to = arg;
1900 			break;
1901 		case OPTION_ARANYM:
1902 			for_aranym = 1;
1903 			all_chars = 1;
1904 			break;
1905 		case OPTION_ALLCHARS:
1906 			all_chars = 1;
1907 			break;
1908 		case OPTION_NO_OFFTABLE:
1909 			do_off_table = 0;
1910 			break;
1911 		case OPTION_VARNAME:
1912 			varname = optarg;
1913 			break;
1914 		case OPTION_SCALE:
1915 			scale = (int)strtol(optarg, NULL, 0);
1916 			if (scale <= 0 || scale > 100)
1917 				usage(stderr, EXIT_FAILURE);
1918 			break;
1919 		case OPTION_GRID:
1920 			grid = (int)strtol(optarg, NULL, 0);
1921 			if (grid < 0 || grid > 100)
1922 				usage(stderr, EXIT_FAILURE);
1923 			break;
1924 
1925 		case OPTION_VERSION:
1926 			print_version();
1927 			return EXIT_SUCCESS;
1928 
1929 		case OPTION_HELP:
1930 			usage(stdout, EXIT_SUCCESS);
1931 			break;
1932 
1933 		/* returned when only flag was set */
1934 		case OPTION_FLAG_SET:
1935 			break;
1936 
1937 		/* returned for unknown/invalid option */
1938 		case OPTION_ERROR:
1939 			exit(EXIT_FAILURE);
1940 			break;
1941 		}
1942 	}
1943 
1944 	if ((argc - optind) != 1)
1945 		usage(stderr, EXIT_FAILURE);
1946 
1947 	from = argv[optind];
1948 
1949 	convert_from = file_type(from);
1950 	convert_to = file_type(to);
1951 
1952 	switch (convert_from)
1953 	{
1954 	case FILE_C:
1955 		fatal("cannot read C files");
1956 		return EXIT_FAILURE;
1957 	case FILE_TXT:
1958 		p = read_txt(from);
1959 		break;
1960 	case FILE_FNT:
1961 		p = read_fnt(from);
1962 		break;
1963 	case FILE_BMP:
1964 		fatal("cannot read BMP files");
1965 		return EXIT_FAILURE;
1966 	default:
1967 		fatal("wrong file type");
1968 		return EXIT_FAILURE;
1969 	}
1970 	switch (convert_to)
1971 	{
1972 	case FILE_C:
1973 		if (for_aranym)
1974 			write_c_aranym(p, to);
1975 		else
1976 			write_c_emutos(p, to);
1977 		break;
1978 	case FILE_TXT:
1979 		write_txt(p, to);
1980 		break;
1981 	case FILE_FNT:
1982 		write_fnt(p, to);
1983 		break;
1984 	case FILE_BMP:
1985 		write_bmp(p, to);
1986 		break;
1987 	default:
1988 		fatal("wrong file type");
1989 		return EXIT_FAILURE;
1990 	}
1991 	return EXIT_SUCCESS;
1992 }
1993