1 /*
2     psftools: Manipulate console fonts in the .PSF format
3     Copyright (C) 2005-6  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 #include "cnvshell.h"
20 #include "psflib.h"
21 #include "cpi.h"
22 
23 /* Extract a font from a .CPI file.
24  *
25  */
26 
27 static char helpbuf[2048];
28 static int height = -1;
29 static int cpage = -1;
30 static int v1 = 0, v2 = 0;
31 static PSF_MAPPING *codepage = NULL;
32 static PSF_FILE psf;
33 static CPI_FILE cpi;
34 
35 /* Program name */
36 char *cnv_progname = "CPI2PSF";
37 
38 /* ddash = 1 if option started with a double-dash; else 0 */
39 /* Return NULL if OK, else error string */
cnv_set_option(int ddash,char * variable,char * value)40 char *cnv_set_option(int ddash, char *variable, char *value)
41 {
42     if (!stricmp(variable, "srcpage") || !stricmp(variable, "codepage"))
43     {
44 	    char buf[20];
45 	    cpage  = atoi(value);
46 	    sprintf(buf, "CP%d", cpage);
47 	    codepage = psf_find_mapping(buf);
48 	    if (codepage == NULL)
49 	    {
50 		    fprintf(stderr, "Warning: No Unicode mapping known for codepage %d\n", cpage);
51 	    }
52 	    return NULL;
53 
54     }
55     if (!stricmp(variable, "height")) { height = atoi(value); return NULL; }
56     if (!stricmp(variable, "psf1"))   { v1 = 1; return NULL; }
57     if (!stricmp(variable, "psf2"))   { v2 = 1; return NULL; }
58     if (strlen(variable) > 2000) variable[2000] = 0;
59     sprintf(helpbuf, "Unknown option: %s\n", variable);
60     return helpbuf;
61     }
62 
63 
64 /* Return help string */
cnv_help(void)65 char *cnv_help(void)
66     {
67     sprintf(helpbuf, "Syntax: %s cpifile psffile { options }\n\n", cnv_progname);
68     strcat (helpbuf, "Options: \n"
69 	             "    --height=x     Extract the font with the given height\n"
70 	             "    --srcpage=x    Only extract codepage x\n"
71                      "    --psf1         Output in PSF1 format\n"
72                      "    --psf2         Output in PSF2 format (default) \n");
73 
74     return helpbuf;
75 }
76 
77 static psf_dword *unicodes = NULL;
78 static int unicodes_max = 0, unicodes_count = 0;
79 
unicode_add(psf_dword * token)80 static psf_errno_t unicode_add(psf_dword *token)
81 {
82 	psf_dword *tmp;
83 	int n;
84 
85 	for (n = 0; n < unicodes_count; n++)
86 	{
87 		if (unicodes[n] == token[0]) return PSF_E_OK;
88 	}
89 
90 	if (unicodes_count >= unicodes_max)
91 	{
92 		tmp = calloc(unicodes_max + 256, sizeof(psf_dword));
93 		if (!tmp) return PSF_E_NOMEM;
94 		if (unicodes) memcpy(tmp, unicodes,
95 				unicodes_max * sizeof(psf_dword));
96 		free(unicodes);
97 		unicodes = tmp;
98 		unicodes_max += 256;
99 	}
100 	unicodes[unicodes_count++] = token[0];
101 	return PSF_E_OK;
102 }
103 
cnv_execute(FILE * infile,FILE * outfile)104 char *cnv_execute(FILE *infile, FILE *outfile)
105 {
106 	int rv, m, n, err;
107 	CP_HEAD *cph;
108 	CP_FONT *cpf;
109 	int  nchars, fontw = 0;
110 	psf_dword glyph;
111 	char buf[20];
112 
113 	nchars = 0;
114 	err = cpi_loadfile(&cpi, infile);
115 	if (err == CPI_ERR_BADFMT) return "File is not a valid CPI file";
116 /* This relies on the same error codes being used by the CPI & PSF libs */
117 	if (err) return psf_error_string(err);
118 
119 /* If no height given, take the first one we find. If there is a height
120  * given, then look it up anyway so we know the width to use for the
121  * output. */
122 	fontw = -1;
123 	for (cph = cpi.firstpage; cph != NULL; cph = cph->next)
124 		for (cpf = cph->fonts; cpf != NULL; cpf = cpf->next)
125 	{
126 		if (height == -1 || cpf->height == height)
127 		{
128 			height = cpf->height;
129 			fontw = cpf->width;
130 			goto gotheight;	/* Break out of 2 loops */
131 		}
132 	}
133 gotheight:
134 	if (fontw < 0) return "Requested font size not found in CPI file.";
135 /* Prepare for dump... */
136 	for (cph = cpi.firstpage; cph != NULL; cph = cph->next)
137 	{
138 		/* If user requested only one codepage, only do that
139 		 * codepage */
140 		if (cpage != -1 && cph->codepage != cpage) continue;
141 
142 	  	sprintf(buf, "CP%d", cph->codepage);
143 	    	codepage = psf_find_mapping(buf);
144 	    	if (cpage == -1 && codepage == NULL)
145 	    	{
146 	    		fprintf(stderr, "Warning: No Unicode mapping "
147 				"known for codepage %d; it will "
148 				"be skipped.\n", cph->codepage);
149 			continue;
150 		}
151 		if (!cph->fonts)
152 		{
153 			fprintf(stderr, "Codepage %d has no fonts; "
154 					"it will be skipped.\n",
155 					cph->codepage);
156 		}
157 		nchars = cph->fonts->nchars;
158 		if (codepage)
159 		{
160 			if (nchars > 256) nchars = 256;
161 			for (n = 0; n < nchars; n++)
162 			{
163 				if (unicode_add(codepage->psfm_tokens[n]))
164 					return "Out of memory";
165 			}
166 		}
167 	}
168 /* OK. There are unicodes_count unique codepoints */
169 	psf_file_new(&psf);
170 	if (unicodes_count)
171 	{
172 /* 1.0.3: If extracting a single page, create a PSF that will hold 256
173  * characters, rather than merging identical Unicode codepoints */
174 		if (cpage == -1)
175 			rv = psf_file_create(&psf, fontw, height, unicodes_count, 1);
176 		else	rv = psf_file_create(&psf, fontw, height, nchars, 1);
177 	}
178 	else	rv = psf_file_create(&psf, fontw, height, nchars, 0);
179 	if (rv) return psf_error_string(rv);
180 /* Dump the character bitmaps */
181 	for (cph = cpi.firstpage; cph != NULL; cph = cph->next)
182 	{
183 		/* Include this codepage? */
184 		if (cpage != -1 && cph->codepage != cpage) continue;
185 
186 		sprintf(buf, "CP%d", cph->codepage);
187 	    	codepage = psf_find_mapping(buf);
188 		if (cpage == -1 && codepage == NULL) continue;
189 		if (!cph->fonts) continue;
190 		nchars = cph->fonts->nchars;
191 		if (nchars > 256) nchars = 256;
192 		for (n = 0; n < nchars; n++)
193 		{
194 			cpi_byte *dst = NULL;
195 			cpi_byte *src = cpi_find_glyph(&cpi,
196 					cph->codepage, height, fontw, n);
197 			if (!src) continue;
198 /* psftools 1.0.3: If extracting a single page, then retain the order of
199  * characters that it used. For compatibility with the way --codepage works
200  * in other psftools. */
201 			if (codepage && (cpage == -1))
202 			{
203 				glyph = codepage->psfm_tokens[n][0];
204 				for (m = 0; m < unicodes_count; m++)
205 				{
206 					if (unicodes[m] == glyph)
207 					{
208 						dst = psf.psf_data + m * psf.psf_charlen;
209 /* Add the entry, if it doesn't exist */
210 						if (psf_unicode_lookup(&psf, glyph, NULL) == PSF_E_NOTFOUND) psf_unicode_addmap(&psf, m, codepage, n);
211 						break;
212 					}
213 				}
214 			}
215 /* Extracting a single codepage, but we can add Unicode mappings */
216 			else
217 			{
218 				dst = psf.psf_data + n * psf.psf_charlen;
219 				if (codepage && (n < 256))
220 				{
221 					psf_unicode_addmap(&psf, n, codepage, n);
222 				}
223 			}
224 			if (dst) memcpy(dst, src, psf.psf_charlen);
225 		}
226 	}
227 	if (v1) psf_force_v1(&psf);
228 	if (v2) psf_force_v2(&psf);
229 	rv = psf_file_write(&psf, outfile);
230 	psf_file_delete(&psf);
231 	if (rv) return psf_error_string(rv);
232 	return NULL;
233 }
234 
235