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 CPI file */
21
22 #include "cnvmulti.h"
23 #include "psflib.h"
24 #include "cpi.h"
25 #include <ctype.h>
26
27
28 static char helpbuf[2048];
29 static char msgbuf[1000];
30 static int drfont;
31 static int nt;
32 static int verbose;
33 static int inter = 1;
34 static char device[9] = "EGA ";
35
36
37 /* Program name */
38 char *cnv_progname = "PSFS2CPI";
39
40 /* ddash = 1 if option started with a double-dash; else 0 */
41 /* Return NULL if OK, else error string */
cnv_set_option(int ddash,char * variable,char * value)42 char *cnv_set_option(int ddash, char *variable, char *value)
43 {
44 if (!stricmp(variable, "drfont"))
45 {
46 drfont = 1;
47 return NULL;
48 }
49 if (!stricmp(variable, "nt"))
50 {
51 nt = 1;
52 return NULL;
53 }
54 if (!strcmp(variable, "verbose"))
55 {
56 verbose = 1;
57 return NULL;
58 }
59 if (!strcmp(variable, "device"))
60 {
61 sprintf(device, "%-8.8s", value);
62 return NULL;
63 }
64 if (!strcmp(variable, "comment"))
65 {
66 strncpy(msgbuf, value, sizeof(msgbuf)-1);
67 msgbuf[sizeof(msgbuf)-1] = 0;
68 return NULL;
69 }
70 if (strlen(variable) > 2000) variable[2000] = 0;
71 sprintf(helpbuf, "Unknown option: %s\n", variable);
72 return helpbuf;
73 }
74
75
76 /* Return help string */
cnv_help(void)77 char *cnv_help(void)
78 {
79 sprintf(helpbuf, "Builds a .CPI codepage from one or more .PSF fonts\n\n"
80 "Syntax: %s +nnn source.psf source.psf +nnn "
81 "source.psf ... target.cpi \n\n", cnv_progname);
82 strcat(helpbuf, "Options are: --device=<device name>\n");
83 strcat(helpbuf, " --comment=<comment>\n");
84 strcat(helpbuf, " --drfont: DRFONT output format\n");
85 strcat(helpbuf, " --nt: FONT.NT output format\n");
86 strcat(helpbuf, " --verbose: List each file as it is processed\n");
87 return helpbuf;
88 }
89
90
91 /* Do the conversion */
cnv_multi(int nfiles,char ** infiles,FILE * outfile)92 char *cnv_multi(int nfiles, char **infiles, FILE *outfile)
93 {
94 int rv, nf, nchars, nc;
95 psf_dword mc;
96 CPI_FILE cpi;
97 CP_HEAD *cph;
98 CP_FONT *cpf, *cpf2;
99 PSF_FILE psf;
100 long codepage = -1;
101 PSF_MAPPING *mapping = NULL;
102 char mapname[20];
103 FILE *fp;
104
105 if (drfont && nt) return "Output format can't be both DRFONT and FONT.NT";
106 /* Always create the file as FONT. We will convert to FONT.NT or DRFONT
107 * later if required */
108 cpi_new(&cpi, CPI_FONT);
109 for (nf = 0; nf < nfiles; nf++)
110 {
111 /* The syntax "+nnn" denotes a codepage number */
112 if (infiles[nf][0] == '+' && isdigit(infiles[nf][1]))
113 {
114 char *equals;
115
116 if (!sscanf(&infiles[nf][1], "%lu", &codepage))
117 codepage = -1;
118
119 /* Doesn't handle 65280 properly in 16-bit DOS
120 * codepage = atoi(&infiles[nf][1]); */
121 equals = strchr(infiles[nf], '=');
122 if (equals) strcpy(mapname, equals + 1);
123 else sprintf(mapname, "CP%ld", codepage);
124 mapping = psf_find_mapping(mapname);
125 if (!mapping)
126 {
127 fprintf(stderr, "Warning: This program contains"
128 " no mapping for codepage %ld.\nThe first 256"
129 " characters of provided PSFs will be used.\n",
130 codepage);
131 }
132 else if (verbose)
133 fprintf(stderr, "Processing codepage %ld\n",
134 codepage);
135 continue;
136 }
137 if (codepage < 0)
138 return "No codepage specified for first PSF file.";
139 if (verbose)
140 fprintf(stderr,"Processing file: '%s'\n",
141 infiles[nf]);
142 if (!strcmp(infiles[nf], "-")) fp = stdin;
143 else fp = fopen(infiles[nf], "rb");
144
145 if (!fp)
146 {
147 perror(infiles[nf]);
148 return "Cannot open font file.";
149 }
150 psf_file_new(&psf);
151 rv = psf_file_read(&psf, fp);
152 if (fp != stdin) fclose(fp);
153 if (rv != PSF_E_OK) return psf_error_string(rv);
154 /* PSF loaded. Get the codepage & font to load into. */
155 cph = cpi_add_page(&cpi, (unsigned)codepage);
156 if (!cph) return "Out of memory adding codepage to file";
157 for (cpf = cph->fonts; cpf != NULL; cpf = cpf->next)
158 {
159 if (cpf->height == psf.psf_height &&
160 cpf->width == psf.psf_width)
161 {
162 fprintf(stderr, "Codepage %ld already has a %dx%d font; it will be replaced\n", codepage, cpf->height, cpf->width);
163 if (cpf->data) free(cpf->data);
164 cpf->data = NULL;
165 cpf->fontsize = 0;
166 break;
167 }
168 }
169 if (cpf == NULL)
170 {
171 cpf = malloc(sizeof(CP_FONT));
172 if (cpf == NULL) return "Out of memory adding font to codepage";
173 memset(cpf, 0, sizeof(CP_FONT));
174 if (!cph->fonts) cph->fonts = cpf;
175 else for (cpf2 = cph->fonts; cpf2 != NULL; cpf2 = cpf2->next)
176 {
177 if (cpf2->next == NULL)
178 {
179 cpf2->next = cpf;
180 break;
181 }
182 }
183 }
184 strcpy(cph->dev_name, device);
185 cph->dev_type = 1;
186 /* OK. cpf is the data structure for the new font, and it's in the chain */
187 if (psf.psf_length < 256)
188 {
189 fprintf(stderr, "Warning: %s has fewer than 256 characters.\n", infiles[nf]);
190 nchars = psf.psf_length;
191 }
192 else nchars = 256;
193 cpf->nchars = nchars;
194 cpf->height = psf.psf_height;
195 cpf->width = psf.psf_width;
196 if (psf.psf_width > 8)
197 {
198 fprintf(stderr, "Warning: %s has a width of %ld\n",
199 infiles[nf], psf.psf_width);
200 }
201 cpf->fontsize = nchars * psf.psf_charlen;
202 cpf->data = malloc(cpf->fontsize);
203 if (cpf->data == NULL) return "Out of memory adding bitmap to font";
204 memset(cpf->data, 0, cpf->fontsize);
205 if (mapping && !psf_is_unicode(&psf))
206 {
207 fprintf(stderr, "Warning: File %s has no "
208 "Unicode table.\nThe first 256 "
209 "characters will be used.\n", infiles[nf]);
210 }
211 if (mapping && psf_is_unicode(&psf))
212 {
213 psf_dword codepoint;
214
215 for (nc = 0; nc < 256; nc++)
216 {
217 codepoint = mapping->psfm_tokens[nc][0];
218 if (psf_unicode_lookupmap(&psf,mapping,nc,&mc, &codepoint))
219 {
220 /* Not found */
221 if (!psf_unicode_banned(codepoint)) fprintf(stderr, "Warning: %s has no bitmap for U+%04lx\n", infiles[nf], codepoint);
222 }
223 else
224 {
225 memcpy(cpf->data + nc * psf.psf_charlen,
226 psf.psf_data + mc * psf.psf_charlen,
227 psf.psf_charlen);
228 }
229 }
230 }
231 else /* No mapping, just grab the font as it is */
232 {
233 memcpy(cpf->data, psf.psf_data, cpf->fontsize);
234 }
235 psf_file_delete(&psf);
236 }
237 if (drfont) rv = cpi_crush(&cpi);
238 else
239 {
240 rv = cpi_bloat(&cpi);
241 if (nt) strcpy(cpi.format, "FONT.NT");
242 }
243
244 if (msgbuf[0])
245 {
246 cpi.comment = malloc(strlen(msgbuf));
247 if (cpi.comment)
248 {
249 cpi.comment_len = strlen(msgbuf);
250 memcpy(cpi.comment, msgbuf, cpi.comment_len);
251 }
252 else rv = CPI_ERR_NOMEM;
253 }
254 if (!rv) rv = cpi_savefile(&cpi, outfile, inter);
255 if (!rv) return NULL;
256 return psf_error_string(rv);
257 }
258
259