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 /* Write a CPI file to disk */
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "cpi.h"
25 
poke4(cpi_byte * address,long value)26 static void poke4(cpi_byte *address, long value)
27 {
28 	address[0] = 0xFF & value;
29 	address[1] = 0xFF & (value >> 8);
30 	address[2] = 0xFF & (value >> 16);
31 	address[3] = 0xFF & (value >> 24);
32 }
33 
write_i1(FILE * fp,unsigned char i1)34 static int write_i1(FILE *fp, unsigned char i1)
35 {
36 	if (fputc(i1, fp) == EOF) return CPI_ERR_ERRNO;
37 	return CPI_ERR_OK;
38 }
39 
write_i2(FILE * fp,unsigned short i2)40 static int write_i2(FILE *fp, unsigned short i2)
41 {
42 	cpi_byte c[2];
43 
44 	c[0] = i2 & 0xFF;
45 	c[1] = i2 >> 8;
46 
47 	if (fwrite(c, 1, 2, fp) < 2) return CPI_ERR_ERRNO;
48 	return CPI_ERR_OK;
49 }
50 
write_i4(FILE * fp,unsigned long i4)51 static int write_i4(FILE *fp, unsigned long i4)
52 {
53 	int err;
54 
55 	err = write_i2(fp,  i4 & 0xFFFF);
56 	if (err) return err;
57 	return (write_i2(fp, i4 >> 16));
58 }
59 
60 
write_cph(FILE * fp,CP_HEAD * cph,int ntfont,long endfile)61 static int write_cph(FILE *fp, CP_HEAD *cph, int ntfont, long endfile)
62 {
63 	char buf[9];
64 	long nxoffset, datoffset;
65 	int err = CPI_ERR_OK;
66 
67 	sprintf(buf, "%-8.8s", cph->dev_name);
68 
69 	if (!err) err = write_i2(fp, 28);
70 
71 	nxoffset  = cph->next_offset;
72 	datoffset = cph->cpih_offset;
73 	if (ntfont)
74 	{
75 		if (!nxoffset) nxoffset = endfile;
76 		nxoffset  -= cph->self_offset;
77 		datoffset -= cph->self_offset;
78 	}
79 	if (!err) err = write_i4(fp, nxoffset);
80 	if (!err) err = write_i2(fp, cph->dev_type);
81 	if (!err && fwrite(buf, 1, 8, fp) < 8) err = CPI_ERR_ERRNO;
82 	if (!err) err = write_i2(fp, cph->codepage);
83 	if (!err) err = write_i4(fp, 0);
84 	if (!err) err = write_i2(fp, 0);
85 	if (!err) err = write_i4(fp, datoffset);
86 	return err;
87 }
88 
cpi_save(CPI_FILE * f,const char * filename,int interleaved)89 int cpi_save  (CPI_FILE *f, const char *filename, int interleaved)
90 {
91 	int err;
92 	FILE *fp;
93 
94         fp = fopen(filename, "wb");
95         if (!fp) return CPI_ERR_ERRNO;
96         err = cpi_savefile(f, fp, interleaved);
97 	if (err)
98 	{
99 		fclose(fp);
100 		remove(filename);
101 		return err;
102 	}
103         if (fclose(fp)) return CPI_ERR_ERRNO;
104 	return err;
105 }
106 
compare_drfonts(const void * a,const void * b)107 static int compare_drfonts(const void *a, const void *b)
108 {
109 	const CP_DRFONT *dra = (CP_DRFONT *)a;
110 	const CP_DRFONT *drb = (CP_DRFONT *)b;
111 
112 	return dra->char_len - drb->char_len;
113 }
114 
115 /* In DRFONT fonts, sort in ascending order */
comp_dcpf(const void * a,const void * b)116 static int comp_dcpf(const void *a, const void *b)
117 {
118 	const CP_FONT *fna = (CP_FONT *)a;
119 	const CP_FONT *fnb = (CP_FONT *)b;
120 
121 	return (fna->height * fna->width) - (fnb->height * fnb->width);
122 }
123 
124 
125 /* In FONT fonts, sort in descending order */
comp_cpf(const void * a,const void * b)126 static int comp_cpf(const void *a, const void *b)
127 {
128 	const CP_FONT *fna = (CP_FONT *)a;
129 	const CP_FONT *fnb = (CP_FONT *)b;
130 
131 	return (fnb->height * fnb->width) - (fna->height * fnb->width);
132 }
133 
134 
cpi_savefile(CPI_FILE * f,FILE * fp,int interleaved)135 int cpi_savefile(CPI_FILE *f, FILE *fp, int interleaved)
136 {
137 	int err = CPI_ERR_OK;
138 	cpi_byte header[23];
139 	long fpos;
140 	CP_HEAD *cph;
141 	CP_FONT *cpf, *cpf2, *cpf3;
142 	int n, count, nchars;
143 	int drfont = 0;
144 	int ntfont = 0;
145 
146 	/* Write out header */
147 	memset(header, 0, sizeof(header));
148 	header[0] = f->magic0;
149 	sprintf((char *)(header+1), "%-7.7s", f->format);
150 	header[0x10] = 1;	/* 1 pointer */
151 	header[0x12] = 1;	/* Type 1 */
152 	fpos = 0x17;
153 	if (!strcmp(f->format, "FONT.NT"))
154 	{
155 		ntfont = 1;
156 	}
157 	if (f->magic0 == 0x7F)	/* DRFONT */
158 	{
159 		drfont = 1;
160 		fpos += (5 * f->drcount) + 1;
161 	}
162 /* Sort the font sizes within the file */
163 	if (drfont) qsort(f->drfonts, f->drcount, sizeof(CP_DRFONT),
164 			compare_drfonts);
165 	for (cph = f->firstpage; cph != NULL; cph = cph->next)
166 	{
167 		n = 0;
168 		for (cpf = cph->fonts; cpf != NULL; cpf = cpf->next) n++;
169 		cpf2 = malloc(n * sizeof(CP_FONT));
170 /* If we have enough memory, sort the fonts as well */
171 		if (cpf2)
172 		{
173 			n = 0;
174 	/* Copy all the fonts to an array for sorting */
175 			for (cpf = cph->fonts; cpf != NULL; cpf = cpf->next)
176 			{
177 				memcpy(&cpf2[n], cpf, sizeof(CP_FONT));
178 				n++;
179 			}
180 	/* Sort them */
181 			if (drfont) qsort(cpf2, n, sizeof(CP_FONT), comp_dcpf);
182 			else	    qsort(cpf2, n, sizeof(CP_FONT), comp_cpf);
183 	/* Copy them back */
184 			n = 0;
185 			for (cpf = cph->fonts; cpf != NULL; cpf = cpf->next)
186 			{
187 				cpf3 = cpf->next;
188 				memcpy(cpf, &cpf2[n], sizeof(CP_FONT));
189 				cpf->next = cpf3;
190 				n++;
191 			}
192 			free(cpf2);
193 		}
194 	}
195 
196 /* Note that this allows us to write a file that's both FONT.NT and DRFONT;
197   I don't advise it :-) */
198 
199 	poke4(header + 0x13, fpos);
200 
201 	if (fwrite(header, 1, sizeof(header), fp) < (int)sizeof(header))
202 	{
203 		return CPI_ERR_ERRNO;
204 	}
205 	/* Calculate addresses of all structures. */
206 	fpos += 2;	/* Skip over count of pages */
207 	count = 0;
208 	/* If not interleaved, do all the headers followed by all the data */
209 	if (!interleaved) for (cph = f->firstpage; cph != NULL; cph = cph->next)
210 	{
211 		cph->self_offset = fpos;
212 		if (cph->next)	cph->next_offset = fpos + 28;
213 		else		cph->next_offset = 0;
214 
215 		fpos += 28;
216 		++count;
217 	}
218 	for (cph = f->firstpage; cph != NULL; cph = cph->next)
219 	{
220 		/* If interleaved, do font headers here */
221 		if (interleaved)
222 		{
223 			cph->self_offset = fpos;
224 			fpos += 28;
225 			++count;
226 		}
227 		cph->cpih_offset = fpos;
228 		fpos += 6;	/* General header */
229 		for (cpf = cph->fonts; cpf != NULL; cpf = cpf->next)
230 		{
231 			if (cph->dev_type == 1)
232 			{
233 				fpos += 6;	/* Fontsize header */
234 				if (!drfont) fpos += cpf->fontsize;
235 			}
236 			else if (cph->dev_type == 2)
237 			{
238 				fpos += 4;	/* Fontsize header */
239 				fpos += cpf->fontsize;
240 			}
241 		}
242 /* Font length in the header does not include the DRFONT index.
243  * DRDOS utilities assume the index is always 256 chars. We will always
244  * write an index that's at least 256 chars long, but if there are more
245  * characters it will be longer. */
246 		cph->font_len = fpos - cph->cpih_offset;
247 		if (drfont)
248 		{
249 			nchars = cpi_count_chars(cph);
250 			if (nchars < 256) nchars = 256;
251 			fpos += 2 * nchars;
252 		}
253 					/* DRFONT font index */
254 		if (interleaved)
255 		{
256 			if (cph->next)	cph->next_offset = fpos;
257 			else		cph->next_offset = 0;
258 		}
259 	}
260 	/* Lastly, the font data (if DRFONT) */
261 	if (drfont) for (n = 0; n < f->drcount; n++)
262 	{
263 		f->drfonts[n].offset = fpos;
264 		fpos += f->drfonts[n].bitmap_len;
265 	}
266 	/* Now start writing the structures. Firstly, the extended
267 	 * DRFONT header. */
268 	if (drfont)
269 	{
270 		err = write_i1(fp, f->drcount);
271 		for (n = 0; n < f->drcount; n++)
272 		{
273 			if (!err) err = write_i1(fp, f->drfonts[n].char_len);
274 		}
275 		for (n = 0; n < f->drcount; n++)
276 		{
277 			if (!err) err = write_i4(fp, f->drfonts[n].offset);
278 		}
279 	}
280 	if (!err) err = write_i2(fp, count);
281 	/* Do the codepage headers (if not interleaving) */
282 	if (!err && !interleaved)
283 		for (cph = f->firstpage; cph != NULL; cph = cph->next)
284 	{
285 		if (!err) err = write_cph(fp, cph, ntfont, fpos);
286 	}
287 
288 	/* Next, the codepage bodies */
289 	if (!err) for (cph = f->firstpage; cph != NULL; cph = cph->next)
290 	{
291 		int fcount = 0;
292 
293 		if (!err && interleaved) err = write_cph(fp, cph, ntfont, fpos);
294 		for (cpf = cph->fonts; cpf != NULL; cpf = cpf->next)
295 			++fcount;
296 		if (!err) err = write_i2(fp, drfont ? 2 : 1);
297 		if (!err) err = write_i2(fp, fcount);
298 		if (!err) err = write_i2(fp, cph->font_len - 6);
299 		if (!err) for (cpf = cph->fonts; cpf != NULL; cpf = cpf->next)
300 		{
301 			if (cph->dev_type == 1)
302 			{
303 				if (!err) err = write_i1(fp,cpf->height);
304 				if (!err) err = write_i1(fp,cpf->width);
305 				if (!err) err = write_i1(fp,cpf->yaspect);
306 				if (!err) err = write_i1(fp,cpf->xaspect);
307 				if (!err) err = write_i2(fp,cpf->nchars);
308 				if (!err && !drfont)
309 				{
310 					if (fwrite(cpf->data, 1,
311 					  cpf->fontsize, fp) < cpf->fontsize)
312 						err = CPI_ERR_ERRNO;
313 				}
314 			}
315 			else if (cph->dev_type == 2)
316 			{
317 				if (!err) err = write_i2(fp,cpf->pr_type);
318 				if (!err) err = write_i2(fp,cpf->pr_seqsize);
319 				if (!err && fwrite(cpf->data, 1,
320 				  cpf->fontsize, fp) < cpf->fontsize)
321 					err = CPI_ERR_ERRNO;
322 			}
323 		}
324 		if (!err && drfont)
325 		{
326 /* DRDOS always assumes that this table has 256 entries. We will always
327  * write an index that's at least 256 chars long, but if there are more
328  * characters it will be longer. */
329 			nchars = cpi_count_chars(cph);
330 			if (nchars < 256) nchars = 256;
331 			for (n = 0; n < nchars; n++)
332 			{
333 				if (!err) err = write_i2(fp,cph->dr_lookup[n]);
334 			}
335 			for (; n < 256; n++)
336 			{
337 				if (!err) err = write_i2(fp, 0);
338 			}
339 		}
340 	}
341 	/* The fonts (if DRFONT) */
342 	if (!err && drfont)
343 	{
344 		for (n = 0; n < f->drcount; n++)
345 		{
346 			if (!err && fwrite(f->drfonts[n].bitmap, 1,
347 			  f->drfonts[n].bitmap_len, fp) < f->drfonts[n].bitmap_len) err = CPI_ERR_ERRNO;
348 		}
349 	}
350 	/* And any comment */
351 	if (!err && f->comment && f->comment_len)
352 	{
353 		if (fwrite(f->comment, 1, f->comment_len, fp) < f->comment_len)
354 			err= CPI_ERR_ERRNO;
355 	}
356 	return err;
357 }
358