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