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