1 /*
2  * xviff.c - load routine for IFF ILBM format pictures
3  *
4  * LoadIFF(fname, pinfo)  -  loads an IFF file
5  *
6  *
7  * This loader module for xv will load most types of IFF ILBM pictures.
8  * Apart from any 'normal' ILBM format, this loader understands
9  * HAM (Hold-And-Modify), HAM8 (extended HAM), EHB (Extra Half Brite)
10  * and 24-Bit.
11  * Multipalette ILBMs are not supported yet, but I'll be working on that!
12  *
13  *
14  * History:
15  *  30-May-93: Project started
16  *             ILBMs can now be loaded from xv
17  *             EHB support added
18  *             HAM support added
19  *  01-May-93: Started HAM8 support
20  *  04-Jun-93: HAM8 finally works! =:)
21  *             24 Bit support works!
22  */
23 
24 
25 /* Copyright Notice
26  * ================
27  * Copyright 1993 by Thomas Meyer
28  *
29  * Author:
30  *   Thomas Meyer
31  *   i03a@alf.zfn.uni-bremen.de
32  *
33  * Permission is hereby granted to use this code only as a part of
34  * the xv distribution by John Bradley.
35  */
36 
37 
38 #include "xv.h"
39 
40 static long filesize;
41 
42 /* static int           readID       PARM((FILE *, char *));  DOES NOT EXIST */
43 static int           iffError     PARM((const char *, const char *));
44 static void          decomprle    PARM((byte *, byte *, long, long));
45 static unsigned int  iff_getword  PARM((byte *));
46 static unsigned long iff_getlong  PARM((byte *));
47 
48 
49 static const char *bname;
50 
51 
52 /* Define internal ILBM types */
53 #define ILBM_NORMAL     0
54 #define ILBM_EHB        1
55 #define ILBM_HAM        2
56 #define ILBM_HAM8       3
57 #define ILBM_24BIT      4
58 
59 
60 
61 /*******************************************/
LoadIFF(fname,pinfo)62 int LoadIFF(fname, pinfo)
63      char    *fname;
64      PICINFO *pinfo;
65 /*******************************************/
66 {
67   /* returns '1' on success */
68 
69   register byte bitmsk, rval, gval, bval;
70   register long col, colbit;
71   FILE          *fp;
72   int           rv;
73   int           BMHDok, CMAPok, CAMGok;
74   int           bmhd_width, bmhd_height, bmhd_bitplanes, bmhd_transcol;
75   int           i, j, k, lineskip, colors, fmt;
76   int           npixels = 0; /* needs to be initialized _outside_ while-loop */
77   byte          bmhd_masking, bmhd_compression;
78   long          chunkLen, camg_viewmode;
79   byte          *databuf, *dataptr, *cmapptr, *picptr, *pic, *bodyptr;
80   byte          *workptr, *workptr2, *workptr3, *decomp_mem;
81 
82   BMHDok = CAMGok = bmhd_width = bmhd_height = bmhd_bitplanes = colors = 0;
83   bmhd_compression = 0;
84   camg_viewmode = 0;
85 
86   bname = BaseName(fname);
87   databuf = picptr = decomp_mem = NULL;
88 
89   pinfo->pic     = (byte *) NULL;
90   pinfo->comment = (char *) NULL;
91 
92   /* open the file */
93   fp = xv_fopen(fname,"r");
94   if (!fp) return (iffError(bname, "cannot open file"));
95 
96   /* compute file length */
97   fseek(fp, 0L, 2);
98   filesize = ftell(fp);
99   fseek(fp, 0L, 0);
100 
101   /* allocate memory for complete file */
102   if((databuf = (byte *) malloc((size_t) filesize)) == NULL) {
103     fclose(fp);
104     FatalError("xviff: cannot malloc file buffer");
105   }
106 
107   fread(databuf, (size_t) filesize, (size_t) 1, fp);
108   fclose(fp);
109 
110 
111   /* initialize work pointer. used to trace the buffer for IFF chunks */
112   dataptr = databuf;
113 
114 
115   /* check if we really got an IFF file */
116   if(strncmp((char *) dataptr, "FORM", (size_t) 4) != 0) {
117     free(databuf);
118     return(iffError(bname, "not an IFF file"));
119   }
120 
121 
122   dataptr = dataptr + 8;                  /* skip ID and length of FORM */
123 
124   /* check if the IFF file is an ILBM (picture) file */
125   if(strncmp((char *) dataptr, "ILBM", (size_t) 4) != 0) {
126     free(databuf);
127     return(iffError(bname, "not an ILBM file"));
128   }
129 
130   if (DEBUG) fprintf(stderr,"IFF ILBM file recognized\n");
131 
132   dataptr = dataptr + 4;                                /* skip ID */
133 
134   /* initialize return value to -1. used to indicate when we are finished with
135      decoding the picture (happens when BODY chunk was found */
136   rv = -1;
137 
138   /* main decoding loop. searches IFF chunks and handles them. terminates when
139      BODY chunk was found or dataptr ran over end of file */
140 
141   while ((rv<0) && (dataptr < (databuf + filesize))) {
142     chunkLen = (iff_getlong(dataptr + 4) + 1) & 0xfffffffe; /* make even */
143 
144     if (strncmp((char *) dataptr, "BMHD", (size_t) 4)==0) { /* BMHD chunk? */
145       bmhd_width       = iff_getword(dataptr + 8);      /* width of picture */
146       bmhd_height      = iff_getword(dataptr + 8 + 2);  /* height of picture */
147       bmhd_bitplanes   = *(dataptr + 8 + 8);            /* # of bitplanes */
148       bmhd_masking     = *(dataptr + 8 + 9);
149       bmhd_compression = *(dataptr + 8 + 10);           /* get compression */
150       bmhd_transcol    = iff_getword(dataptr + 8 + 12);
151       BMHDok = 1;                                       /* got BMHD */
152       dataptr += 8 + chunkLen;                          /* to next chunk */
153 
154       npixels = bmhd_width * bmhd_height;  /* 65535*65535 max */
155       if (bmhd_width <= 0 || bmhd_height <= 0
156           || npixels/bmhd_width != bmhd_height)
157         return (iffError(bname, "xviff: image dimensions out of range"));
158     }
159 
160 
161     else if (strncmp((char *) dataptr, "CMAP", (size_t) 4)==0) { /* CMAP ? */
162       cmapptr = dataptr + 8;
163       colors = chunkLen / 3;                            /* calc no of colors */
164 
165       /* copy colors to color map */
166       for (i=0; i < colors; i++) {
167 	pinfo->r[i] = *cmapptr++;
168 	pinfo->g[i] = *cmapptr++;
169 	pinfo->b[i] = *cmapptr++;
170       }
171 
172       CMAPok = 1;                                       /* got CMAP */
173       dataptr += 8 + chunkLen;                          /* to next chunk */
174     }
175 
176 
177     else if (strncmp((char *) dataptr, "CAMG", (size_t) 4)==0) {  /* CAMG ? */
178       camg_viewmode = iff_getlong(dataptr + 8);             /* get viewmodes */
179       CAMGok = 1;                                       /* got CAMG */
180       dataptr += 8 + chunkLen;                          /* to next chunk */
181     }
182 
183 
184     else if (strncmp((char *) dataptr, "BODY", (size_t) 4)==0) { /* BODY ? */
185       int byte_width = (((bmhd_width + 15) >> 4) << 1);  /* 8192 max */
186 
187       bodyptr = dataptr + 8;                            /* -> BODY data */
188 
189       if (BMHDok) {                                     /* BMHD found? */
190 	/* if BODY is compressed, allocate buffer for decrunched BODY and
191 	   decompress it (run length encoding) */
192 
193 	if (bmhd_compression == 1) {
194 	  /* calc size of decrunch buffer - (size of the actual picture
195 	     decompressed in interleaved Amiga bitplane format) */
196 
197 	  int bytes_per_bitplane = byte_width * bmhd_height; /* 536862720 max */
198           long decomp_bufsize = bytes_per_bitplane * bmhd_bitplanes;
199 
200 	  if (byte_width <= 0 || bmhd_height <= 0 ||
201 	      bytes_per_bitplane/byte_width != bmhd_height ||
202 	      decomp_bufsize/bytes_per_bitplane != bmhd_bitplanes)
203 	  {
204 	    return (iffError(bname, "xviff: image dimensions out of range"));
205 	  }
206 
207 	  if ((decomp_mem = (byte *)malloc((size_t) decomp_bufsize)) != NULL) {
208 	    decomprle(dataptr + 8, decomp_mem, chunkLen, decomp_bufsize);
209 	    bodyptr = decomp_mem;                 /* -> uncompressed BODY */
210 	    free(databuf);                        /* free old data buffer */
211 	    databuf = NULL;
212 	  }
213 	  else {
214 	    free(databuf);
215 	    FatalError("xviff: cannot malloc() decrunch buffer");
216 	  }
217 	}
218 
219 
220 	/* the following determines the type of the ILBM file.
221 	   it's either NORMAL, EHB, HAM, HAM8 or 24BIT */
222 
223 	fmt = ILBM_NORMAL;                        /* assume normal ILBM */
224 	/* FIXME:  does ILBM_NORMAL really support up to 255 bitplanes? */
225 
226 	if      (bmhd_bitplanes == 24) fmt = ILBM_24BIT;
227 	else if (bmhd_bitplanes == 8) {
228 	  if (CAMGok && (camg_viewmode & 0x800)) fmt = ILBM_HAM8;
229 	}
230 
231 	else if ((bmhd_bitplanes > 5) && CAMGok) {
232 	  if (camg_viewmode & 0x80) fmt = ILBM_EHB;
233 	  else if (camg_viewmode & 0x800) fmt = ILBM_HAM;
234 	}
235 
236 
237 	if (DEBUG) {
238 	  fprintf(stderr, "LoadIFF: %s %dx%d, planes=%d (%d cols), comp=%d\n",
239 		  (fmt==ILBM_NORMAL) ? "Normal ILBM" :
240 		  (fmt==ILBM_HAM)    ? "HAM ILBM" :
241 		  (fmt==ILBM_HAM8)   ? "HAM8 ILBM" :
242 		  (fmt==ILBM_EHB)    ? "EHB ILBM" :
243 		  (fmt==ILBM_24BIT)  ? "24BIT ILBM" : "unknown ILBM",
244 		  bmhd_width, bmhd_height, bmhd_bitplanes,
245 		  1<<bmhd_bitplanes, bmhd_compression);
246 	}
247 
248 
249 	if ((fmt==ILBM_NORMAL) || (fmt==ILBM_EHB) || (fmt==ILBM_HAM)) {
250 	  if (DEBUG) fprintf(stderr,"Converting CMAP from normal ILBM CMAP\n");
251 
252 	  switch(fmt) {
253 	  case ILBM_NORMAL: colors = 1 << bmhd_bitplanes; break;
254 	  case ILBM_EHB:    colors = 32; break;
255 	  case ILBM_HAM:    colors = 16; break;
256 	  }
257 
258 	  for (i=0; i < colors; i++) {
259 	    pinfo->r[i] = (pinfo->r[i] >> 4) * 17;
260 	    pinfo->g[i] = (pinfo->g[i] >> 4) * 17;
261 	    pinfo->b[i] = (pinfo->b[i] >> 4) * 17;
262 	  }
263 	}
264 
265 
266 	if ((fmt == ILBM_HAM) || (fmt == ILBM_HAM8) || (fmt == ILBM_24BIT)) {
267 	  int bufsize = 3 * npixels;
268 
269 	  if (bufsize/3 != npixels) {
270 	    if (databuf)    free(databuf);
271 	    if (decomp_mem) free(decomp_mem);
272 	    return (iffError(bname, "xviff: image dimensions out of range"));
273 	  }
274 	  if ((picptr=(byte *) malloc((size_t) bufsize)) == NULL) {
275 	    if (databuf)    free(databuf);
276 	    if (decomp_mem) free(decomp_mem);
277 	    return (iffError(bname, "xviff: no memory for decoded picture"));
278 	  }
279 
280 	  else {
281 	    pic = picptr;
282 	    workptr = bodyptr;
283 	    lineskip = byte_width;
284 
285 	    for (i=0; i<bmhd_height; i++) {
286 	      bitmsk = 0x80;
287 	      workptr2 = workptr;
288 
289 	      /* at start of each line, init RGB values to background */
290 	      rval = pinfo->r[0];
291 	      gval = pinfo->g[0];
292 	      bval = pinfo->b[0];
293 
294 	      for (j=0; j<bmhd_width; j++) {
295 		col = 0;
296 		colbit = 1;
297 		workptr3 = workptr2;
298 		for (k=0; k<bmhd_bitplanes; k++) {
299 		  if (*workptr3 & bitmsk) col += colbit;
300 		  workptr3 += lineskip;
301 		  colbit <<= 1;
302 		}
303 
304 		if (fmt==ILBM_HAM) {
305 		  switch (col & 0x30) {
306 		  case 0x00: rval = pinfo->r[col & 0x0f];
307 		             gval = pinfo->g[col & 0x0f];
308 		             bval = pinfo->b[col & 0x0f];
309 		             break;
310 
311 		  case 0x10: bval = (col & 0x0f) * 17;
312                              break;
313 
314 		  case 0x20: rval = (col & 0x0f) * 17;
315                              break;
316 
317 		  case 0x30: gval = (col & 0x0f) * 17;
318 		  }
319 		}
320 
321 		else if (fmt == ILBM_HAM8) {
322 		  switch(col & 0xc0) {
323 		  case 0x00: rval = pinfo->r[col & 0x3f];
324 		             gval = pinfo->g[col & 0x3f];
325                              bval = pinfo->b[col & 0x3f];
326                              break;
327 
328 		  case 0x40: bval = (bval & 3) | ((col & 0x3f) << 2);
329                              break;
330 
331 		  case 0x80: rval = (rval & 3) | ((col & 0x3f) << 2);
332                              break;
333 
334 		  case 0xc0: gval = (rval & 3) | ((col & 0x3f) << 2);
335 		  }
336 		}
337 
338 		else {
339 		  rval = col & 0xff;
340 		  gval = (col >> 8) & 0xff;
341 		  bval = (col >> 16) & 0xff;
342 		}
343 
344 		*pic++ = rval;
345 		*pic++ = gval;
346 		*pic++ = bval;
347 
348 		bitmsk = bitmsk >> 1;
349 		if (bitmsk == 0) {
350 		  bitmsk = 0x80;
351 		  workptr2++;
352 		}
353 	      }
354 	      workptr += lineskip * bmhd_bitplanes;
355 	    }
356 
357 	    pinfo->type = PIC24;
358 	  }
359 	}  /* HAM, HAM8, or 24BIT */
360 
361 
362 	else if ((fmt == ILBM_NORMAL) || (fmt == ILBM_EHB)) {
363 	  /* if bmhd_width and bmhd_height are OK (checked in BMHD block
364 	   * above; guaranteed by BMHDok), then npixels is OK, too */
365 	  if ((picptr = (byte *) malloc((size_t) npixels)) == NULL) {
366 	    if (databuf) free(databuf);
367 	    if (decomp_mem) free(decomp_mem);
368 	    return (iffError(bname, "xviff: no memory for decoded picture"));
369 	  }
370 
371 	  else if (fmt == ILBM_EHB) {
372 	    if (DEBUG) fprintf(stderr,"Doubling CMAP for EHB mode\n");
373 
374 	    for (i=0; i<32; i++) {
375 	      pinfo->r[i + colors] = pinfo->r[i] >> 1;
376 	      pinfo->g[i + colors] = pinfo->g[i] >> 1;
377 	      pinfo->b[i + colors] = pinfo->b[i] >> 1;
378 	    }
379 	  }
380 
381 	  pic = picptr;             /* ptr to chunky buffer */
382 	  workptr = bodyptr;        /* ptr to uncmp'd pic, planar format */
383 	  lineskip = byte_width;
384 
385 	  for (i=0; i<bmhd_height; i++) {
386 	    bitmsk = 0x80;                      /* left most bit (mask) */
387 	    workptr2 = workptr;                 /* work ptr to source */
388 	    for (j=0; j<bmhd_width; j++) {
389 	      col = 0;
390 	      colbit = 1;
391 	      workptr3 = workptr2;              /* ptr to byte in 1st pln */
392 
393 	      for (k=0; k<bmhd_bitplanes; k++) {
394 		if (*workptr3 & bitmsk)          /* if bit set in this pln */
395 		  col = col + colbit;           /* add bit to chunky byte */
396 		workptr3 += lineskip;           /* go to next line */
397 		colbit <<= 1;                   /* shift color bit */
398 	      }
399 
400 	      *pic++ = col;                     /* write to chunky buffer */
401 	      bitmsk = bitmsk >> 1;             /* shift mask to next bit */
402 	      if (bitmsk == 0) {                /* if mask is zero */
403 		bitmsk = 0x80;                  /* reset mask */
404 		workptr2++;                     /* mv ptr to next byte */
405 	      }
406 	    }  /* for j ... */
407 
408 	    workptr += lineskip * bmhd_bitplanes;  /* to next line */
409 	  }  /* for i ... */
410 
411 	  pinfo->type = PIC8;
412 	}  /* if NORMAL or EHB */
413 
414 
415 	/* fill up PICINFO */
416 
417 	pinfo->pic = picptr;
418 	pinfo->w = bmhd_width;
419 	pinfo->h = bmhd_height;
420 	pinfo->normw = pinfo->w;   pinfo->normh = pinfo->h;
421 	pinfo->colType = F_FULLCOLOR;
422 	pinfo->frmType = -1;
423 
424 	sprintf(pinfo->fullInfo, "%s (%ld bytes)",
425 		(fmt==ILBM_NORMAL) ? "IFF ILBM" :
426 		(fmt==ILBM_HAM)    ? "HAM ILBM" :
427 		(fmt==ILBM_HAM8)   ? "HAM8 ILBM" :
428 		(fmt==ILBM_EHB)    ? "EHB ILBM" :
429 		(fmt==ILBM_24BIT)  ? "24BIT ILBM" : "unknown ILBM",
430 		filesize);
431 
432 	sprintf(pinfo->shrtInfo, "%dx%d ILBM.", bmhd_width, bmhd_height);
433 
434 	rv = 1;
435 
436       }  /* if BMHDok */
437 
438       else rv = 0;                   /* didn't get BMHD header */
439 
440     }  /* "BODY" chunk */
441 
442     else {
443       if (DEBUG)
444 	fprintf(stderr,"Skipping unknown chunk '%c%c%c%c'\n",
445                 *dataptr, *(dataptr+1), *(dataptr+2), *(dataptr+3));
446 
447       dataptr = dataptr + 8 + chunkLen;             /* skip unknown chunk */
448     }
449   }
450 
451 
452   if (decomp_mem) free(decomp_mem);
453   if (databuf)    free(databuf);
454 
455   if (rv != 1) free(picptr);
456 
457   return rv;
458 }
459 
460 
461 
462 
463 
464 /**************************************************************************
465   void decomprle(source, destination, source length, buffer size)
466 
467   Decompress run-length encoded data from source to destination. Terminates
468   when source is decoded completely or destination buffer is full.
469 
470   The decruncher is as optimized as I could make it, without risking
471   safety in case of corrupt BODY chunks.
472 ***************************************************************************/
473 
474 
475 /*******************************************/
decomprle(sptr,dptr,slen,dlen)476 static void decomprle(sptr, dptr, slen, dlen)
477      byte *sptr, *dptr;
478      register long slen, dlen;
479 {
480   register byte codeByte, dataByte;
481 
482   while ((slen > 0) && (dlen > 0)) {
483 
484     /* read control byte */
485     codeByte = *sptr++;
486 
487     if (codeByte < 0x80) {
488       codeByte++;
489       if ((slen > (long) codeByte) && (dlen >= (long) codeByte)) {
490         slen -= codeByte + 1;
491         dlen -= codeByte;
492         while (codeByte > 0) {
493           *dptr++ = *sptr++;
494           codeByte--;
495         }
496       }
497       else slen = 0;
498     }
499 
500     else if (codeByte > 0x80) {
501       codeByte = 0x81 - (codeByte & 0x7f);
502       if ((slen > (long) 0) && (dlen >= (long) codeByte)) {
503 	dataByte = *sptr++;
504 	slen -= 2;
505 	dlen -= codeByte;
506 	while (codeByte > 0) {
507 	  *dptr++ = dataByte;
508 	  codeByte--;
509 	}
510       }
511       else slen = 0;
512     }
513   }
514 }
515 
516 
517 
518 /*******************************************/
iff_getword(ptr)519 static unsigned int iff_getword(ptr)
520      byte *ptr;
521 {
522   register unsigned int v;
523 
524   v = *ptr++;
525   v = (v << 8) + *ptr;
526 
527   return v;
528 }
529 
530 
531 /*******************************************/
iff_getlong(ptr)532 static unsigned long iff_getlong(ptr)
533      byte *ptr;
534 {
535   register unsigned long l;
536 
537   l = *ptr++;
538   l = (l << 8) + *ptr++;
539   l = (l << 8) + *ptr++;
540   l = (l << 8) + *ptr;
541 
542   return l;
543 }
544 
545 
546 /*******************************************/
iffError(fname,st)547 static int iffError(fname, st)
548      const char *fname, *st;
549 {
550   SetISTR(ISTR_WARNING,"%s:  %s", fname, st);
551   return 0;
552 }
553