1 /*
2 psftools: Manipulate console fonts in the .PSF format
3 Copyright (C) 2005,2006,2007 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 /* Load a CPI file into memory */
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "cpi.h"
25
26 #ifndef SEEK_SET
27 #define SEEK_SET 0
28 #define SEEK_CUR 1
29 #define SEEK_END 2
30 #endif
31
32 static const cpi_byte font_magic[8] = { 0xFF,'F','O','N','T',' ',' ',' ' };
33 static const cpi_byte fontnt_magic[8] = { 0xFF,'F','O','N','T','.','N','T' };
34 static const cpi_byte drfont_magic[8] = { 0x7F,'D','R','F','O','N','T',' ' };
35
peek2(cpi_byte * p)36 static unsigned short peek2(cpi_byte *p)
37 {
38 return (((unsigned short)p[1]) << 8) | p[0];
39 }
peek4(cpi_byte * p)40 static unsigned long peek4(cpi_byte *p)
41 {
42 return (((unsigned long)p[3]) << 24) |
43 (((unsigned long)p[2]) << 16) |
44 (((unsigned long)p[1]) << 8) | p[0];
45 }
46
47 /* 1.0.5: Support CPI files with segment:offset pointers */
peek4so(cpi_byte * p)48 static unsigned long peek4so(cpi_byte *p)
49 {
50 return peek2(p+2) * 16L + peek2(p);
51 }
52
read2(FILE * fp,unsigned short * s)53 static int read2(FILE *fp, unsigned short *s)
54 {
55 cpi_byte b[2];
56 if (fread(b, 1, 2, fp) < 2) return CPI_ERR_ERRNO;
57 *s = peek2(b);
58 return 0;
59 }
60
read4(FILE * fp,unsigned long * l)61 static int read4(FILE *fp, unsigned long *l)
62 {
63 cpi_byte b[4];
64 if (fread(b, 1, 4, fp) < 4) return CPI_ERR_ERRNO;
65 *l = peek4(b);
66 return 0;
67 }
68
69
load_fontdata(FILE * fp,CP_HEAD * cph,cpi_byte * fonts_head)70 static int load_fontdata(FILE *fp, CP_HEAD *cph, cpi_byte *fonts_head)
71 {
72 unsigned fonttype, numfonts, m;
73 CP_FONT *cpf;
74 cpi_byte font_blk[6];
75 long pos;
76
77 fonttype = peek2(fonts_head);
78 numfonts = peek2(fonts_head + 2);
79 cph->font_len = peek2(fonts_head + 4);
80 /* Can't have DRFONT fonts in a FONT CPI */
81 /* Toshiba LCD.CPI has FONTs of type 0, just to be awkward */
82 if (fonttype != 1 && fonttype != 0) return CPI_ERR_BADFMT;
83 /* Load the individual fonts */
84 /* Fix for some DRDOS CPIs: printer CPIs report 2 but there is only one font */
85 if (cph->dev_type == 2) numfonts = 1;
86 cph->font_count = numfonts;
87 for (m = 0; m < numfonts; m++)
88 {
89 if (cph->dev_type == 1)
90 {
91 pos = ftell(fp);
92 if (fread(font_blk, 1, sizeof(font_blk), fp) < (int)sizeof(font_blk))
93 return CPI_ERR_BADFMT;
94 cpf = cph_add_font(cph);
95 if (!cpf) return CPI_ERR_NOMEM;
96 /* Codepage header created. */
97 cpf->height = font_blk[0];
98 cpf->width = font_blk[1];
99 cpf->yaspect= font_blk[2];
100 cpf->xaspect= font_blk[3];
101 cpf->nchars = peek2(font_blk + 4);
102 cpf->fontsize = ((cpf->width + 7) / 8) * cpf->height * cpf->nchars;
103 cpf->self_offset = pos;
104 cpf->data = malloc(cpf->fontsize);
105 if (cpf->data == NULL)
106 {
107 cpf->height = cpf->width = cpf->nchars = 0;
108 return CPI_ERR_NOMEM;
109 }
110 if (fread(cpf->data, 1, cpf->fontsize, fp) < cpf->fontsize)
111 return CPI_ERR_BADFMT;
112 }
113 else if (cph->dev_type == 2)
114 {
115 pos = ftell(fp);
116 if (fread(font_blk, 1, 4, fp) < 4)
117 return CPI_ERR_BADFMT;
118 cpf = cph_add_font(cph);
119 if (!cpf) return CPI_ERR_NOMEM;
120 cpf->pr_type = peek2(font_blk);
121 cpf->pr_seqsize = peek2(font_blk + 2);
122 cpf->self_offset = pos;
123 cpf->fontsize = cph->font_len - 4;
124 cpf->data = malloc(cpf->fontsize);
125 if (cpf->data == NULL)
126 {
127 cpf->fontsize = 0;
128 return CPI_ERR_NOMEM;
129 }
130 if (fread(cpf->data, 1, cpf->fontsize, fp) < cpf->fontsize)
131 return CPI_ERR_BADFMT;
132 }
133 }
134 return CPI_ERR_OK;
135 }
136
137
cpi_loadfont(CPI_FILE * f,FILE * fp,cpi_byte * header)138 static int cpi_loadfont(CPI_FILE *f, FILE *fp, cpi_byte *header)
139 {
140 unsigned short count;
141 unsigned n;
142 int err;
143 cpi_byte fonts_head[6], cphead[28];
144 CP_HEAD *cph;
145 long pos, maxpos;
146 int fontnt = 0;
147 long filesize;
148 int use_so = 0;
149
150 if (!memcmp(header, fontnt_magic, 8)) fontnt = 1;
151
152
153 maxpos = 0;
154 cpi_new(f, fontnt ? CPI_FONTNT : CPI_FONT);
155 /* Get the file size. If there's a pointer pointing beyond the end of
156 * the file, it may indicate that we need to change to segment:offset
157 * mode. */
158 if (fseek(fp, 0, SEEK_END)) return CPI_ERR_ERRNO;
159 filesize = ftell(fp);
160 if (fseek(fp, peek4(header + 19), SEEK_SET)) return CPI_ERR_ERRNO;
161 err = read2(fp, &count);
162 if (err) return err;
163
164 for (n = 0; n < count; n++)
165 {
166 pos = ftell(fp);
167 if (fread(cphead, 1, sizeof(cphead), fp) < (int)sizeof(cphead))
168 return CPI_ERR_BADFMT;
169 if (peek2(cphead) > 28)
170 {
171 /* More data in the header. May want to do something
172 * with this, later. */
173 }
174 cph = cpi_add_page(f, peek2(cphead + 16));
175 if (!cph) return CPI_ERR_NOMEM;
176 cph->headsize = peek2(cphead);
177 cph->self_offset = pos;
178 cph->dev_type = peek2(cphead + 6);
179 sprintf(cph->dev_name, "%-8.8s", cphead + 8);
180
181 if (use_so)
182 {
183 cph->next_offset = peek4so(cphead + 2);
184 cph->cpih_offset = peek4so(cphead + 24);
185 }
186 else
187 {
188 cph->next_offset = peek4(cphead + 2);
189 cph->cpih_offset = peek4(cphead + 24);
190 /* 1.0.5: Check for segment:offset; if it's in use, then one or other
191 * pointer (interpreted as a 32-bit integer) will almost certainly
192 * be pointed off the end of the file.
193 *
194 * Note that psftools cannot save CPIs with segment:offset pointers,
195 * so the fact that segment:offset pointers are in use is not recorded
196 * in the memory structure.
197 *
198 * Also don't allow a next_offset of FFFF:FFFF to force segment:offset,
199 * because that's an end-of-file marker.
200 * */
201 if (cph->cpih_offset > filesize ||
202 (cph->next_offset > filesize &&
203 cph->next_offset != 0xFFFFFFFFL))
204 {
205 use_so = 1;
206 cph->next_offset = peek4so(cphead + 2);
207 cph->cpih_offset = peek4so(cphead + 24);
208 }
209 }
210 /* FONT.NT fonts use relative offsets */
211 if (fontnt)
212 {
213 cph->cpih_offset += pos;
214 cph->next_offset += pos;
215 }
216 /* Codepage header created. */
217 if (fseek(fp, cph->cpih_offset, SEEK_SET))
218 return CPI_ERR_ERRNO;
219 if (fread(fonts_head, 1, sizeof(fonts_head), fp) < (int)sizeof(fonts_head))
220 return CPI_ERR_BADFMT;
221
222 err = load_fontdata(fp, cph, fonts_head);
223 if (err) return err;
224
225 if (ftell(fp) > maxpos) maxpos = ftell(fp);
226
227 if (cph->next_offset == 0xFFFFFFFFL || cph->next_offset == 0)
228 break;
229 if (fseek(fp, cph->next_offset, SEEK_SET))
230 return CPI_ERR_ERRNO;
231
232 }
233 fseek(fp, 0, SEEK_END);
234 if (ftell(fp) > maxpos)
235 {
236 f->comment_len = ftell(fp) - maxpos;
237 f->comment = malloc(f->comment_len);
238 if (!f->comment) return CPI_ERR_NOMEM;
239 if (fseek(fp, maxpos, SEEK_SET)) return CPI_ERR_ERRNO;
240 if (fread(f->comment, 1, f->comment_len, fp) < f->comment_len)
241 return CPI_ERR_ERRNO;
242 }
243 return 0;
244 }
245
246
cpi_loaddrfont(CPI_FILE * f,FILE * fp,cpi_byte * header)247 int cpi_loaddrfont(CPI_FILE *f, FILE *fp, cpi_byte *header)
248 {
249 unsigned short count;
250 unsigned m, n, q;
251 cpi_byte cphead[28], fonts_head[6], font_blk[6];
252 CP_HEAD *cph;
253 CP_FONT *cpf;
254 long pos, maxpos;
255 unsigned fonttype, numfonts, maxchar;
256 int c, d, err, nchars;
257
258 maxpos = 0;
259 cpi_new(f, CPI_DRFONT);
260 c = fgetc(fp); /* Length of extended header */
261 if (c == EOF) return CPI_ERR_BADFMT;
262 f->drcount = c;
263 f->drfonts = malloc(c * sizeof(CP_DRFONT));
264 if (!f->drfonts) return CPI_ERR_NOMEM;
265 for (n = 0; n < c; n++)
266 {
267 d = fgetc(fp);
268 if (d == EOF) return CPI_ERR_BADFMT;
269 f->drfonts[n].char_len = d;
270 }
271 for (n = 0; n < c; n++)
272 {
273 err = read4(fp, (unsigned long *)&f->drfonts[n].offset);
274 if (err) return err;
275 }
276 /* Header read. Now seek to start of data. */
277 if (fseek(fp, peek4(header + 19), SEEK_SET)) return CPI_ERR_ERRNO;
278 err = read2(fp, &count);
279 if (err) return err;
280
281 maxchar = 0;
282 for (n = 0; n < count; n++)
283 {
284 pos = ftell(fp);
285 if (fread(cphead, 1, sizeof(cphead), fp) < (int)sizeof(cphead))
286 return CPI_ERR_BADFMT;
287 if (peek2(cphead) > 28)
288 {
289 /* More data in the header. May want to do something
290 * with this, later. */
291 }
292 cph = cpi_add_page(f, peek2(cphead + 16));
293 if (!cph) return CPI_ERR_NOMEM;
294 cph->headsize = peek2(cphead);
295 cph->self_offset = pos;
296 cph->next_offset = peek4(cphead + 2);
297 cph->dev_type = peek2(cphead + 6);
298 sprintf(cph->dev_name, "%-8.8s", cphead + 8);
299 cph->cpih_offset = peek4(cphead + 24);
300 /* Codepage header created. */
301 if (fseek(fp, cph->cpih_offset, SEEK_SET))
302 return CPI_ERR_ERRNO;
303 if (fread(fonts_head, 1, sizeof(fonts_head), fp) < (int)sizeof(fonts_head))
304 return CPI_ERR_BADFMT;
305 fonttype = peek2(fonts_head);
306 numfonts = peek2(fonts_head + 2);
307 cph->font_len = peek2(fonts_head + 4);
308 if (fonttype == 1) /* Wow. FONT record in a DRFONT. DRDOS
309 mode doesn't seem to mind this, so... */
310 {
311 err = load_fontdata(fp, cph, fonts_head);
312 if (err) return err;
313 }
314 else if (fonttype != 2) return CPI_ERR_BADFMT;
315 else
316 {
317 for (m = 0; m < c; m++)
318 /* Load the individual fonts */
319 {
320 if (fread(font_blk, 1, sizeof(font_blk), fp) < (int)sizeof(font_blk))
321 return CPI_ERR_BADFMT;
322 cpf = cph_add_font(cph);
323 if (!cpf) return CPI_ERR_NOMEM;
324 cpf->height = font_blk[0];
325 cpf->width = font_blk[1];
326 cpf->yaspect= font_blk[2];
327 cpf->xaspect= font_blk[3];
328 cpf->nchars = peek2(font_blk + 4);
329 cpf->fontsize = 0;
330 cpf->self_offset = pos;
331 cpf->data = NULL;
332 }
333 nchars = cpi_count_chars(cph);
334 /* Now load the lookup table into the cph.
335 * DRDOS utilities all assume the table is 256 chars long. We will be liberal
336 * in what we read and conservative in what we write: the mallocced table will
337 * always be at least 256 chars long, but we will only read from the file for
338 * characters that are actually there. */
339 if (nchars < 256) nchars = 256;
340 cph->dr_lookup = malloc(nchars * sizeof(unsigned short));
341 if (!cph->dr_lookup) return CPI_ERR_NOMEM;
342 nchars = cpi_count_chars(cph);
343 for (q = 0; q < nchars; q++)
344 {
345 err = read2(fp, &cph->dr_lookup[q]);
346 if (err) return err;
347 if (cph->dr_lookup[q] > maxchar)
348 maxchar = cph->dr_lookup[q];
349 }
350 for (;q < 256; q++)
351 {
352 cph->dr_lookup[q] = 0;
353 }
354 }
355 /* Go to next codepage */
356 if (maxpos < ftell(fp)) maxpos = ftell(fp);
357 if (cph->next_offset == 0xFFFFFFFFL || cph->next_offset == 0)
358 break;
359 if (fseek(fp, cph->next_offset, SEEK_SET))
360 return CPI_ERR_ERRNO;
361 }
362 /* Now we need to load the font bitmaps */
363 for (n = 0; n < f->drcount; n++)
364 {
365 if (fseek(fp, f->drfonts[n].offset, SEEK_SET))
366 return CPI_ERR_ERRNO;
367 f->drfonts[n].bitmap_len = (maxchar + 1) * f->drfonts[n].char_len;
368 f->drfonts[n].bitmap = malloc(f->drfonts[n].bitmap_len);
369 if (!f->drfonts[n].bitmap) return CPI_ERR_NOMEM;
370 if (fread(f->drfonts[n].bitmap, 1, f->drfonts[n].bitmap_len, fp) < f->drfonts[n].bitmap_len) return CPI_ERR_BADFMT;
371 if (maxpos < ftell(fp)) maxpos = ftell(fp);
372 }
373 fseek(fp, 0, SEEK_END);
374 if (ftell(fp) > maxpos)
375 {
376 f->comment_len = ftell(fp) - maxpos;
377 f->comment = malloc(f->comment_len);
378 if (!f->comment) return CPI_ERR_NOMEM;
379 if (fseek(fp, maxpos, SEEK_SET)) return CPI_ERR_ERRNO;
380 if (fread(f->comment, 1, f->comment_len, fp) < f->comment_len)
381 return CPI_ERR_ERRNO;
382 }
383 return 0;
384 }
385
386
cpi_loadnaked(CPI_FILE * f,FILE * fp)387 int cpi_loadnaked(CPI_FILE *f, FILE *fp)
388 {
389 int err;
390 cpi_byte cphead[28], fonts_head[6];
391 CP_HEAD *cph;
392 long maxpos;
393
394 maxpos = 0;
395 cpi_new(f, CPI_NAKED);
396
397 if (fread(cphead, 1, sizeof(cphead), fp) < (int)sizeof(cphead))
398 return CPI_ERR_BADFMT;
399 if (peek2(cphead) > 28)
400 {
401 /* This length doesn't seem to make any difference in a
402 * naked CP file */
403 }
404 cph = cpi_add_page(f, peek2(cphead + 16));
405 if (!cph) return CPI_ERR_NOMEM;
406 cph->headsize = peek2(cphead);
407 /* These pointers are meaningless in a .CP file, but do them anyway... */
408 cph->self_offset = 0;
409 cph->next_offset = peek4(cphead + 2);
410 cph->dev_type = peek2(cphead + 6);
411 sprintf(cph->dev_name, "%-8.8s", cphead + 8);
412 cph->cpih_offset = peek4(cphead + 24);
413 /* Codepage header created. Don't bother to seek to the image header; it
414 * must follow this header immediately. */
415 if (fread(fonts_head, 1, sizeof(fonts_head), fp) < (int)sizeof(fonts_head))
416 return CPI_ERR_BADFMT;
417 /* Force the type in the header to 1; some files have 0. */
418 fonts_head[0] = 1;
419 fonts_head[1] = 0;
420 err = load_fontdata(fp, cph, fonts_head);
421 if (err) return err;
422
423 if (ftell(fp) > maxpos) maxpos = ftell(fp);
424 fseek(fp, 0, SEEK_END);
425 if (ftell(fp) > maxpos)
426 {
427 f->comment_len = ftell(fp) - maxpos;
428 f->comment = malloc(f->comment_len);
429 if (!f->comment) return CPI_ERR_NOMEM;
430 if (fseek(fp, maxpos, SEEK_SET)) return CPI_ERR_ERRNO;
431 if (fread(f->comment, 1, f->comment_len, fp) < f->comment_len)
432 return CPI_ERR_ERRNO;
433 }
434 return 0;
435 }
436
437
438
cpi_load(CPI_FILE * f,const char * filename)439 int cpi_load(CPI_FILE *f, const char *filename)
440 {
441 int err;
442 FILE *fp;
443
444 fp = fopen(filename, "rb");
445 if (!fp) return CPI_ERR_ERRNO;
446 err = cpi_loadfile(f, fp);
447 if (fclose(fp)) return CPI_ERR_ERRNO;
448 return err;
449 }
450
cpi_loadfile(CPI_FILE * f,FILE * fp)451 int cpi_loadfile(CPI_FILE *f, FILE *fp)
452 {
453 cpi_byte magic[23];
454 int err;
455
456 if (fread(magic, 1, 23, fp) < 23)
457 {
458 fclose(fp);
459 return CPI_ERR_BADFMT;
460 }
461 if (!memcmp(magic, font_magic, 8) || !memcmp(magic, fontnt_magic, 8))
462 {
463 f->magic0 = magic[0];
464 sprintf(f->format, "%-7.7s", magic + 1);
465 err = cpi_loadfont(f, fp, magic);
466 }
467 else if (!memcmp(magic, drfont_magic, 8))
468 {
469 f->magic0 = magic[0];
470 sprintf(f->format, "%-7.7s", magic + 1);
471 err = cpi_loaddrfont(f, fp, magic);
472 }
473 /* .CP: "Naked" font extracted from a CPI file */
474 else if (magic[0] >= 0x1A && magic[0] <= 0x20 && magic[1] == 0)
475 {
476 f->magic0 = 0xFF;
477 sprintf(f->format, "%-7.7s", "FONT");
478 fseek(fp, 0, SEEK_SET);
479 err = cpi_loadnaked(f, fp);
480 }
481 else err = CPI_ERR_BADFMT;
482 return err;
483 }
484
485