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/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33#include <sys/types.h>
34#include <sys/fnv_hash.h>
35#include <sys/endian.h>
36#include <sys/param.h>
37#include <sys/queue.h>
38#include <sys/font.h>
39
40#include <assert.h>
41#include <err.h>
42#include <stdint.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <stdbool.h>
46#include <string.h>
47#include <unistd.h>
48#include <lz4.h>
49
50#define VFNT_MAXGLYPHS 131072
51#define VFNT_MAXDIMENSION 128
52
53static unsigned int width = 8, wbytes, height = 16;
54
55struct glyph {
56	TAILQ_ENTRY(glyph)	 g_list;
57	SLIST_ENTRY(glyph)	 g_hash;
58	uint8_t			*g_data;
59	unsigned int		 g_index;
60};
61
62#define	FONTCVT_NHASH 4096
63TAILQ_HEAD(glyph_list, glyph);
64static SLIST_HEAD(, glyph) glyph_hash[FONTCVT_NHASH];
65static struct glyph_list glyphs[VFNT_MAPS] = {
66	TAILQ_HEAD_INITIALIZER(glyphs[0]),
67	TAILQ_HEAD_INITIALIZER(glyphs[1]),
68	TAILQ_HEAD_INITIALIZER(glyphs[2]),
69	TAILQ_HEAD_INITIALIZER(glyphs[3]),
70};
71static unsigned int glyph_total, glyph_count[4], glyph_unique, glyph_dupe;
72
73struct mapping {
74	TAILQ_ENTRY(mapping)	 m_list;
75	unsigned int		 m_char;
76	unsigned int		 m_length;
77	struct glyph		*m_glyph;
78};
79
80TAILQ_HEAD(mapping_list, mapping);
81static struct mapping_list maps[VFNT_MAPS] = {
82	TAILQ_HEAD_INITIALIZER(maps[0]),
83	TAILQ_HEAD_INITIALIZER(maps[1]),
84	TAILQ_HEAD_INITIALIZER(maps[2]),
85	TAILQ_HEAD_INITIALIZER(maps[3]),
86};
87static unsigned int mapping_total, map_count[4], map_folded_count[4],
88    mapping_unique, mapping_dupe;
89
90enum output_format {
91	VT_FONT,		/* default */
92	VT_C_SOURCE,		/* C source for built in fonts */
93	VT_C_COMPRESSED		/* C source with compressed font data */
94};
95
96struct whitelist {
97	uint32_t c;
98	uint32_t len;
99};
100
101/*
102 * Compressed font glyph list. To be used with boot loader, we need to have
103 * ascii set and box drawing chars.
104 */
105static struct whitelist c_list[] = {
106	{ .c = 0, .len = 0 },		/* deault char */
107	{ .c = 0x20, .len = 0x5f },
108	{ .c = 0x2500, .len = 0 },	/* single frame */
109	{ .c = 0x2502, .len = 0 },
110	{ .c = 0x250c, .len = 0 },
111	{ .c = 0x2510, .len = 0 },
112	{ .c = 0x2514, .len = 0 },
113	{ .c = 0x2518, .len = 0 },
114	{ .c = 0x2550, .len = 1 },	/* double frame */
115	{ .c = 0x2554, .len = 0 },
116	{ .c = 0x2557, .len = 0 },
117	{ .c = 0x255a, .len = 0 },
118	{ .c = 0x255d, .len = 0 },
119};
120
121/*
122 * Uncompressed source. For x86 we need cp437 so the vga text mode
123 * can program font into the vga card.
124 */
125static struct whitelist s_list[] = {
126	{ .c = 0, .len = 0 },		/* deault char */
127	{ .c = 0x20, .len = 0x5f },	/* ascii set */
128	{ .c = 0xA0, .len = 0x5f },	/* latin 1 */
129	{ .c = 0x0192, .len = 0 },
130	{ .c = 0x0332, .len = 0 },	/* composing lower line */
131	{ .c = 0x0393, .len = 0 },
132	{ .c = 0x0398, .len = 0 },
133	{ .c = 0x03A3, .len = 0 },
134	{ .c = 0x03A6, .len = 0 },
135	{ .c = 0x03A9, .len = 0 },
136	{ .c = 0x03B1, .len = 1 },
137	{ .c = 0x03B4, .len = 0 },
138	{ .c = 0x03C0, .len = 0 },
139	{ .c = 0x03C3, .len = 0 },
140	{ .c = 0x03C4, .len = 0 },
141	{ .c = 0x207F, .len = 0 },
142	{ .c = 0x20A7, .len = 0 },
143	{ .c = 0x2205, .len = 0 },
144	{ .c = 0x220A, .len = 0 },
145	{ .c = 0x2219, .len = 1 },
146	{ .c = 0x221E, .len = 0 },
147	{ .c = 0x2229, .len = 0 },
148	{ .c = 0x2248, .len = 0 },
149	{ .c = 0x2261, .len = 0 },
150	{ .c = 0x2264, .len = 1 },
151	{ .c = 0x2310, .len = 0 },
152	{ .c = 0x2320, .len = 1 },
153	{ .c = 0x2500, .len = 0 },
154	{ .c = 0x2502, .len = 0 },
155	{ .c = 0x250C, .len = 0 },
156	{ .c = 0x2510, .len = 0 },
157	{ .c = 0x2514, .len = 0 },
158	{ .c = 0x2518, .len = 0 },
159	{ .c = 0x251C, .len = 0 },
160	{ .c = 0x2524, .len = 0 },
161	{ .c = 0x252C, .len = 0 },
162	{ .c = 0x2534, .len = 0 },
163	{ .c = 0x253C, .len = 0 },
164	{ .c = 0x2550, .len = 0x1c },
165	{ .c = 0x2580, .len = 0 },
166	{ .c = 0x2584, .len = 0 },
167	{ .c = 0x2588, .len = 0 },
168	{ .c = 0x258C, .len = 0 },
169	{ .c = 0x2590, .len = 3 },
170	{ .c = 0x25A0, .len = 0 },
171};
172
173static bool filter = true;
174static enum output_format format = VT_FONT;
175/* Type for write callback. */
176typedef size_t (*vt_write)(const void *, size_t, size_t, FILE *);
177static uint8_t *uncompressed;
178
179static void
180usage(void)
181{
182
183	(void)fprintf(stderr, "usage: vtfontcvt "
184	    "[-n] [-f font|source|compressed-source] [-w width] "
185	    "[-h height]\n\t[-v] normal.bdf [bold.bdf] out.fnt\n");
186	exit(1);
187}
188
189static void *
190xmalloc(size_t size)
191{
192	void *m;
193
194	if ((m = calloc(1, size)) == NULL)
195		errx(1, "memory allocation failure");
196	return (m);
197}
198
199static int
200add_mapping(struct glyph *gl, unsigned int c, unsigned int map_idx)
201{
202	struct mapping *mp, *mp_temp;
203	struct mapping_list *ml;
204
205	mapping_total++;
206
207	mp = xmalloc(sizeof *mp);
208	mp->m_char = c;
209	mp->m_glyph = gl;
210	mp->m_length = 0;
211
212	ml = &maps[map_idx];
213	if (TAILQ_LAST(ml, mapping_list) == NULL ||
214	    TAILQ_LAST(ml, mapping_list)->m_char < c) {
215		/* Common case: empty list or new char at end of list. */
216		TAILQ_INSERT_TAIL(ml, mp, m_list);
217	} else {
218		/* Find insertion point for char; cannot be at end. */
219		TAILQ_FOREACH(mp_temp, ml, m_list) {
220			if (mp_temp->m_char >= c) {
221				TAILQ_INSERT_BEFORE(mp_temp, mp, m_list);
222				break;
223			}
224		}
225	}
226
227	map_count[map_idx]++;
228	mapping_unique++;
229
230	return (0);
231}
232
233static int
234dedup_mapping(unsigned int map_idx)
235{
236	struct mapping *mp_bold, *mp_normal, *mp_temp;
237	unsigned normal_map_idx = map_idx - VFNT_MAP_BOLD;
238
239	assert(map_idx == VFNT_MAP_BOLD || map_idx == VFNT_MAP_BOLD_RIGHT);
240	mp_normal = TAILQ_FIRST(&maps[normal_map_idx]);
241	TAILQ_FOREACH_SAFE(mp_bold, &maps[map_idx], m_list, mp_temp) {
242		while (mp_normal->m_char < mp_bold->m_char)
243			mp_normal = TAILQ_NEXT(mp_normal, m_list);
244		if (mp_bold->m_char != mp_normal->m_char)
245			errx(1, "Character %u not in normal font!",
246			    mp_bold->m_char);
247		if (mp_bold->m_glyph != mp_normal->m_glyph)
248			continue;
249
250		/* No mapping is needed if it's equal to the normal mapping. */
251		TAILQ_REMOVE(&maps[map_idx], mp_bold, m_list);
252		free(mp_bold);
253		mapping_dupe++;
254	}
255	return (0);
256}
257
258static struct glyph *
259add_glyph(const uint8_t *bytes, unsigned int map_idx, int fallback)
260{
261	struct glyph *gl;
262	int hash;
263
264	glyph_total++;
265	glyph_count[map_idx]++;
266
267	/* Return existing glyph if we have an identical one. */
268	hash = fnv_32_buf(bytes, wbytes * height, FNV1_32_INIT) % FONTCVT_NHASH;
269	SLIST_FOREACH(gl, &glyph_hash[hash], g_hash) {
270		if (memcmp(gl->g_data, bytes, wbytes * height) == 0) {
271			glyph_dupe++;
272			return (gl);
273		}
274	}
275
276	/* Allocate new glyph. */
277	gl = xmalloc(sizeof *gl);
278	gl->g_data = xmalloc(wbytes * height);
279	memcpy(gl->g_data, bytes, wbytes * height);
280	if (fallback)
281		TAILQ_INSERT_HEAD(&glyphs[map_idx], gl, g_list);
282	else
283		TAILQ_INSERT_TAIL(&glyphs[map_idx], gl, g_list);
284	SLIST_INSERT_HEAD(&glyph_hash[hash], gl, g_hash);
285
286	glyph_unique++;
287	if (glyph_unique > VFNT_MAXGLYPHS)
288		errx(1, "too many glyphs (%u)", glyph_unique);
289	return (gl);
290}
291
292static bool
293check_whitelist(unsigned c)
294{
295	struct whitelist *w = NULL;
296	int i, n = 0;
297
298	if (filter == false)
299		return (true);
300
301	if (format == VT_C_SOURCE) {
302		w = s_list;
303		n = sizeof (s_list) / sizeof (s_list[0]);
304	}
305	if (format == VT_C_COMPRESSED) {
306		w = c_list;
307		n = sizeof (c_list) / sizeof (c_list[0]);
308	}
309	if (w == NULL)
310		return (true);
311	for (i = 0; i < n; i++) {
312		if (c >= w[i].c && c <= w[i].c + w[i].len)
313			return (true);
314	}
315	return (false);
316}
317
318static int
319add_char(unsigned curchar, unsigned map_idx, uint8_t *bytes, uint8_t *bytes_r)
320{
321	struct glyph *gl;
322
323	/* Prevent adding two glyphs for 0xFFFD */
324	if (curchar == 0xFFFD) {
325		if (map_idx < VFNT_MAP_BOLD)
326			gl = add_glyph(bytes, 0, 1);
327	} else if (filter == false || curchar >= 0x20) {
328		gl = add_glyph(bytes, map_idx, 0);
329		if (add_mapping(gl, curchar, map_idx) != 0)
330			return (1);
331		if (bytes_r != NULL) {
332			gl = add_glyph(bytes_r, map_idx + 1, 0);
333			if (add_mapping(gl, curchar, map_idx + 1) != 0)
334				return (1);
335		}
336	}
337	return (0);
338}
339
340/*
341 * Right-shift glyph row.
342 */
343static void
344rshift_row(uint8_t *buf, size_t len, size_t shift)
345{
346	ssize_t i, off_byte = shift / 8;
347	size_t off_bit = shift % 8;
348
349	if (shift == 0)
350		return;
351	for (i = len - 1; i >= 0; i--)
352		buf[i] = (i >= off_byte ? buf[i - off_byte] >> off_bit : 0) |
353		    (i > off_byte ? buf[i - off_byte - 1] << (8 - off_bit) : 0);
354}
355
356/*
357 * Split double-width characters into left and right half. Single-width
358 * characters in _left_ only.
359 */
360static int
361split_row(uint8_t *left, uint8_t *right, uint8_t *line, size_t w)
362{
363	size_t s, i;
364
365	s = wbytes * 8 - width;
366
367	memcpy(left, line, wbytes);
368	*(left + wbytes - 1) &= 0xFF << s;
369
370	if (w > width) { /* Double-width character. */
371		uint8_t t;
372
373		for (i = 0; i < wbytes; i++) {
374			t = *(line + wbytes + i - 1);
375			t <<= 8 - s;
376			t |= *(line + wbytes + i) >> s;
377			*(right + i) = t;
378		}
379		*(right + wbytes - 1) &= 0xFF << s;
380	}
381	return (0);
382}
383
384static void
385set_height(int h)
386{
387	if (h <= 0 || h > VFNT_MAXDIMENSION)
388		errx(1, "invalid height %d", h);
389	height = h;
390}
391
392static void
393set_width(int w)
394{
395	if (w <= 0 || w > VFNT_MAXDIMENSION)
396		errx(1, "invalid width %d", w);
397	width = w;
398	wbytes = howmany(width, 8);
399}
400
401static int
402parse_bdf(FILE *fp, unsigned int map_idx)
403{
404	char *ln, *p;
405	size_t length;
406	uint8_t *line, *bytes, *bytes_r;
407	unsigned int curchar = 0, i, j, linenum = 0, bbwbytes;
408	int bbw, bbh, bbox, bboy;		/* Glyph bounding box. */
409	int fbbw = 0, fbbh, fbbox, fbboy;	/* Font bounding box. */
410	int dwidth = 0, dwy = 0;
411	int rv = -1;
412	char spc = '\0';
413
414	/*
415	 * Step 1: Parse FONT logical font descriptor and FONTBOUNDINGBOX
416	 * bounding box.
417	 */
418	while ((ln = fgetln(fp, &length)) != NULL) {
419		linenum++;
420		ln[length - 1] = '\0';
421
422		if (strncmp(ln, "FONT ", 5) == 0) {
423			p = ln + 5;
424			i = 0;
425			while ((p = strchr(p, '-')) != NULL) {
426				p++;
427				i++;
428				if (i == 11) {
429					spc = *p;
430					break;
431				}
432			}
433		} else if (strncmp(ln, "FONTBOUNDINGBOX ", 16) == 0) {
434			if (sscanf(ln + 16, "%d %d %d %d", &fbbw, &fbbh, &fbbox,
435			    &fbboy) != 4)
436				errx(1, "invalid FONTBOUNDINGBOX at line %u",
437				    linenum);
438			set_width(fbbw);
439			set_height(fbbh);
440			break;
441		}
442	}
443	if (fbbw == 0)
444		errx(1, "broken font header");
445	if (spc != 'c' && spc != 'C')
446		errx(1, "font spacing \"C\" (character cell) required");
447
448	/* Step 2: Validate DWIDTH (Device Width) of all glyphs. */
449	while ((ln = fgetln(fp, &length)) != NULL) {
450		linenum++;
451		ln[length - 1] = '\0';
452
453		if (strncmp(ln, "DWIDTH ", 7) == 0) {
454			if (sscanf(ln + 7, "%d %d", &dwidth, &dwy) != 2)
455				errx(1, "invalid DWIDTH at line %u", linenum);
456			if (dwy != 0 || (dwidth != fbbw && dwidth * 2 != fbbw))
457				errx(1, "bitmap with unsupported DWIDTH %d %d at line %u",
458				    dwidth, dwy, linenum);
459			if (dwidth < fbbw)
460				set_width(dwidth);
461		}
462	}
463
464	/* Step 3: Restart at the beginning of the file and read glyph data. */
465	dwidth = bbw = bbh = 0;
466	rewind(fp);
467	linenum = 0;
468	bbwbytes = 0; /* GCC 4.2.1 "may be used uninitialized" workaround. */
469	bytes = xmalloc(wbytes * height);
470	bytes_r = xmalloc(wbytes * height);
471	line = xmalloc(wbytes * 2);
472	while ((ln = fgetln(fp, &length)) != NULL) {
473		linenum++;
474		ln[length - 1] = '\0';
475
476		if (strncmp(ln, "ENCODING ", 9) == 0) {
477			curchar = atoi(ln + 9);
478		} else if (strncmp(ln, "DWIDTH ", 7) == 0) {
479			dwidth = atoi(ln + 7);
480		} else if (strncmp(ln, "BBX ", 4) == 0) {
481			if (sscanf(ln + 4, "%d %d %d %d", &bbw, &bbh, &bbox,
482			     &bboy) != 4)
483				errx(1, "invalid BBX at line %u", linenum);
484			if (bbw < 1 || bbh < 1 || bbw > fbbw || bbh > fbbh ||
485			    bbox < fbbox || bboy < fbboy ||
486			    bbh + bboy > fbbh + fbboy)
487				errx(1, "broken bitmap with BBX %d %d %d %d at line %u",
488				    bbw, bbh, bbox, bboy, linenum);
489			bbwbytes = howmany(bbw, 8);
490		} else if (strncmp(ln, "BITMAP", 6) == 0 &&
491		    (ln[6] == ' ' || ln[6] == '\0')) {
492			if (dwidth == 0 || bbw == 0 || bbh == 0)
493				errx(1, "broken char header at line %u!",
494				    linenum);
495			memset(bytes, 0, wbytes * height);
496			memset(bytes_r, 0, wbytes * height);
497
498			/*
499			 * Assume that the next _bbh_ lines are bitmap data.
500			 * ENDCHAR is allowed to terminate the bitmap
501			 * early but is not otherwise checked; any extra data
502			 * is ignored.
503			 */
504			for (i = (fbbh + fbboy) - (bbh + bboy);
505			    i < (unsigned int)((fbbh + fbboy) - bboy); i++) {
506				if ((ln = fgetln(fp, &length)) == NULL)
507					errx(1, "unexpected EOF");
508				linenum++;
509				ln[length - 1] = '\0';
510				if (strcmp(ln, "ENDCHAR") == 0)
511					break;
512				if (strlen(ln) < bbwbytes * 2)
513					errx(1, "broken bitmap at line %u",
514					    linenum);
515				memset(line, 0, wbytes * 2);
516				for (j = 0; j < bbwbytes; j++) {
517					unsigned int val;
518					if (sscanf(ln + j * 2, "%2x", &val) ==
519					    0)
520						break;
521					*(line + j) = (uint8_t)val;
522				}
523
524				rshift_row(line, wbytes * 2, bbox - fbbox);
525				rv = split_row(bytes + i * wbytes,
526				     bytes_r + i * wbytes, line, dwidth);
527				if (rv != 0)
528					goto out;
529			}
530
531			if (check_whitelist(curchar) == true) {
532				rv = add_char(curchar, map_idx, bytes,
533				    dwidth > (int)width ? bytes_r : NULL);
534				if (rv != 0)
535					goto out;
536			}
537
538			dwidth = bbw = bbh = 0;
539		}
540	}
541
542out:
543	free(bytes);
544	free(bytes_r);
545	free(line);
546	return (rv);
547}
548
549static int
550parse_hex(FILE *fp, unsigned int map_idx)
551{
552	char *ln, *p;
553	size_t length;
554	uint8_t *bytes = NULL, *bytes_r = NULL, *line = NULL;
555	unsigned curchar = 0, gwidth, gwbytes, i, j, chars_per_row;
556	int rv = 0;
557
558	while ((ln = fgetln(fp, &length)) != NULL) {
559		ln[length - 1] = '\0';
560
561		if (strncmp(ln, "# Height: ", 10) == 0) {
562			if (bytes != NULL)
563				errx(1, "malformed input: Height tag after font data");
564			set_height(atoi(ln + 10));
565		} else if (strncmp(ln, "# Width: ", 9) == 0) {
566			if (bytes != NULL)
567				errx(1, "malformed input: Width tag after font data");
568			set_width(atoi(ln + 9));
569		} else if (sscanf(ln, "%6x:", &curchar)) {
570			if (bytes == NULL) {
571				bytes = xmalloc(wbytes * height);
572				bytes_r = xmalloc(wbytes * height);
573				line = xmalloc(wbytes * 2);
574			}
575			/* ln is guaranteed to have a colon here. */
576			p = strchr(ln, ':') + 1;
577			chars_per_row = strlen(p) / height;
578			if (chars_per_row < wbytes * 2)
579				errx(1,
580				    "malformed input: broken bitmap, character %06x",
581				    curchar);
582			gwidth = width * 2;
583			gwbytes = howmany(gwidth, 8);
584			if (chars_per_row < gwbytes * 2 || gwidth <= 8) {
585				gwidth = width; /* Single-width character. */
586				gwbytes = wbytes;
587			}
588
589			for (i = 0; i < height; i++) {
590				for (j = 0; j < gwbytes; j++) {
591					unsigned int val;
592					if (sscanf(p + j * 2, "%2x", &val) == 0)
593						break;
594					*(line + j) = (uint8_t)val;
595				}
596				rv = split_row(bytes + i * wbytes,
597				    bytes_r + i * wbytes, line, gwidth);
598				if (rv != 0)
599					goto out;
600				p += gwbytes * 2;
601			}
602
603			if (check_whitelist(curchar) == true) {
604				rv = add_char(curchar, map_idx, bytes,
605				    gwidth != width ? bytes_r : NULL);
606				if (rv != 0)
607					goto out;
608			}
609		}
610	}
611out:
612	free(bytes);
613	free(bytes_r);
614	free(line);
615	return (rv);
616}
617
618static int
619parse_file(const char *filename, unsigned int map_idx)
620{
621	FILE *fp;
622	size_t len;
623	int rv;
624
625	fp = fopen(filename, "r");
626	if (fp == NULL) {
627		perror(filename);
628		return (1);
629	}
630	len = strlen(filename);
631	if (len > 4 && strcasecmp(filename + len - 4, ".hex") == 0)
632		rv = parse_hex(fp, map_idx);
633	else
634		rv = parse_bdf(fp, map_idx);
635	fclose(fp);
636	return (rv);
637}
638
639static void
640number_glyphs(void)
641{
642	struct glyph *gl;
643	unsigned int i, idx = 0;
644
645	for (i = 0; i < VFNT_MAPS; i++)
646		TAILQ_FOREACH(gl, &glyphs[i], g_list)
647			gl->g_index = idx++;
648}
649
650/* Note we only deal with byte stream here. */
651static size_t
652write_glyph_source(const void *ptr, size_t size, size_t nitems, FILE *stream)
653{
654	const uint8_t *data = ptr;
655	size_t i;
656
657	size *= nitems;
658	for (i = 0; i < size; i++) {
659		if ((i % wbytes) == 0) {
660			if (fprintf(stream, "\n") < 0)
661				return (0);
662		}
663		if (fprintf(stream, "0x%02x, ", data[i]) < 0)
664			return (0);
665	}
666	if (fprintf(stream, "\n") < 0)
667		nitems = 0;
668
669	return (nitems);
670}
671
672/* Write to buffer */
673static size_t
674write_glyph_buf(const void *ptr, size_t size, size_t nitems,
675    FILE *stream __unused)
676{
677	static size_t index = 0;
678
679	size *= nitems;
680	(void) memmove(uncompressed + index, ptr, size);
681	index += size;
682
683	return (nitems);
684}
685
686static int
687write_glyphs(FILE *fp, vt_write cb)
688{
689	struct glyph *gl;
690	unsigned int i;
691
692	for (i = 0; i < VFNT_MAPS; i++) {
693		TAILQ_FOREACH(gl, &glyphs[i], g_list)
694			if (cb(gl->g_data, wbytes * height, 1, fp) != 1)
695				return (1);
696	}
697	return (0);
698}
699
700static void
701fold_mappings(unsigned int map_idx)
702{
703	struct mapping_list *ml = &maps[map_idx];
704	struct mapping *mn, *mp, *mbase;
705
706	mp = mbase = TAILQ_FIRST(ml);
707	for (mp = mbase = TAILQ_FIRST(ml); mp != NULL; mp = mn) {
708		mn = TAILQ_NEXT(mp, m_list);
709		if (mn != NULL && mn->m_char == mp->m_char + 1 &&
710		    mn->m_glyph->g_index == mp->m_glyph->g_index + 1)
711			continue;
712		mbase->m_length = mp->m_char - mbase->m_char + 1;
713		mbase = mp = mn;
714		map_folded_count[map_idx]++;
715	}
716}
717
718static int
719write_mappings(FILE *fp, unsigned int map_idx)
720{
721	struct mapping_list *ml = &maps[map_idx];
722	struct mapping *mp;
723	vfnt_map_t fm;
724	unsigned int i = 0, j = 0;
725
726	TAILQ_FOREACH(mp, ml, m_list) {
727		j++;
728		if (mp->m_length > 0) {
729			i += mp->m_length;
730			fm.vfm_src = htobe32(mp->m_char);
731			fm.vfm_dst = htobe16(mp->m_glyph->g_index);
732			fm.vfm_len = htobe16(mp->m_length - 1);
733			if (fwrite(&fm, sizeof fm, 1, fp) != 1)
734				return (1);
735		}
736	}
737	assert(i == j);
738	return (0);
739}
740
741static int
742write_source_mappings(FILE *fp, unsigned int map_idx)
743{
744	struct mapping_list *ml = &maps[map_idx];
745	struct mapping *mp;
746	unsigned int i = 0, j = 0;
747
748	TAILQ_FOREACH(mp, ml, m_list) {
749		j++;
750		if (mp->m_length > 0) {
751			i += mp->m_length;
752			if (fprintf(fp, "\t{ 0x%08x, 0x%04x, 0x%04x },\n",
753			    mp->m_char, mp->m_glyph->g_index,
754			    mp->m_length - 1) < 0)
755				return (1);
756		}
757	}
758	assert(i == j);
759	return (0);
760}
761
762static int
763write_fnt(const char *filename)
764{
765	FILE *fp;
766	struct font_header fh = {
767		.fh_magic = FONT_HEADER_MAGIC,
768	};
769
770	fp = fopen(filename, "wb");
771	if (fp == NULL) {
772		perror(filename);
773		return (1);
774	}
775
776	fh.fh_width = width;
777	fh.fh_height = height;
778	fh.fh_glyph_count = htobe32(glyph_unique);
779	fh.fh_map_count[0] = htobe32(map_folded_count[0]);
780	fh.fh_map_count[1] = htobe32(map_folded_count[1]);
781	fh.fh_map_count[2] = htobe32(map_folded_count[2]);
782	fh.fh_map_count[3] = htobe32(map_folded_count[3]);
783	if (fwrite(&fh, sizeof(fh), 1, fp) != 1) {
784		perror(filename);
785		fclose(fp);
786		return (1);
787	}
788
789	if (write_glyphs(fp, &fwrite) != 0 ||
790	    write_mappings(fp, VFNT_MAP_NORMAL) != 0 ||
791	    write_mappings(fp, VFNT_MAP_NORMAL_RIGHT) != 0 ||
792	    write_mappings(fp, VFNT_MAP_BOLD) != 0 ||
793	    write_mappings(fp, VFNT_MAP_BOLD_RIGHT) != 0) {
794		perror(filename);
795		fclose(fp);
796		return (1);
797	}
798
799	fclose(fp);
800	return (0);
801}
802
803static int
804write_fnt_source(bool lz4, const char *filename)
805{
806	FILE *fp;
807	int rv = 1;
808	size_t uncompressed_size = wbytes * height * glyph_unique;
809	size_t compressed_size = uncompressed_size;
810	uint8_t *compressed = NULL;
811
812	fp = fopen(filename, "w");
813	if (fp == NULL) {
814		perror(filename);
815		return (1);
816	}
817
818	if (lz4 == true) {
819		uncompressed = xmalloc(uncompressed_size);
820		compressed = xmalloc(uncompressed_size);
821	}
822	if (fprintf(fp, "/* Generated %ux%u console font source. */\n\n",
823	    width, height) < 0)
824		goto done;
825	if (fprintf(fp, "#include <sys/types.h>\n") < 0)
826		goto done;
827	if (fprintf(fp, "#include <sys/param.h>\n") < 0)
828		goto done;
829	if (fprintf(fp, "#include <sys/font.h>\n\n") < 0)
830		goto done;
831
832	/* Write font bytes. */
833	if (fprintf(fp, "static uint8_t FONTDATA_%ux%u[] = {\n",
834	    width, height) < 0)
835		goto done;
836	if (lz4 == true) {
837		if (write_glyphs(fp, &write_glyph_buf) != 0)
838			goto done;
839		compressed_size = lz4_compress(uncompressed, compressed,
840		    uncompressed_size, compressed_size, 0);
841		if (write_glyph_source(compressed, compressed_size, 1, fp) != 1)
842			goto done;
843		free(uncompressed);
844		free(compressed);
845	} else {
846		if (write_glyphs(fp, &write_glyph_source) != 0)
847			goto done;
848	}
849	if (fprintf(fp, "};\n\n") < 0)
850	goto done;
851
852	/* Write font maps. */
853	if (!TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL])) {
854		if (fprintf(fp, "static vfnt_map_t "
855		    "FONTMAP_NORMAL_%ux%u[] = {\n", width, height) < 0)
856			goto done;
857		if (write_source_mappings(fp, VFNT_MAP_NORMAL) != 0)
858			goto done;
859		if (fprintf(fp, "};\n\n") < 0)
860			goto done;
861	}
862	if (!TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL_RIGHT])) {
863		if (fprintf(fp, "static vfnt_map_t "
864		    "FONTMAP_NORMAL_RH_%ux%u[] = {\n", width, height) < 0)
865			goto done;
866		if (write_source_mappings(fp, VFNT_MAP_NORMAL_RIGHT) != 0)
867			goto done;
868		if (fprintf(fp, "};\n\n") < 0)
869			goto done;
870	}
871	if (!TAILQ_EMPTY(&maps[VFNT_MAP_BOLD])) {
872		if (fprintf(fp, "static vfnt_map_t "
873		    "FONTMAP_BOLD_%ux%u[] = {\n", width, height) < 0)
874			goto done;
875		if (write_source_mappings(fp, VFNT_MAP_BOLD) != 0)
876			goto done;
877		if (fprintf(fp, "};\n\n") < 0)
878			goto done;
879	}
880	if (!TAILQ_EMPTY(&maps[VFNT_MAP_BOLD_RIGHT])) {
881		if (fprintf(fp, "static vfnt_map_t "
882		    "FONTMAP_BOLD_RH_%ux%u[] = {\n", width, height) < 0)
883			goto done;
884		if (write_source_mappings(fp, VFNT_MAP_BOLD_RIGHT) != 0)
885			goto done;
886		if (fprintf(fp, "};\n\n") < 0)
887			goto done;
888	}
889
890	/* Write struct font. */
891	if (fprintf(fp, "struct vt_font font_%ux%u = {\n",
892	    width, height) < 0)
893		goto done;
894	if (fprintf(fp, "\t.vf_map\t= {\n") < 0)
895		goto done;
896	if (TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL])) {
897		if (fprintf(fp, "\t\t\tNULL,\n") < 0)
898			goto done;
899	} else {
900		if (fprintf(fp, "\t\t\tFONTMAP_NORMAL_%ux%u,\n",
901		    width, height) < 0)
902			goto done;
903	}
904	if (TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL_RIGHT])) {
905		if (fprintf(fp, "\t\t\tNULL,\n") < 0)
906			goto done;
907	} else {
908		if (fprintf(fp, "\t\t\tFONTMAP_NORMAL_RH_%ux%u,\n",
909		    width, height) < 0)
910			goto done;
911	}
912	if (TAILQ_EMPTY(&maps[VFNT_MAP_BOLD])) {
913		if (fprintf(fp, "\t\t\tNULL,\n") < 0)
914			goto done;
915	} else {
916		if (fprintf(fp, "\t\t\tFONTMAP_BOLD_%ux%u,\n",
917		    width, height) < 0)
918			goto done;
919	}
920	if (TAILQ_EMPTY(&maps[VFNT_MAP_BOLD_RIGHT])) {
921		if (fprintf(fp, "\t\t\tNULL\n") < 0)
922			goto done;
923	} else {
924		if (fprintf(fp, "\t\t\tFONTMAP_BOLD_RH_%ux%u\n",
925		    width, height) < 0)
926			goto done;
927	}
928	if (fprintf(fp, "\t\t},\n") < 0)
929		goto done;
930	if (lz4 == true) {
931		if (fprintf(fp, "\t.vf_bytes\t= NULL,\n") < 0)
932			goto done;
933	} else {
934		if (fprintf(fp, "\t.vf_bytes\t= FONTDATA_%ux%u,\n",
935		    width, height) < 0) {
936			goto done;
937		}
938	}
939	if (fprintf(fp, "\t.vf_width\t= %u,\n", width) < 0)
940		goto done;
941	if (fprintf(fp, "\t.vf_height\t= %u,\n", height) < 0)
942		goto done;
943	if (fprintf(fp, "\t.vf_map_count\t= { %u, %u, %u, %u }\n",
944	    map_folded_count[0], map_folded_count[1], map_folded_count[2],
945	    map_folded_count[3]) < 0) {
946		goto done;
947	}
948	if (fprintf(fp, "};\n\n") < 0)
949		goto done;
950
951	/* Write bitmap data. */
952	if (fprintf(fp, "vt_font_bitmap_data_t font_data_%ux%u = {\n",
953	    width, height) < 0)
954		goto done;
955	if (fprintf(fp, "\t.vfbd_width\t= %u,\n", width) < 0)
956		goto done;
957	if (fprintf(fp, "\t.vfbd_height\t= %u,\n", height) < 0)
958		goto done;
959	if (lz4 == true) {
960		if (fprintf(fp, "\t.vfbd_compressed_size\t= %zu,\n",
961		    compressed_size) < 0) {
962			goto done;
963		}
964		if (fprintf(fp, "\t.vfbd_uncompressed_size\t= %zu,\n",
965		    uncompressed_size) < 0) {
966			goto done;
967		}
968		if (fprintf(fp, "\t.vfbd_compressed_data\t= FONTDATA_%ux%u,\n",
969		    width, height) < 0) {
970			goto done;
971		}
972	} else {
973		if (fprintf(fp, "\t.vfbd_compressed_size\t= 0,\n") < 0)
974			goto done;
975		if (fprintf(fp, "\t.vfbd_uncompressed_size\t= %zu,\n",
976		    uncompressed_size) < 0) {
977			goto done;
978		}
979		if (fprintf(fp, "\t.vfbd_compressed_data\t= NULL,\n") < 0)
980			goto done;
981	}
982	if (fprintf(fp, "\t.vfbd_font = &font_%ux%u\n", width, height) < 0)
983		goto done;
984	if (fprintf(fp, "};\n") < 0)
985		goto done;
986
987	rv = 0;
988done:
989	if (rv != 0)
990		perror(filename);
991	fclose(fp);
992	return (0);
993}
994
995static void
996print_font_info(void)
997{
998	printf(
999"Statistics:\n"
1000"- width:                       %6u\n"
1001"- height:                      %6u\n"
1002"- glyph_total:                 %6u\n"
1003"- glyph_normal:                %6u\n"
1004"- glyph_normal_right:          %6u\n"
1005"- glyph_bold:                  %6u\n"
1006"- glyph_bold_right:            %6u\n"
1007"- glyph_unique:                %6u\n"
1008"- glyph_dupe:                  %6u\n"
1009"- mapping_total:               %6u\n"
1010"- mapping_normal:              %6u\n"
1011"- mapping_normal_folded:       %6u\n"
1012"- mapping_normal_right:        %6u\n"
1013"- mapping_normal_right_folded: %6u\n"
1014"- mapping_bold:                %6u\n"
1015"- mapping_bold_folded:         %6u\n"
1016"- mapping_bold_right:          %6u\n"
1017"- mapping_bold_right_folded:   %6u\n"
1018"- mapping_unique:              %6u\n"
1019"- mapping_dupe:                %6u\n",
1020	    width, height,
1021	    glyph_total,
1022	    glyph_count[0],
1023	    glyph_count[1],
1024	    glyph_count[2],
1025	    glyph_count[3],
1026	    glyph_unique, glyph_dupe,
1027	    mapping_total,
1028	    map_count[0], map_folded_count[0],
1029	    map_count[1], map_folded_count[1],
1030	    map_count[2], map_folded_count[2],
1031	    map_count[3], map_folded_count[3],
1032	    mapping_unique, mapping_dupe);
1033}
1034
1035int
1036main(int argc, char *argv[])
1037{
1038	int ch, verbose = 0, rv = 0;
1039	char *outfile = NULL;
1040
1041	assert(sizeof(struct font_header) == 32);
1042	assert(sizeof(vfnt_map_t) == 8);
1043
1044	while ((ch = getopt(argc, argv, "nf:h:vw:o:")) != -1) {
1045		switch (ch) {
1046		case 'f':
1047			if (strcmp(optarg, "font") == 0)
1048				format = VT_FONT;
1049			else if (strcmp(optarg, "source") == 0)
1050				format = VT_C_SOURCE;
1051			else if (strcmp(optarg, "compressed-source") == 0)
1052				format = VT_C_COMPRESSED;
1053			else
1054				errx(1, "Invalid format: %s", optarg);
1055			break;
1056		case 'h':
1057			height = atoi(optarg);
1058			break;
1059		case 'n':
1060			filter = false;
1061			break;
1062		case 'o':
1063			outfile = optarg;
1064			break;
1065		case 'v':
1066			verbose = 1;
1067			break;
1068		case 'w':
1069			width = atoi(optarg);
1070			break;
1071		case '?':
1072		default:
1073			usage();
1074		}
1075	}
1076	argc -= optind;
1077	argv += optind;
1078
1079	if (outfile == NULL && (argc < 2 || argc > 3))
1080		usage();
1081
1082	if (outfile == NULL) {
1083		outfile = argv[argc - 1];
1084		argc--;
1085	}
1086
1087	set_width(width);
1088	set_height(height);
1089
1090	if (parse_file(argv[0], VFNT_MAP_NORMAL) != 0)
1091		return (1);
1092	argc--;
1093	argv++;
1094	if (argc == 1) {
1095		if (parse_file(argv[0], VFNT_MAP_BOLD) != 0)
1096			return (1);
1097		argc--;
1098		argv++;
1099	}
1100	number_glyphs();
1101	dedup_mapping(VFNT_MAP_BOLD);
1102	dedup_mapping(VFNT_MAP_BOLD_RIGHT);
1103	fold_mappings(0);
1104	fold_mappings(1);
1105	fold_mappings(2);
1106	fold_mappings(3);
1107
1108	switch (format) {
1109	case VT_FONT:
1110		rv = write_fnt(outfile);
1111		break;
1112	case VT_C_SOURCE:
1113		rv = write_fnt_source(false, outfile);
1114		break;
1115	case VT_C_COMPRESSED:
1116		rv = write_fnt_source(true, outfile);
1117		break;
1118	}
1119
1120	if (verbose)
1121		print_font_info();
1122
1123	return (rv);
1124}
1125