1 /*
2 * xvzx.c - load routine for Spectrum screen$
3 *
4 * John Elliott, 7 August 1998
5 *
6 * LoadZX(fname, pinfo) - load file
7 * WriteZX(fp,pic,ptype,w,h,r,g,b,numcols,style,cmt,comment) - convert to
8 * 256x192 SCREEN$ and save.
9 */
10
11 #include "copyright.h"
12
13 #include "xv.h"
14
15
16
17 /*
18 * comments on error handling:
19 * a file with a bad header checksum is a warning error.
20 *
21 * not being able to malloc is a Fatal Error. The program is aborted.
22 */
23
24
25 #define TRUNCSTR "File appears to be truncated."
26
27 static int zxError PARM((const char *, const char *));
28
29 static const char *bname;
30
31 /*******************************************/
LoadZX(fname,pinfo)32 int LoadZX(fname, pinfo)
33 char *fname;
34 PICINFO *pinfo;
35 /*******************************************/
36 {
37 /* returns '1' on success */
38
39 FILE *fp;
40 unsigned int c, c1;
41 int x,y, trunc;
42 byte *zxfile;
43
44 bname = BaseName(fname);
45
46 pinfo->pic = (byte *) NULL;
47 pinfo->comment = (char *) NULL;
48
49 /* Allocate memory for a 256x192x8bit image */
50
51 pinfo->pic = (byte *)malloc(256*192);
52 if (!pinfo->pic) FatalError("malloc failure in xvzx.c LoadZX");
53
54 /* Allocate 1B80h bytes and slurp the whole file into memory */
55
56 zxfile = (byte *)malloc(7040);
57 if (!zxfile) FatalError("malloc failure in xvzx.c LoadZX");
58
59 /* open the file */
60 fp = xv_fopen(fname,"r");
61 if (!fp) return (zxError(bname, "can't open file"));
62
63 /* Load it in en bloc */
64 memset(zxfile, 0, 7040);
65 if (fread(zxfile, 1, 7040, fp) < 7040) trunc = 1;
66
67 /* Transform to 8-bit */
68
69 for (y = 0; y < 192; y++) for (x = 0; x < 256; x++)
70 {
71 /* Spectrum screen layout: three 2k segments at y=0, y=64, y=128 */
72 /* In each segment: Scan lines 0,8,16,...,56,1,9,...,57 etc. Each
73 scanline is 32 bytes, so line 1 is 256 bytes after line 0
74
75 So address of line start is ((y>>6) * 2048) + ((y & 7) * 256)
76 + ((y & 0x38) * 4)
77
78 The colour byte for a cell is at screen + 6k + (y >> 3)*32 + (x>>3)
79
80 */
81
82 int offset;
83 byte *dst = pinfo->pic + 256*y + x;
84 byte attr, pt, mask;
85
86 offset = (y >> 6) * 2048;
87 offset += (y & 7) * 256;
88 offset += (y & 0x38) * 4;
89 offset += (x >> 3);
90
91 pt = zxfile[offset + 128]; /* Ink/paper map */
92
93 offset = 0x1880;
94 offset += (y >> 3) * 32;
95 offset += (x >> 3);
96
97 attr = zxfile[offset]; /* Colours for cell */
98
99 mask = 0x80;
100
101 if (x & 7) mask >>= (x & 7);
102
103 if (pt & mask) *dst = attr & 7; /* Ink */
104 else *dst = (attr >> 3) & 7; /* Paper */
105
106 if (attr & 0x40) *dst |= 8; /* High intensity */
107 }
108
109 /* Picture bytes converted; now build the colour maps */
110
111 pinfo->normw = pinfo->w = 256;
112 pinfo->normh = pinfo->h = 192;
113 pinfo->type = PIC8;
114
115 for (c = 0; c < 16; c++)
116 {
117 if (c < 8) c1 = 192; else c1 = 255; /* low-intensity colours use 192 */
118 /* high-intensity colours use 255 */
119 pinfo->b[c] = (c & 1 ? c1 : 0);
120 pinfo->r[c] = (c & 2 ? c1 : 0);
121 pinfo->g[c] = (c & 4 ? c1 : 0);
122 }
123
124 pinfo->colType = F_FULLCOLOR;
125 pinfo->frmType = F_ZX; /* Save as SCREEN$ */
126 sprintf(pinfo->fullInfo, "Spectrum SCREEN$, load address %04x",
127 zxfile[16]+256*zxfile[17]);
128 strcpy(pinfo->shrtInfo, "Spectrum SCREEN$.");
129
130 /* Almost as an afterthought, check that the +3DOS header is valid.
131
132 If it isn't, then odds are that the file isn't a graphic. But it
133 had the right magic number, so it might be. Let them see it anyway.
134
135 The check is: Byte 127 of the header should be the 8-bit sum of bytes
136 0-126 of the header. The header should also have the
137 +3DOS magic number, but we know it does or we wouldn't
138 have got this far.
139 */
140
141 c1 = 0;
142 for (c1 = c = 0; c < 127; c++) c1 = ((c1 + zxfile[c]) & 0xFF);
143 if (c1 != zxfile[127]) zxError(bname, "Bad header checksum.");
144
145 fclose(fp);
146 free(zxfile);
147 return 1;
148 }
149
150
151
152
153
154 /*******************************************/
zxError(fname,st)155 static int zxError(fname, st)
156 const char *fname, *st;
157 {
158 SetISTR(ISTR_WARNING,"%s: %s", fname, st);
159 return 0;
160 }
161
162
163 /* Spectrum screen file header. The first 18 bytes are used in the magic
164 number test */
165
166 byte ZXheader[128] =
167 {
168 'P', 'L', 'U', 'S', '3', 'D', 'O', 'S', 26, /* Spectrum +3DOS file */
169 1, 0, /* Header type 1.0 */
170 128, 27, 0, 0, /* 7040 bytes */
171 3, /* Binary format */
172 0, 27, /* 6912 data bytes */
173 0, 64 /* load address 0x4000 */
174 };
175
176
177
178 /* Get the Spectrum colour/bright byte (0-15) from a pixel */
179
PointZX(pic,w,h,rmap,gmap,bmap,x,y)180 static int PointZX(pic, w, h, rmap, gmap, bmap, x, y)
181 byte *pic;
182 int w,h;
183 byte *rmap, *gmap, *bmap;
184 int x,y;
185 {
186 int index, r, g, b, zxc;
187
188 /* If the picture is smaller than the screen, pad out the edges
189 with "bright black" - a colour not otherwise returned */
190
191 if (x >= w || y >= h) return 8;
192
193 /* Get colour index */
194
195 index = pic[y*w + x];
196
197 /* Convert to rgb */
198
199 r = rmap[index];
200 g = gmap[index];
201 b = bmap[index];
202 zxc = 0;
203
204 /* Work out Spectrum colour by a simplistic "nearest colour" method */
205
206 if (b >= 160) zxc |= 1; /* Blue */
207 if (r >= 160) zxc |= 2; /* Red */
208 if (g >= 160) zxc |= 4; /* Green */
209 if (r > 208 || g >= 208 || b >= 208) zxc |= 8; /* High intensity */
210
211 return zxc;
212 }
213
214
215 /* Work out what colours should be used in a cell */
216
CellZX(pic,w,h,rmap,gmap,bmap,cx,cy,zxfile)217 static void CellZX(pic, w, h, rmap, gmap, bmap, cx, cy, zxfile)
218 byte *pic;
219 int w,h;
220 byte *rmap, *gmap, *bmap;
221 int cx,cy;
222 byte *zxfile;
223 {
224 byte counts[16]; /* Count of no. of colours */
225 int offset, ink, paper, n, m, x, y, x0, y0, di, dp;
226
227 x0 = cx * 8; /* Convert from cell to pixel coords */
228 y0 = cy * 8;
229
230 for (n = 0; n < 16; n++) counts[n] = 0; /* Reset all counts */
231
232 /* Count no. of pixels of various colours */
233
234 for (y = y0; y < y0+8; y++) for (x = x0; x < x0+8; x++)
235 {
236 m = PointZX(pic, w, h, rmap, gmap, bmap, x, y);
237
238 counts[m]++;
239 }
240 counts[8] = 0; /* Discard Bright Black (pixels not in the picture area)
241 */
242
243 /* Assign the most popular colour as ink */
244 for (n = m = ink = 0; n < 16; n++) if (counts[n] > m)
245 {
246 ink = n;
247 m = counts[n];
248 }
249 counts[ink] = 0;
250
251 /* Assign the next most popular colour as paper */
252 for (n = m = paper = 0; n < 16; n++) if (counts[n] > m)
253 {
254 paper = n;
255 m = counts[n];
256 }
257 /* We have ink and paper. Set cell's attributes */
258
259 offset = cy*32 + cx + 0x1880;
260
261 /* Set the high-intensity bit if ink is high-intensity */
262 if (ink & 8) zxfile[offset] = 0x40; else zxfile[offset] = 0;
263 zxfile[offset] |= ((paper & 7) << 3);
264 zxfile[offset] |= (ink & 7);
265
266 /* Plot the points */
267 for (y = y0; y < y0+8; y++)
268 {
269 byte mask = 0x80;
270
271 offset = (y >> 6) * 2048;
272 offset += (y & 7) * 256;
273 offset += (y & 0x38) * 4;
274 offset += (x0 >> 3);
275
276 for (x = x0; x < x0+8; x++)
277 {
278 /* Work out whether the point should be plotted as ink or
279 paper */
280 m = PointZX(pic, w, h, rmap, gmap, bmap, x, y);
281
282 di = (ink & 7) - (m & 7); /* "Difference" from ink */
283 dp = (paper & 7) - (m & 7); /* "Difference" from paper */
284
285 if (di < 0) di = -di;
286 if (dp < 0) dp = -dp;
287
288 if (di < dp) /* Point is more like ink */
289 zxfile[offset+128] |= mask;
290
291 mask = (mask >> 1);
292 }
293 }
294
295 }
296
297
298
299 /*******************************************/
WriteZX(fp,pic,ptype,w,h,rmap,gmap,bmap,numcols,colorstyle,comment)300 int WriteZX(fp,pic,ptype,w,h,rmap,gmap,bmap,numcols,colorstyle,comment)
301 FILE *fp;
302 byte *pic;
303 int ptype, w,h;
304 byte *rmap, *gmap, *bmap;
305 int numcols, colorstyle;
306 char *comment;
307 {
308 int rv, x, y;
309 byte *zxfile;
310 byte *pic8;
311 byte rtemp[256],gtemp[256],btemp[256];
312
313 /* To simplify matters, reduce 24-bit to 8-bit. Since the Spectrum
314 screen is 3.5-bit anyway, it doesn't make much difference */
315
316 if (ptype == PIC24)
317 {
318 pic8 = Conv24to8(pic, w, h, 256, rtemp,gtemp,btemp);
319 if (!pic8) FatalError("Unable to malloc in WriteZX()");
320 rmap = rtemp; gmap = gtemp; bmap = btemp; numcols=256;
321 }
322 else pic8 = pic;
323
324 ZXheader[127] = 0x71; /* The correct checksum. */
325
326 /* Create a memory image of the SCREEN$ */
327
328 zxfile = (byte *)malloc(7040);
329 if (!zxfile) FatalError("malloc failure in xvzx.c WriteZX");
330
331 memset(zxfile, 0, 7040); /* Reset all points to black */
332 memcpy(zxfile, ZXheader, 128); /* Create +3DOS header */
333
334 /* Convert the image, character cell by character cell */
335 for (y = 0; y < 24; y++) for (x = 0; x < 32; x++)
336 {
337 CellZX(pic8, w, h, rmap, gmap, bmap, x, y, zxfile);
338 }
339 rv = 0;
340 if (fwrite(zxfile, 1, 7040, fp) < 7040) rv = -1;
341
342 if (ptype == PIC24) free(pic8);
343 free(zxfile);
344
345 if (ferror(fp)) rv = -1;
346
347 return rv;
348 }
349
350