1/*
2 * Copyright (c) 2009, 2014 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Ed Schouten under sponsorship from the
6 * FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/types.h>
31#include <endian.h>
32#include <sys/param.h>
33#include <sys/queue.h>
34
35#include <assert.h>
36#include <err.h>
37#include <stdint.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <stdbool.h>
41#include <string.h>
42#include <unistd.h>
43#include <lz4.h>
44#include "fnv_hash.h"
45
46#define	VFNT_MAPS 4
47#define	VFNT_MAP_NORMAL 0
48#define	VFNT_MAP_NORMAL_RH 1
49#define	VFNT_MAP_BOLD 2
50#define	VFNT_MAP_BOLD_RH 3
51
52static unsigned int width = 8, wbytes, height = 16;
53
54struct bbox {
55	unsigned int width;	/* pixels */
56	unsigned int height;
57	int x;			/* lower left corner x */
58	int y;			/* lower left corner y */
59};
60
61static struct bbox bbox;	/* font bounding box */
62static int font_ascent;		/* pixels above baseline */
63static int font_descent;	/* pixels below baseline */
64static unsigned int default_char = 0xFFFD;
65
66struct glyph {
67	TAILQ_ENTRY(glyph)	 g_list;
68	SLIST_ENTRY(glyph)	 g_hash;
69	uint8_t			*g_data;
70	unsigned int		 g_index;
71};
72
73#define	FONTCVT_NHASH 4096
74TAILQ_HEAD(glyph_list, glyph);
75static SLIST_HEAD(, glyph) glyph_hash[FONTCVT_NHASH];
76static struct glyph_list glyphs[VFNT_MAPS] = {
77    TAILQ_HEAD_INITIALIZER(glyphs[0]),
78    TAILQ_HEAD_INITIALIZER(glyphs[1]),
79    TAILQ_HEAD_INITIALIZER(glyphs[2]),
80    TAILQ_HEAD_INITIALIZER(glyphs[3]),
81};
82static unsigned int glyph_total, glyph_count[4], glyph_unique, glyph_dupe;
83
84struct mapping {
85	TAILQ_ENTRY(mapping)	 m_list;
86	unsigned int		 m_char;
87	unsigned int		 m_length;
88	struct glyph		*m_glyph;
89};
90
91TAILQ_HEAD(mapping_list, mapping);
92static struct mapping_list maps[VFNT_MAPS] = {
93    TAILQ_HEAD_INITIALIZER(maps[0]),
94    TAILQ_HEAD_INITIALIZER(maps[1]),
95    TAILQ_HEAD_INITIALIZER(maps[2]),
96    TAILQ_HEAD_INITIALIZER(maps[3]),
97};
98static unsigned int mapping_total, map_count[4], map_folded_count[4],
99    mapping_unique, mapping_dupe;
100
101enum output_format {
102	VT_FONT,		/* default */
103	VT_C_SOURCE,		/* C source for built in fonts */
104	VT_C_COMPRESSED		/* C source with compressed font data */
105};
106
107struct whitelist {
108	uint32_t c;
109	uint32_t len;
110};
111
112/*
113 * Compressed font glyph list. To be used with boot loader, we need to have
114 * ascii set and box drawing chars.
115 */
116static struct whitelist c_list[] = {
117	{ .c = 0, .len = 0 },		/* deault char */
118	{ .c = 0x20, .len = 0x5f },
119	{ .c = 0x2500, .len = 0 },	/* single frame */
120	{ .c = 0x2502, .len = 0 },
121	{ .c = 0x250c, .len = 0 },
122	{ .c = 0x2510, .len = 0 },
123	{ .c = 0x2514, .len = 0 },
124	{ .c = 0x2518, .len = 0 },
125	{ .c = 0x2550, .len = 1 },	/* double frame */
126	{ .c = 0x2554, .len = 0 },
127	{ .c = 0x2557, .len = 0 },
128	{ .c = 0x255a, .len = 0 },
129	{ .c = 0x255d, .len = 0 },
130};
131
132/*
133 * Uncompressed source. For x86 we need cp437 so the vga text mode
134 * can program font into the vga card.
135 */
136static struct whitelist s_list[] = {
137	{ .c = 0, .len = 0 },		/* deault char */
138	{ .c = 0x20, .len = 0x5f },	/* ascii set */
139	{ .c = 0xA0, .len = 0x5f },	/* latin 1 */
140	{ .c = 0x0192, .len = 0 },
141	{ .c = 0x0332, .len = 0 },	/* composing lower line */
142	{ .c = 0x0393, .len = 0 },
143	{ .c = 0x0398, .len = 0 },
144	{ .c = 0x03A3, .len = 0 },
145	{ .c = 0x03A6, .len = 0 },
146	{ .c = 0x03A9, .len = 0 },
147	{ .c = 0x03B1, .len = 1 },
148	{ .c = 0x03B4, .len = 0 },
149	{ .c = 0x03C0, .len = 0 },
150	{ .c = 0x03C3, .len = 0 },
151	{ .c = 0x03C4, .len = 0 },
152	{ .c = 0x207F, .len = 0 },
153	{ .c = 0x20A7, .len = 0 },
154	{ .c = 0x2205, .len = 0 },
155	{ .c = 0x220A, .len = 0 },
156	{ .c = 0x2219, .len = 1 },
157	{ .c = 0x221E, .len = 0 },
158	{ .c = 0x2229, .len = 0 },
159	{ .c = 0x2248, .len = 0 },
160	{ .c = 0x2261, .len = 0 },
161	{ .c = 0x2264, .len = 1 },
162	{ .c = 0x2310, .len = 0 },
163	{ .c = 0x2320, .len = 1 },
164	{ .c = 0x2500, .len = 0 },
165	{ .c = 0x2502, .len = 0 },
166	{ .c = 0x250C, .len = 0 },
167	{ .c = 0x2510, .len = 0 },
168	{ .c = 0x2514, .len = 0 },
169	{ .c = 0x2518, .len = 0 },
170	{ .c = 0x251C, .len = 0 },
171	{ .c = 0x2524, .len = 0 },
172	{ .c = 0x252C, .len = 0 },
173	{ .c = 0x2534, .len = 0 },
174	{ .c = 0x253C, .len = 0 },
175	{ .c = 0x2550, .len = 0x1c },
176	{ .c = 0x2580, .len = 0 },
177	{ .c = 0x2584, .len = 0 },
178	{ .c = 0x2588, .len = 0 },
179	{ .c = 0x258C, .len = 0 },
180	{ .c = 0x2590, .len = 3 },
181	{ .c = 0x25A0, .len = 0 },
182};
183
184bool filter = true;
185enum output_format format = VT_FONT;
186/* Type for write callback. */
187typedef size_t (*vt_write)(const void *, size_t, size_t, FILE *);
188uint8_t *uncompressed;
189
190static void
191usage(void)
192{
193
194	(void) fprintf(stderr, "usage:\tvtfontcvt "
195	    "[-n] [-f font|source|compressed-source] [-w width] "
196	    "[-h height]\n\t[-v] -o outfile normal.bdf [bold.bdf]\n");
197	exit(1);
198}
199
200static void *
201xmalloc(size_t size)
202{
203	void *m;
204
205	if ((m = malloc(size)) == NULL)
206		errx(1, "memory allocation failure");
207	return (m);
208}
209
210static int
211add_mapping(struct glyph *gl, unsigned int c, unsigned int map_idx)
212{
213	struct mapping *mp, *v;
214	struct mapping_list *ml;
215
216	mapping_total++;
217
218	mp = xmalloc(sizeof (*mp));
219	mp->m_char = c;
220	mp->m_glyph = gl;
221	mp->m_length = 0;
222
223	ml = &maps[map_idx];
224	if (TAILQ_LAST(ml, mapping_list) != NULL &&
225	    TAILQ_LAST(ml, mapping_list)->m_char >= c) {
226		TAILQ_FOREACH_REVERSE(v, ml, mapping_list, m_list) {
227			if (v->m_char < c) {
228				TAILQ_INSERT_AFTER(ml, v, mp, m_list);
229				break;
230			} else if (v->m_char == c)
231				errx(1, "Bad ordering at character %u", c);
232		}
233	} else
234		TAILQ_INSERT_TAIL(ml, mp, m_list);
235
236	map_count[map_idx]++;
237	mapping_unique++;
238
239	return (0);
240}
241
242static int
243dedup_mapping(unsigned int map_idx)
244{
245	struct mapping *tmp, *mp_bold, *mp_normal;
246	unsigned normal_map_idx = map_idx - VFNT_MAP_BOLD;
247
248	assert(map_idx == VFNT_MAP_BOLD || map_idx == VFNT_MAP_BOLD_RH);
249	mp_normal = TAILQ_FIRST(&maps[normal_map_idx]);
250	TAILQ_FOREACH_SAFE(mp_bold, &maps[map_idx], m_list, tmp) {
251		while (mp_normal->m_char < mp_bold->m_char)
252			mp_normal = TAILQ_NEXT(mp_normal, m_list);
253		if (mp_bold->m_char != mp_normal->m_char)
254			errx(1, "Character %u not in normal font!",
255			    mp_bold->m_char);
256		if (mp_bold->m_glyph != mp_normal->m_glyph)
257			continue;
258
259		/* No mapping is needed if it's equal to the normal mapping. */
260		TAILQ_REMOVE(&maps[map_idx], mp_bold, m_list);
261		free(mp_bold);
262		mapping_dupe++;
263	}
264	return (0);
265}
266
267static struct glyph *
268add_glyph(const uint8_t *bytes, unsigned int map_idx, int fallback)
269{
270	struct glyph *gl;
271	int hash;
272
273	glyph_total++;
274	glyph_count[map_idx]++;
275
276	hash = fnv_32_buf(bytes, wbytes * height, FNV1_32_INIT) % FONTCVT_NHASH;
277	SLIST_FOREACH(gl, &glyph_hash[hash], g_hash) {
278		if (memcmp(gl->g_data, bytes, wbytes * height) == 0) {
279			glyph_dupe++;
280			return (gl);
281		}
282	}
283
284	gl = xmalloc(sizeof (*gl));
285	gl->g_data = xmalloc(wbytes * height);
286	memcpy(gl->g_data, bytes, wbytes * height);
287	if (fallback)
288		TAILQ_INSERT_HEAD(&glyphs[map_idx], gl, g_list);
289	else
290		TAILQ_INSERT_TAIL(&glyphs[map_idx], gl, g_list);
291	SLIST_INSERT_HEAD(&glyph_hash[hash], gl, g_hash);
292
293	glyph_unique++;
294	return (gl);
295}
296
297static bool
298check_whitelist(unsigned c)
299{
300	struct whitelist *w = NULL;
301	int i, n = 0;
302
303	if (filter == false)
304		return (true);
305
306	if (format == VT_C_SOURCE) {
307		w = s_list;
308		n = sizeof (s_list) / sizeof (s_list[0]);
309	}
310	if (format == VT_C_COMPRESSED) {
311		w = c_list;
312		n = sizeof (c_list) / sizeof (c_list[0]);
313	}
314	if (w == NULL)
315		return (true);
316	for (i = 0; i < n; i++) {
317		if (c >= w[i].c && c <= w[i].c + w[i].len)
318			return (true);
319	}
320	return (false);
321}
322
323static int
324add_char(unsigned curchar, unsigned map_idx, uint8_t *bytes, uint8_t *bytes_r)
325{
326	struct glyph *gl;
327
328	/* Prevent adding two glyphs for default_char */
329	if (curchar == default_char) {
330		if (map_idx < VFNT_MAP_BOLD)
331			gl = add_glyph(bytes, 0, 1);
332	} else if (filter == false || curchar >= 0x20) {
333		gl = add_glyph(bytes, map_idx, 0);
334		if (add_mapping(gl, curchar, map_idx) != 0)
335			return (1);
336		if (bytes_r != NULL) {
337			gl = add_glyph(bytes_r, map_idx + 1, 0);
338			if (add_mapping(gl, curchar,
339			    map_idx + 1) != 0)
340				return (1);
341		}
342	}
343	return (0);
344}
345
346
347static int
348parse_bitmap_line(uint8_t *left, uint8_t *right, unsigned int line,
349    unsigned int dwidth)
350{
351	uint8_t *p;
352	unsigned int i, subline;
353
354	if (dwidth != width && dwidth != width * 2)
355		errx(1, "Bitmap with unsupported width %u!", dwidth);
356
357	/* Move pixel data right to simplify splitting double characters. */
358	line >>= (howmany(dwidth, 8) * 8) - dwidth;
359
360	for (i = dwidth / width; i > 0; i--) {
361		p = (i == 2) ? right : left;
362
363		subline = line & ((1 << width) - 1);
364		subline <<= (howmany(width, 8) * 8) - width;
365
366		if (wbytes == 1) {
367			*p = subline;
368		} else if (wbytes == 2) {
369			*p++ = subline >> 8;
370			*p = subline;
371		} else {
372			errx(1, "Unsupported wbytes %u!", wbytes);
373		}
374
375		line >>= width;
376	}
377
378	return (0);
379}
380
381static int
382parse_bdf(FILE *fp, unsigned int map_idx)
383{
384	char ln[BUFSIZ];
385	uint8_t bytes[wbytes * height], bytes_r[wbytes * height];
386	unsigned int curchar = 0, dwidth = 0, i, line;
387
388	memset(bytes, 0, sizeof (bytes));
389	memset(bytes_r, 0, sizeof (bytes_r));
390
391	while (fgets(ln, sizeof (ln), fp) != NULL) {
392		if (sscanf(ln, "ENCODING %u", &curchar) == 1)
393			continue;
394
395		if (sscanf(ln, "DWIDTH %u", &dwidth) == 1)
396			continue;
397
398		if (strncmp(ln, "BITMAP", 6) == 0) {
399			for (i = 0; i < height; i++) {
400				if (fgets(ln, sizeof (ln), fp) == NULL)
401					errx(1, "Unexpected EOF!");
402				sscanf(ln, "%x", &line);
403				if (parse_bitmap_line(bytes + i * wbytes,
404				    bytes_r + i * wbytes, line, dwidth) != 0)
405					return (1);
406			}
407
408			if (check_whitelist(curchar) == true) {
409				if (add_char(curchar, map_idx, bytes,
410				    dwidth == width * 2 ? bytes_r : NULL) != 0)
411					return (1);
412			}
413		}
414	}
415
416	return (0);
417}
418
419static void
420set_width(int w)
421{
422
423	if (w <= 0 || w > 128)
424		errx(1, "invalid width %d", w);
425	width = w;
426	wbytes = howmany(width, 8);
427}
428
429static int
430parse_hex(FILE *fp, unsigned int map_idx)
431{
432	char ln[BUFSIZ], *p;
433	char fmt_str[8];
434	uint8_t *bytes = NULL, *bytes_r = NULL;
435	unsigned curchar = 0, i, line, chars_per_row, dwidth;
436	int rv = 0;
437
438	while (fgets(ln, sizeof (ln), fp) != NULL) {
439		if (strncmp(ln, "# Height: ", 10) == 0) {
440			if (bytes != NULL) {
441				errx(1, "malformed input: Height tag after "
442				    "font data");
443			}
444			height = atoi(ln + 10);
445		} else if (strncmp(ln, "# Width: ", 9) == 0) {
446			if (bytes != NULL) {
447				errx(1, "malformed input: Width tag after "
448				    "font data");
449			}
450			set_width(atoi(ln + 9));
451		} else if (sscanf(ln, "%6x:", &curchar)) {
452			if (bytes == NULL) {
453				bytes = xmalloc(wbytes * height);
454				bytes_r = xmalloc(wbytes * height);
455			}
456			/* ln is guaranteed to have a colon here. */
457			p = strchr(ln, ':') + 1;
458			chars_per_row = strlen(p) / height;
459			dwidth = width;
460			if (chars_per_row / 2 > (width + 7) / 8)
461				dwidth *= 2; /* Double-width character. */
462			snprintf(fmt_str, sizeof (fmt_str), "%%%ux",
463			    chars_per_row);
464
465			for (i = 0; i < height; i++) {
466				sscanf(p, fmt_str, &line);
467				p += chars_per_row;
468				if (parse_bitmap_line(bytes + i * wbytes,
469				    bytes_r + i * wbytes, line, dwidth) != 0) {
470					rv = 1;
471					goto out;
472				}
473			}
474
475			if (add_char(curchar, map_idx, bytes,
476			    dwidth == width * 2 ? bytes_r : NULL) != 0) {
477				rv = 1;
478				goto out;
479			}
480		}
481	}
482out:
483	free(bytes);
484	free(bytes_r);
485	return (rv);
486}
487
488/* Read BDF header and set the font data. */
489static int
490parse_bdf_header(FILE *fp)
491{
492	char ln[BUFSIZ];
493	char spacing = '\0';	/* Should we assume C if not specified? */
494	int ret;
495
496	while (fgets(ln, sizeof (ln), fp) != NULL) {
497		ret = sscanf(ln, "FONTBOUNDINGBOX %u %u %d %d",
498		    &bbox.width, &bbox.height, &bbox.x, &bbox.y);
499		if (ret == 4)
500			continue;
501		ret = sscanf(ln, "FONT_ASCENT %u", &font_ascent);
502		if (ret == 1)
503			continue;
504		ret = sscanf(ln, "FONT_DESCENT %u", &font_descent);
505		if (ret == 1)
506			continue;
507		ret = sscanf(ln, "DEFAULT_CHAR %u", &default_char);
508		if (ret == 1) {
509			c_list[0].c = default_char;
510			s_list[0].c = default_char;
511			continue;
512		}
513		ret = sscanf(ln, "SPACING \"%c\"", &spacing);
514		if (ret == 1)
515			continue;
516		if (strncmp("ENDPROPERTIES", ln, 13) == 0)
517			break;
518	}
519	if (spacing != 'C') {
520		printf("Spacing '%c' is not supported\n", spacing);
521		return (1);
522	}
523	if (bbox.width == 0)
524		bbox.width = width;
525	set_width(bbox.width);
526
527	if (bbox.height == 0)
528		bbox.height = height;
529	height = bbox.height;
530	return (0);
531}
532
533static int
534parse_file(const char *filename, unsigned int map_idx)
535{
536	FILE *fp;
537	size_t len;
538	int rv;
539
540	fp = fopen(filename, "r");
541	if (fp == NULL) {
542		perror(filename);
543		return (1);
544	}
545	len = strlen(filename);
546	if (len > 4 && strcasecmp(filename + len - 4, ".hex") == 0) {
547		rv = parse_hex(fp, map_idx);
548	} else {
549		if ((rv = parse_bdf_header(fp)) == 0)
550			rv = parse_bdf(fp, map_idx);
551	}
552	fclose(fp);
553	return (rv);
554}
555
556static void
557number_glyphs(void)
558{
559	struct glyph *gl;
560	unsigned int i, idx = 0;
561
562	for (i = 0; i < VFNT_MAPS; i++)
563		TAILQ_FOREACH(gl, &glyphs[i], g_list)
564			gl->g_index = idx++;
565}
566
567/* Note we only deal with byte stream here. */
568static size_t
569write_glyph_source(const void *ptr, size_t size, size_t nitems, FILE *stream)
570{
571	const uint8_t *data = ptr;
572	size_t i;
573
574	size *= nitems;
575	for (i = 0; i < size; i++) {
576		if ((i % wbytes) == 0) {
577			if (fprintf(stream, "\n") < 0)
578				return (0);
579		}
580		if (fprintf(stream, "0x%02x, ", data[i]) < 0)
581			return (0);
582	}
583	if (fprintf(stream, "\n") < 0)
584		nitems = 0;
585
586	return (nitems);
587}
588
589/* Write to buffer */
590static size_t
591write_glyph_buf(const void *ptr, size_t size, size_t nitems, FILE *stream)
592{
593	static size_t index = 0;
594
595	size *= nitems;
596	(void) memmove(uncompressed + index, ptr, size);
597	index += size;
598
599	return (nitems);
600}
601
602static int
603write_glyphs(FILE *fp, vt_write cb)
604{
605	struct glyph *gl;
606	unsigned int i;
607
608	for (i = 0; i < VFNT_MAPS; i++) {
609		TAILQ_FOREACH(gl, &glyphs[i], g_list)
610			if (cb(gl->g_data, wbytes * height, 1, fp) != 1)
611				return (1);
612	}
613	return (0);
614}
615
616static void
617fold_mappings(unsigned int map_idx)
618{
619	struct mapping_list *ml = &maps[map_idx];
620	struct mapping *mn, *mp, *mbase;
621
622	mp = mbase = TAILQ_FIRST(ml);
623	for (mp = mbase = TAILQ_FIRST(ml); mp != NULL; mp = mn) {
624		mn = TAILQ_NEXT(mp, m_list);
625		if (mn != NULL && mn->m_char == mp->m_char + 1 &&
626		    mn->m_glyph->g_index == mp->m_glyph->g_index + 1)
627			continue;
628		mbase->m_length = mp->m_char - mbase->m_char + 1;
629		mbase = mp = mn;
630		map_folded_count[map_idx]++;
631	}
632}
633
634struct file_mapping {
635	uint32_t	source;
636	uint16_t	destination;
637	uint16_t	length;
638} __attribute__((packed));
639
640static int
641write_mappings(FILE *fp, unsigned int map_idx)
642{
643	struct mapping_list *ml = &maps[map_idx];
644	struct mapping *mp;
645	struct file_mapping fm;
646	unsigned int i = 0, j = 0;
647
648	TAILQ_FOREACH(mp, ml, m_list) {
649		j++;
650		if (mp->m_length > 0) {
651			i += mp->m_length;
652			fm.source = htobe32(mp->m_char);
653			fm.destination = htobe16(mp->m_glyph->g_index);
654			fm.length = htobe16(mp->m_length - 1);
655			if (fwrite(&fm, sizeof (fm), 1, fp) != 1)
656				return (1);
657		}
658	}
659	assert(i == j);
660	return (0);
661}
662
663static int
664write_source_mappings(FILE *fp, unsigned int map_idx)
665{
666	struct mapping_list *ml = &maps[map_idx];
667	struct mapping *mp;
668	unsigned int i = 0, j = 0;
669
670	TAILQ_FOREACH(mp, ml, m_list) {
671		j++;
672		if (mp->m_length > 0) {
673			i += mp->m_length;
674			if (fprintf(fp, "\t{ 0x%08x, 0x%04x, 0x%04x },\n",
675			    mp->m_char, mp->m_glyph->g_index,
676			    mp->m_length - 1) < 0)
677				return (1);
678		}
679	}
680	assert(i == j);
681	return (0);
682}
683
684struct file_header {
685	uint8_t		magic[8];
686	uint8_t		width;
687	uint8_t		height;
688	uint16_t	pad;
689	uint32_t	glyph_count;
690	uint32_t	map_count[4];
691} __attribute__((packed));
692
693static int
694write_fnt(const char *filename)
695{
696	FILE *fp;
697	struct file_header fh = {
698		.magic = "VFNT0002",
699	};
700
701	fp = fopen(filename, "wb");
702	if (fp == NULL) {
703		perror(filename);
704		return (1);
705	}
706
707	fh.width = width;
708	fh.height = height;
709	fh.glyph_count = htobe32(glyph_unique);
710	fh.map_count[0] = htobe32(map_folded_count[0]);
711	fh.map_count[1] = htobe32(map_folded_count[1]);
712	fh.map_count[2] = htobe32(map_folded_count[2]);
713	fh.map_count[3] = htobe32(map_folded_count[3]);
714	if (fwrite(&fh, sizeof (fh), 1, fp) != 1) {
715		perror(filename);
716		fclose(fp);
717		return (1);
718	}
719
720	if (write_glyphs(fp, &fwrite) != 0 ||
721	    write_mappings(fp, VFNT_MAP_NORMAL) != 0 ||
722	    write_mappings(fp, 1) != 0 ||
723	    write_mappings(fp, VFNT_MAP_BOLD) != 0 ||
724	    write_mappings(fp, 3) != 0) {
725		perror(filename);
726		fclose(fp);
727		return (1);
728	}
729
730	fclose(fp);
731	return (0);
732}
733
734static int
735write_fnt_source(bool lz4, const char *filename)
736{
737	FILE *fp;
738	int rv = 1;
739	size_t uncompressed_size = wbytes * height * glyph_unique;
740	size_t compressed_size = uncompressed_size;
741	uint8_t *compressed = NULL;
742
743	fp = fopen(filename, "w");
744	if (fp == NULL) {
745		perror(filename);
746		return (1);
747	}
748
749	if (lz4 == true) {
750		uncompressed = xmalloc(uncompressed_size);
751		compressed = xmalloc(uncompressed_size);
752	}
753	if (fprintf(fp, "/* Generated %ux%u console font source. */\n\n",
754	    width, height) < 0)
755		goto done;
756	if (fprintf(fp, "#include <sys/types.h>\n") < 0)
757		goto done;
758	if (fprintf(fp, "#include <sys/param.h>\n") < 0)
759		goto done;
760	if (fprintf(fp, "#include <sys/font.h>\n\n") < 0)
761		goto done;
762
763	/* Write font bytes. */
764	if (fprintf(fp, "static uint8_t FONTDATA_%ux%u[] = {\n",
765	    width, height) < 0)
766		goto done;
767	if (lz4 == true) {
768		if (write_glyphs(fp, &write_glyph_buf) != 0)
769			goto done;
770		compressed_size = lz4_compress(uncompressed, compressed,
771		    uncompressed_size, compressed_size, 0);
772		if (write_glyph_source(compressed, compressed_size, 1, fp) != 1)
773			goto done;
774		free(uncompressed);
775		free(compressed);
776	} else {
777		if (write_glyphs(fp, &write_glyph_source) != 0)
778			goto done;
779	}
780	if (fprintf(fp, "};\n\n") < 0)
781		goto done;
782
783	/* Write font maps. */
784	if (!TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL])) {
785		if (fprintf(fp, "static struct font_map "
786		    "FONTMAP_NORMAL_%ux%u[] = {\n", width, height) < 0)
787			goto done;
788		if (write_source_mappings(fp, VFNT_MAP_NORMAL) != 0)
789			goto done;
790		if (fprintf(fp, "};\n\n") < 0)
791			goto done;
792	}
793	if (!TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL_RH])) {
794		if (fprintf(fp, "static struct font_map "
795		    "FONTMAP_NORMAL_RH_%ux%u[] = {\n", width, height) < 0)
796			goto done;
797		if (write_source_mappings(fp, VFNT_MAP_NORMAL_RH) != 0)
798			goto done;
799		if (fprintf(fp, "};\n\n") < 0)
800			goto done;
801	}
802	if (!TAILQ_EMPTY(&maps[VFNT_MAP_BOLD])) {
803		if (fprintf(fp, "static struct font_map "
804		    "FONTMAP_BOLD_%ux%u[] = {\n", width, height) < 0)
805			goto done;
806		if (write_source_mappings(fp, VFNT_MAP_BOLD) != 0)
807			goto done;
808		if (fprintf(fp, "};\n\n") < 0)
809			goto done;
810	}
811	if (!TAILQ_EMPTY(&maps[VFNT_MAP_BOLD_RH])) {
812		if (fprintf(fp, "static struct font_map "
813		    "FONTMAP_BOLD_RH_%ux%u[] = {\n", width, height) < 0)
814			goto done;
815		if (write_source_mappings(fp, VFNT_MAP_BOLD_RH) != 0)
816			goto done;
817		if (fprintf(fp, "};\n\n") < 0)
818			goto done;
819	}
820
821	/* Write struct font. */
822	if (fprintf(fp, "struct font font_%ux%u = {\n",
823	    width, height) < 0)
824		goto done;
825	if (fprintf(fp, "\t.vf_map\t= {\n") < 0)
826		goto done;
827	if (TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL])) {
828		if (fprintf(fp, "\t\t\tNULL,\n") < 0)
829			goto done;
830	} else {
831		if (fprintf(fp, "\t\t\tFONTMAP_NORMAL_%ux%u,\n",
832		    width, height) < 0)
833			goto done;
834	}
835	if (TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL_RH])) {
836		if (fprintf(fp, "\t\t\tNULL,\n") < 0)
837			goto done;
838	} else {
839		if (fprintf(fp, "\t\t\tFONTMAP_NORMAL_RH_%ux%u,\n",
840		    width, height) < 0)
841			goto done;
842	}
843	if (TAILQ_EMPTY(&maps[VFNT_MAP_BOLD])) {
844		if (fprintf(fp, "\t\t\tNULL,\n") < 0)
845			goto done;
846	} else {
847		if (fprintf(fp, "\t\t\tFONTMAP_BOLD_%ux%u,\n",
848		    width, height) < 0)
849			goto done;
850	}
851	if (TAILQ_EMPTY(&maps[VFNT_MAP_BOLD_RH])) {
852		if (fprintf(fp, "\t\t\tNULL\n") < 0)
853			goto done;
854	} else {
855		if (fprintf(fp, "\t\t\tFONTMAP_BOLD_RH_%ux%u\n",
856		    width, height) < 0)
857			goto done;
858	}
859	if (fprintf(fp, "\t\t},\n") < 0)
860		goto done;
861	if (lz4 == true) {
862		if (fprintf(fp, "\t.vf_bytes\t= NULL,\n") < 0)
863			goto done;
864	} else {
865		if (fprintf(fp, "\t.vf_bytes\t= FONTDATA_%ux%u,\n",
866		    width, height) < 0) {
867			goto done;
868		}
869	}
870	if (fprintf(fp, "\t.vf_width\t= %u,\n", width) < 0)
871		goto done;
872	if (fprintf(fp, "\t.vf_height\t= %u,\n", height) < 0)
873		goto done;
874	if (fprintf(fp, "\t.vf_map_count\t= { %u, %u, %u, %u }\n",
875	    map_folded_count[0], map_folded_count[1], map_folded_count[2],
876	    map_folded_count[3]) < 0) {
877		goto done;
878	}
879	if (fprintf(fp, "};\n\n") < 0)
880		goto done;
881
882	/* Write bitmap data. */
883	if (fprintf(fp, "bitmap_data_t font_data_%ux%u = {\n",
884	    width, height) < 0)
885		goto done;
886	if (fprintf(fp, "\t.width\t= %u,\n", width) < 0)
887		goto done;
888	if (fprintf(fp, "\t.height\t= %u,\n", height) < 0)
889		goto done;
890	if (lz4 == true) {
891		if (fprintf(fp, "\t.compressed_size\t= %u,\n",
892		    compressed_size) < 0) {
893			goto done;
894		}
895		if (fprintf(fp, "\t.uncompressed_size\t= %u,\n",
896		    uncompressed_size) < 0) {
897			goto done;
898		}
899		if (fprintf(fp, "\t.compressed_data\t= FONTDATA_%ux%u,\n",
900		    width, height) < 0) {
901			goto done;
902		}
903	} else {
904		if (fprintf(fp, "\t.compressed_size\t= 0,\n") < 0)
905			goto done;
906		if (fprintf(fp, "\t.uncompressed_size\t= %u,\n",
907		    uncompressed_size) < 0) {
908			goto done;
909		}
910		if (fprintf(fp, "\t.compressed_data\t= NULL,\n") < 0)
911			goto done;
912	}
913	if (fprintf(fp, "\t.font = &font_%ux%u\n", width, height) < 0)
914		goto done;
915	if (fprintf(fp, "};\n") < 0)
916		goto done;
917
918	rv = 0;
919done:
920	if (rv != 0)
921		perror(filename);
922	fclose(fp);
923	return (0);
924}
925
926static void
927print_font_info(void)
928{
929	printf(
930"Statistics:\n"
931"- glyph_total:                 %6u\n"
932"- glyph_normal:                %6u\n"
933"- glyph_normal_right:          %6u\n"
934"- glyph_bold:                  %6u\n"
935"- glyph_bold_right:            %6u\n"
936"- glyph_unique:                %6u\n"
937"- glyph_dupe:                  %6u\n"
938"- mapping_total:               %6u\n"
939"- mapping_normal:              %6u\n"
940"- mapping_normal_folded:       %6u\n"
941"- mapping_normal_right:        %6u\n"
942"- mapping_normal_right_folded: %6u\n"
943"- mapping_bold:                %6u\n"
944"- mapping_bold_folded:         %6u\n"
945"- mapping_bold_right:          %6u\n"
946"- mapping_bold_right_folded:   %6u\n"
947"- mapping_unique:              %6u\n"
948"- mapping_dupe:                %6u\n",
949	    glyph_total,
950	    glyph_count[0],
951	    glyph_count[1],
952	    glyph_count[2],
953	    glyph_count[3],
954	    glyph_unique, glyph_dupe,
955	    mapping_total,
956	    map_count[0], map_folded_count[0],
957	    map_count[1], map_folded_count[1],
958	    map_count[2], map_folded_count[2],
959	    map_count[3], map_folded_count[3],
960	    mapping_unique, mapping_dupe);
961}
962
963int
964main(int argc, char *argv[])
965{
966	int ch, val, verbose = 0, rv = 0;
967	char *outfile = NULL;
968
969	assert(sizeof (struct file_header) == 32);
970	assert(sizeof (struct file_mapping) == 8);
971
972	while ((ch = getopt(argc, argv, "nf:h:vw:o:")) != -1) {
973		switch (ch) {
974		case 'f':
975			if (strcmp(optarg, "font") == 0)
976				format = VT_FONT;
977			else if (strcmp(optarg, "source") == 0)
978				format = VT_C_SOURCE;
979			else if (strcmp(optarg, "compressed-source") == 0)
980				format = VT_C_COMPRESSED;
981			else
982				errx(1, "Invalid format: %s", optarg);
983			break;
984		case 'h':
985			val = atoi(optarg);
986			if (val <= 0 || val > 128)
987				errx(1, "Invalid height %d", val);
988			height = val;
989			break;
990		case 'n':
991			filter = false;
992			break;
993		case 'o':
994			outfile = optarg;
995			break;
996		case 'v':
997			verbose = 1;
998			break;
999		case 'w':
1000			set_width(atoi(optarg));
1001			break;
1002		case '?':
1003		default:
1004			usage();
1005		}
1006	}
1007	argc -= optind;
1008	argv += optind;
1009
1010	if (outfile == NULL || argc < 1 || argc > 2)
1011		usage();
1012
1013	wbytes = howmany(width, 8);
1014
1015	if (parse_file(argv[0], VFNT_MAP_NORMAL) != 0)
1016		return (1);
1017	argc--;
1018	argv++;
1019	if (argc == 1) {
1020		if (parse_file(argv[0], VFNT_MAP_BOLD) != 0)
1021			return (1);
1022		argc--;
1023		argv++;
1024	}
1025	number_glyphs();
1026	dedup_mapping(VFNT_MAP_BOLD);
1027	dedup_mapping(VFNT_MAP_BOLD_RH);
1028	fold_mappings(0);
1029	fold_mappings(1);
1030	fold_mappings(2);
1031	fold_mappings(3);
1032
1033	switch (format) {
1034	case VT_FONT:
1035		rv = write_fnt(outfile);
1036		break;
1037	case VT_C_SOURCE:
1038		rv = write_fnt_source(false, outfile);
1039		break;
1040	case VT_C_COMPRESSED:
1041		rv = write_fnt_source(true, outfile);
1042		break;
1043	}
1044
1045	if (verbose)
1046		print_font_info();
1047
1048	return (rv);
1049}
1050