1 /*
2     psftools: Manipulate console fonts in the .PSF format
3     Copyright (C) 2005  John Elliott
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 
20 /* Merge one or more PSF files into a new PSF file */
21 
22 #include "cnvmulti.h"
23 #include "psflib.h"
24 #include <ctype.h>
25 
26 
27 static char helpbuf[2048];
28 static char msgbuf[1000];
29 static int v1 = 0;
30 static PSF_FILE *psf_in, psf_out;
31 
32 /* Program name */
33 char *cnv_progname = "PSFMERGE";
34 
35 /* ddash = 1 if option started with a double-dash; else 0 */
36 /* Return NULL if OK, else error string */
cnv_set_option(int ddash,char * variable,char * value)37 char *cnv_set_option(int ddash, char *variable, char *value)
38 {
39 	if (!stricmp(variable, "psf1")) { v1 = 1; return NULL; }
40 	if (!stricmp(variable, "psf2")) { v1 = 0; return NULL; }
41 	if (strlen(variable) > 2000) variable[2000] = 0;
42 	sprintf(helpbuf, "Unknown option: %s\n", variable);
43 	return helpbuf;
44 }
45 
46 
47 /* Return help string */
cnv_help(void)48 char *cnv_help(void)
49 {
50     sprintf(helpbuf, "Combines Unicode .PSF fonts\n\n"
51 		     "Syntax: %s source.psf source.psf "
52 		            " ... target.psf \n\n", cnv_progname);
53     strcat(helpbuf,  "Options are:\n");
54     strcat(helpbuf,  "             --psf1: Output in PSF1 format\n");
55     strcat(helpbuf,  "             --psf2: Output in PSF2 format (default)\n");
56     return helpbuf;
57 }
58 
59 /* Start of program */
60 
61 /* This is similar to the Unicode buffer management in psfucs.c, but instead
62  * of the fixed number of characters in a PSF file, we have a variable number
63  * of characters tracked by unicodes_count.
64  *
65  * unicodes[] holds unicodes_count head pointers, and can hold unicodes_max
66  * of them.
67  * bufchain holds the Unicode entry buffers that have been allocated.
68  * freechain is the list of free entries in bufchain.
69  * dirents_nfree is the count of entries in freechain. */
70 static psf_unicode_dirent **unicodes = NULL;
71 static int unicodes_max = 0, unicodes_count = 0;
72 static psf_unicode_buffer *bufchain = NULL;
73 static psf_unicode_dirent *freechain = NULL;
74 static int dirents_nfree = 0;
75 
76 /* Free all the above structures */
free_buffers()77 void free_buffers()
78 {
79 	psf_unicode_buffer *nbuf;
80 	void *v;
81 	nbuf = bufchain;
82 	while (nbuf)
83 	{
84 		v = nbuf;
85 		nbuf = nbuf->psfb_next;
86 		free(v);
87 	}
88 	bufchain = NULL;
89 	freechain = NULL;
90 	dirents_nfree = 0;
91 	free(unicodes);
92 	unicodes = NULL;
93 	unicodes_max = unicodes_count = 0;
94 }
95 
96 /* Convert a Unicode token chain to text form, for ease of comparison
97  * and searching */
chain2str(psf_unicode_dirent * chain)98 static char *chain2str(psf_unicode_dirent *chain)
99 {
100 	char *ptr;
101 
102 	if (psf_unicode_to_string(chain, &ptr)) return NULL;
103 	return ptr;
104 }
105 
106 
107 /* Allocate a psf_unicode_dirent */
newentry(psf_dword token)108 psf_unicode_dirent *newentry(psf_dword token)
109 {
110 	psf_unicode_buffer *nbuf;
111 	psf_unicode_dirent *ude;
112 
113 	if (!dirents_nfree)
114 	{
115 		nbuf = psf_malloc_unicode_buffer();
116 		if (!nbuf) return NULL;
117 		nbuf->psfb_next = bufchain;
118 		bufchain = nbuf;
119 		nbuf->psfb_dirents[0].psfu_next = freechain;
120 		freechain = &(nbuf->psfb_dirents[PSF_ENTRIES_PER_BUFFER - 1]);
121 		dirents_nfree += PSF_ENTRIES_PER_BUFFER;
122 	}
123 	/* Take first entry from the free list */
124 	ude = freechain;
125 	freechain = freechain->psfu_next;
126 	ude->psfu_next = NULL;
127 	ude->psfu_token = token;
128 	--dirents_nfree;
129 	return ude;
130 }
131 
132 /* Given two chains of unicode tokens, return the union of them */
chain_merge(psf_unicode_dirent ** dest,psf_unicode_dirent * src)133 static int chain_merge(psf_unicode_dirent **dest, psf_unicode_dirent *src)
134 {
135 	char *str1, *str2, *tok2;
136 	psf_dword token;
137 	psf_unicode_dirent *ude, *ude2, *end;
138 
139 	end = *dest;
140 	for (ude2 = *dest; ude2 != NULL; ude2 = ude2->psfu_next) end = ude2;
141 
142 	str1 = chain2str(*dest);
143 	str2 = chain2str(src);
144 
145 	for (tok2 = strtok(str2, ";");
146 	     tok2 != NULL;
147      	     tok2 = strtok(NULL, ";"))
148 	{
149 		if (strstr(str1, tok2))
150 		{
151 			continue;
152 		}
153 		if (strchr(tok2, '+'))
154 		{
155 			/* Add multiple token to end */
156 			tok2++;
157 			/* Add sequence leadin */
158 			ude = newentry(0xFFFE);
159 			if (!ude) return PSF_E_NOMEM;
160 			end->psfu_next = ude;
161 			ude->psfu_next = NULL;
162 			end = ude;
163 			while (strlen(tok2) > 8)
164 			{
165 				sscanf(tok2, "%lx", &token);
166 				ude = newentry(token);
167 				if (!ude) return PSF_E_NOMEM;
168 				end->psfu_next = ude;
169 				ude->psfu_next = NULL;
170 				end = ude;
171 				tok2 += 9;	/* Skip hex & separator */
172 			}
173 		}
174 		else
175 		{
176 			sscanf(tok2 + 1, "%lx", &token);
177 			ude = newentry(token);
178 			if (!ude) return PSF_E_NOMEM;
179 /* Single token: Insert at front of chain */
180 			ude->psfu_next = *dest;
181 			*dest = ude;
182 
183 		}
184 	}
185 	free(str1);
186 	free(str2);
187 	return 1;
188 }
189 
190 /* Given two chains of unicode tokens, see if their intersection is
191  * nonempty. */
chain_matches(psf_unicode_dirent * c1,psf_unicode_dirent * c2)192 static int chain_matches(psf_unicode_dirent *c1, psf_unicode_dirent *c2)
193 {
194 	char *str1, *str2, *tok2;
195 
196 	str1 = chain2str(c1);
197 	str2 = chain2str(c2);
198 
199 	for (tok2 = strtok(str2, ";");
200 	     tok2 != NULL;
201      	     tok2 = strtok(NULL, ";"))
202 	{
203 		if (strstr(str1, tok2))
204 		{
205 			free(str1);
206 			free(str2);
207 			return 1;
208 		}
209 	}
210 	return 0;
211 }
212 
213 /* Copy a chain of unicode tokens from a PSF file into the unicodes[] array */
chain_copy(psf_unicode_dirent ** dest,psf_unicode_dirent * src)214 static psf_errno_t chain_copy(psf_unicode_dirent **dest, psf_unicode_dirent *src)
215 {
216 	psf_unicode_dirent *ude;
217 	while (src)
218 	{
219 		ude = newentry(src->psfu_token);
220 		if (!ude) return PSF_E_NOMEM;
221 		*dest = ude;
222 		dest = &ude->psfu_next;
223 		src = src->psfu_next;
224 	}
225 	return PSF_E_OK;
226 }
227 
228 
229 /* Look up an entry in the unicodes array, and return its index. */
unicode_lookup(psf_unicode_dirent * chain)230 static psf_dword unicode_lookup(psf_unicode_dirent *chain)
231 {
232 	psf_dword n;
233 
234 	for (n = 0; n < unicodes_count; n++)
235 	{
236 		if (chain_matches(unicodes[n], chain)) return n;
237 	}
238 	/* This should never happen because unicode_add() should have
239 	 * added the chain to the list already */
240 	return 0;
241 }
242 
243 
244 /* Add a chain of Unicode tokens. If it intersects with an existing chain,
245  * make the result their union; otherwise, add it as a new chain */
unicode_add(psf_unicode_dirent * chain)246 static psf_errno_t unicode_add(psf_unicode_dirent *chain)
247 {
248 	psf_unicode_dirent **tmp;
249 	int n;
250 
251 	for (n = 0; n < unicodes_count; n++)
252 	{
253 		if (chain_matches(unicodes[n], chain))
254 		{
255 			chain_merge(&unicodes[n], chain);
256 			return PSF_E_OK;
257 		}
258 	}
259 
260 	if (unicodes_count >= unicodes_max)
261 	{
262 		tmp = calloc(unicodes_max + 256, sizeof(psf_unicode_dirent *));
263 		if (!tmp) return PSF_E_NOMEM;
264 		if (unicodes) memcpy(tmp, unicodes,
265 				unicodes_max * sizeof(psf_unicode_dirent *));
266 		free(unicodes);
267 		unicodes = tmp;
268 		unicodes_max += 256;
269 	}
270 	return chain_copy(&unicodes[unicodes_count++], chain);
271 }
272 
273 
274 /* Do the conversion */
cnv_multi(int nfiles,char ** infiles,FILE * outfile)275 char *cnv_multi(int nfiles, char **infiles, FILE *outfile)
276 {
277         int rv, nf, nc;
278 	psf_dword mc;
279         FILE *fp;
280 	int w = -1, h = -1;
281 	psf_unicode_dirent *ude;
282 
283 /* Load all the source files */
284 	psf_in = malloc(nfiles * sizeof(PSF_FILE));
285 	if (!psf_in) return "Out of memory";
286         for (nf = 0; nf < nfiles; nf++)
287         {
288 		psf_file_new(&psf_in[nf]);
289                 if (!strcmp(infiles[nf], "-")) fp = stdin;
290                 else  fp = fopen(infiles[nf], "rb");
291 
292                 if (!fp)
293 		{
294 			perror(infiles[nf]);
295 			return "Cannot open font file.";
296 		}
297 		rv = psf_file_read(&psf_in[nf], fp);
298                 if (fp != stdin) fclose(fp);
299 		if (rv != PSF_E_OK)
300 		{
301 			sprintf(msgbuf, "%s: %s", infiles[nf],
302 					psf_error_string(rv));
303 			return msgbuf;
304 		}
305 		if (w == -1 && h == -1)
306 		{
307 			w = psf_in[nf].psf_width;
308 			h = psf_in[nf].psf_height;
309 		}
310 		else if (w != psf_in[nf].psf_width || h != psf_in[nf].psf_height)
311 		{
312 			return "All fonts must have the same dimensions.";
313 		}
314 		if (!psf_is_unicode(&psf_in[nf]))
315 		{
316 			sprintf(msgbuf, "%s has no Unicode table.", infiles[nf]);
317 			return msgbuf;
318 		}
319 	}
320 /* Work out how many unique characters there are */
321         for (nf = 0; nf < nfiles; nf++)
322         {
323 		for (nc = 0; nc < psf_in[nf].psf_length; nc++)
324 		{
325 			/* Skip characters with no Unicode mapping */
326 			if (!psf_in[nf].psf_dirents_used[nc]) continue;
327 			rv = unicode_add(psf_in[nf].psf_dirents_used[nc]);
328 			if (rv)
329 			{
330 				free_buffers();
331 				return psf_error_string(rv);
332 			}
333 		}
334 	}
335 	rv = psf_file_create(&psf_out, w, h, unicodes_count, 1);
336 	if (rv)
337 	{
338 		free_buffers();
339 		return psf_error_string(rv);
340 	}
341         for (nf = 0; nf < nfiles; nf++)
342         {
343 		for (nc = 0; nc < psf_in[nf].psf_length; nc++)
344 		{
345 			/* Skip characters with no Unicode mapping */
346 			if (!psf_in[nf].psf_dirents_used[nc]) continue;
347 			mc = unicode_lookup(psf_in[nf].psf_dirents_used[nc]);
348 			memcpy(psf_out.psf_data + mc * psf_out.psf_charlen,
349 			       psf_in[nf].psf_data + nc * psf_in[nf].psf_charlen,
350 			       psf_out.psf_charlen);
351 		}
352 	}
353 	/* Do the Unicode directory */
354 	rv = 0;
355 	for (nf = 0; nf < unicodes_count; nf++)
356 	{
357 		for (ude = unicodes[nf]; ude != NULL; ude = ude->psfu_next)
358 		{
359 			rv = psf_unicode_add(&psf_out, nf, ude->psfu_token);
360 			if (rv) break;
361 		}
362 		if (rv) break;
363 	}
364 	if (!rv) rv = psf_file_write(&psf_out, outfile);
365 	psf_file_delete(&psf_out);
366 	free_buffers();
367         for (nf = 0; nf < nfiles; nf++)
368         {
369 		psf_file_delete(&psf_in[nf]);
370 	}
371 	free(psf_in);
372 	if (rv) return psf_error_string(rv);
373 	return NULL;
374 }
375 
376