/* * xviff.c - load routine for IFF ILBM format pictures * * LoadIFF(fname, pinfo) - loads an IFF file * * * This loader module for xv will load most types of IFF ILBM pictures. * Apart from any 'normal' ILBM format, this loader understands * HAM (Hold-And-Modify), HAM8 (extended HAM), EHB (Extra Half Brite) * and 24-Bit. * Multipalette ILBMs are not supported yet, but I'll be working on that! * * * History: * 30-May-93: Project started * ILBMs can now be loaded from xv * EHB support added * HAM support added * 01-May-93: Started HAM8 support * 04-Jun-93: HAM8 finally works! =:) * 24 Bit support works! */ /* Copyright Notice * ================ * Copyright 1993 by Thomas Meyer * * Author: * Thomas Meyer * i03a@alf.zfn.uni-bremen.de * * Permission is hereby granted to use this code only as a part of * the xv distribution by John Bradley. */ #include "xv.h" static long filesize; static int readID PARM((FILE *, char *)); static int iffError PARM((char *, char *)); static void decomprle PARM((byte *, byte *, long, long)); static unsigned int iff_getword PARM((byte *)); static unsigned long iff_getlong PARM((byte *)); static char *bname; /* Define internal ILBM types */ #define ILBM_NORMAL 0 #define ILBM_EHB 1 #define ILBM_HAM 2 #define ILBM_HAM8 3 #define ILBM_24BIT 4 /*******************************************/ int LoadIFF(fname, pinfo) char *fname; PICINFO *pinfo; /*******************************************/ { /* returns '1' on success */ register byte bitmsk, rval, gval, bval; register long col, colbit; FILE *fp; int rv; int BMHDok, CMAPok, CAMGok, BODYok; int bmhd_width, bmhd_height, bmhd_bitplanes, bmhd_transcol; int i, j, k, lineskip, colors, fmt; byte bmhd_masking, bmhd_compression; long chunkLen, camg_viewmode, decomp_bufsize; byte *databuf, *dataptr, *cmapptr, *picptr, *pic, *bodyptr; byte *workptr, *workptr2, *workptr3, *decomp_mem; BMHDok = CAMGok = bmhd_width = bmhd_height = bmhd_bitplanes = colors = 0; bmhd_compression = 0; camg_viewmode = 0; bname = BaseName(fname); databuf = picptr = decomp_mem = NULL; pinfo->pic = (byte *) NULL; pinfo->comment = (char *) NULL; /* open the file */ fp = xv_fopen(fname,"r"); if (!fp) return (iffError(bname, "cannot open file")); /* compute file length */ fseek(fp, 0L, 2); filesize = ftell(fp); fseek(fp, 0L, 0); /* allocate memory for complete file */ if((databuf = (byte *) malloc((size_t) filesize)) == NULL) { fclose(fp); FatalError("xviff: cannot malloc file buffer"); } fread(databuf, (size_t) filesize, (size_t) 1, fp); fclose(fp); /* initialize work pointer. used to trace the buffer for IFF chunks */ dataptr = databuf; /* check if we really got an IFF file */ if(strncmp((char *) dataptr, "FORM", (size_t) 4) != 0) { free(databuf); return(iffError(bname, "not an IFF file")); } dataptr = dataptr + 8; /* skip ID and length of FORM */ /* check if the IFF file is an ILBM (picture) file */ if(strncmp((char *) dataptr, "ILBM", (size_t) 4) != 0) { free(databuf); return(iffError(bname, "not an ILBM file")); } if (DEBUG) fprintf(stderr,"IFF ILBM file recognized\n"); dataptr = dataptr + 4; /* skip ID */ /* initialize return value to -1. used to indicate when we are finished with decoding the picture (happens when BODY chunk was found */ rv = -1; /* main decoding loop. searches IFF chunks and handles them. terminates when BODY chunk was found or dataptr ran over end of file */ while ((rv<0) && (dataptr < (databuf + filesize))) { chunkLen = (iff_getlong(dataptr + 4) + 1) & 0xfffffffe; /* make even */ if (strncmp((char *) dataptr, "BMHD", (size_t) 4)==0) { /* BMHD chunk? */ bmhd_width = iff_getword(dataptr + 8); /* width of picture */ bmhd_height = iff_getword(dataptr + 8 + 2); /* height of picture */ bmhd_bitplanes = *(dataptr + 8 + 8); /* # of bitplanes */ bmhd_masking = *(dataptr + 8 + 9); bmhd_compression = *(dataptr + 8 + 10); /* get compression */ bmhd_transcol = iff_getword(dataptr + 8 + 12); BMHDok = 1; /* got BMHD */ dataptr += 8 + chunkLen; /* to next chunk */ } else if (strncmp((char *) dataptr, "CMAP", (size_t) 4)==0) { /* CMAP ? */ cmapptr = dataptr + 8; colors = chunkLen / 3; /* calc no of colors */ /* copy colors to color map */ for (i=0; i < colors; i++) { pinfo->r[i] = *cmapptr++; pinfo->g[i] = *cmapptr++; pinfo->b[i] = *cmapptr++; } CMAPok = 1; /* got CMAP */ dataptr += 8 + chunkLen; /* to next chunk */ } else if (strncmp((char *) dataptr, "CAMG", (size_t) 4)==0) { /* CAMG ? */ camg_viewmode = iff_getlong(dataptr + 8); /* get viewmodes */ CAMGok = 1; /* got CAMG */ dataptr += 8 + chunkLen; /* to next chunk */ } else if (strncmp((char *) dataptr, "BODY", (size_t) 4)==0) { /* BODY ? */ bodyptr = dataptr + 8; /* -> BODY data */ if (BMHDok) { /* BMHD found? */ /* if BODY is compressed, allocate buffer for decrunched BODY and decompress it (run length encoding) */ if (bmhd_compression == 1) { /* calc size of decrunch buffer - (size of the actual picture decompressed in interleaved Amiga bitplane format) */ decomp_bufsize = (((bmhd_width + 15) >> 4) << 1) * bmhd_height * bmhd_bitplanes; if ((decomp_mem = (byte *)malloc((size_t) decomp_bufsize)) != NULL) { decomprle(dataptr + 8, decomp_mem, chunkLen, decomp_bufsize); bodyptr = decomp_mem; /* -> uncompressed BODY */ free(databuf); /* free old data buffer */ databuf = NULL; } else { free(databuf); FatalError("xviff: cannot malloc() decrunch buffer"); } } /* the following determines the type of the ILBM file. it's either NORMAL, EHB, HAM, HAM8 or 24BIT */ fmt = ILBM_NORMAL; /* assume normal ILBM */ if (bmhd_bitplanes == 24) fmt = ILBM_24BIT; else if (bmhd_bitplanes == 8) { if (CAMGok && (camg_viewmode & 0x800)) fmt = ILBM_HAM8; } else if ((bmhd_bitplanes > 5) && CAMGok) { if (camg_viewmode & 0x80) fmt = ILBM_EHB; else if (camg_viewmode & 0x800) fmt = ILBM_HAM; } if (DEBUG) { fprintf(stderr, "LoadIFF: %s %dx%d, planes=%d (%d cols), comp=%d\n", (fmt==ILBM_NORMAL) ? "Normal ILBM" : (fmt==ILBM_HAM) ? "HAM ILBM" : (fmt==ILBM_HAM8) ? "HAM8 ILBM" : (fmt==ILBM_EHB) ? "EHB ILBM" : (fmt==ILBM_24BIT) ? "24BIT ILBM" : "unknown ILBM", bmhd_width, bmhd_height, bmhd_bitplanes, 1<r[i] = (pinfo->r[i] >> 4) * 17; pinfo->g[i] = (pinfo->g[i] >> 4) * 17; pinfo->b[i] = (pinfo->b[i] >> 4) * 17; } } if ((fmt == ILBM_HAM) || (fmt == ILBM_HAM8) || (fmt == ILBM_24BIT)) { if ((picptr=(byte *) malloc((size_t)bmhd_width*bmhd_height*3)) ==NULL) { if (databuf) free(databuf); if (decomp_mem) free(decomp_mem); return (iffError(bname, "xviff: no memory for decoded picture")); } else { pic = picptr; workptr = bodyptr; lineskip = ((bmhd_width + 15) >> 4) << 1; for (i=0; ir[0]; gval = pinfo->g[0]; bval = pinfo->b[0]; for (j=0; jr[col & 0x0f]; gval = pinfo->g[col & 0x0f]; bval = pinfo->b[col & 0x0f]; break; case 0x10: bval = (col & 0x0f) * 17; break; case 0x20: rval = (col & 0x0f) * 17; break; case 0x30: gval = (col & 0x0f) * 17; } } else if (fmt == ILBM_HAM8) { switch(col & 0xc0) { case 0x00: rval = pinfo->r[col & 0x3f]; gval = pinfo->g[col & 0x3f]; bval = pinfo->b[col & 0x3f]; break; case 0x40: bval = (bval & 3) | ((col & 0x3f) << 2); break; case 0x80: rval = (rval & 3) | ((col & 0x3f) << 2); break; case 0xc0: gval = (rval & 3) | ((col & 0x3f) << 2); } } else { rval = col & 0xff; gval = (col >> 8) & 0xff; bval = (col >> 16) & 0xff; } *pic++ = rval; *pic++ = gval; *pic++ = bval; bitmsk = bitmsk >> 1; if (bitmsk == 0) { bitmsk = 0x80; workptr2++; } } workptr += lineskip * bmhd_bitplanes; } pinfo->type = PIC24; } } /* HAM, HAM8, or 24BIT */ else if ((fmt == ILBM_NORMAL) || (fmt == ILBM_EHB)) { if ((picptr = (byte *) malloc((size_t) bmhd_width * bmhd_height)) == NULL) { if (databuf) free(databuf); if (decomp_mem) free(decomp_mem); return (iffError(bname, "xviff: no memory for decoded picture")); } else if (fmt == ILBM_EHB) { if (DEBUG) fprintf(stderr,"Doubling CMAP for EHB mode\n"); for (i=0; i<32; i++) { pinfo->r[i + colors] = pinfo->r[i] >> 1; pinfo->g[i + colors] = pinfo->g[i] >> 1; pinfo->b[i + colors] = pinfo->b[i] >> 1; } } pic = picptr; /* ptr to chunky buffer */ workptr = bodyptr; /* ptr to uncmp'd pic, planar format */ lineskip = ((bmhd_width + 15) >> 4) << 1; /* # of bytes/line */ for (i=0; i> 1; /* shift mask to next bit */ if (bitmsk == 0) { /* if mask is zero */ bitmsk = 0x80; /* reset mask */ workptr2++; /* mv ptr to next byte */ } } /* for j ... */ workptr += lineskip * bmhd_bitplanes; /* to next line */ } /* for i ... */ pinfo->type = PIC8; } /* if NORMAL or EHB */ /* fill up PICINFO */ pinfo->pic = picptr; pinfo->w = bmhd_width; pinfo->h = bmhd_height; pinfo->normw = pinfo->w; pinfo->normh = pinfo->h; pinfo->colType = F_FULLCOLOR; pinfo->frmType = -1; sprintf(pinfo->fullInfo, "%s (%ld bytes)", (fmt==ILBM_NORMAL) ? "IFF ILBM" : (fmt==ILBM_HAM) ? "HAM ILBM" : (fmt==ILBM_HAM8) ? "HAM8 ILBM" : (fmt==ILBM_EHB) ? "EHB ILBM" : (fmt==ILBM_24BIT) ? "24BIT ILBM" : "unknown ILBM", filesize); sprintf(pinfo->shrtInfo, "%dx%d ILBM.", bmhd_width, bmhd_height); rv = 1; } /* if BMHDok */ else rv = 0; /* didn't get BMHD header */ } else { if (DEBUG) fprintf(stderr,"Skipping unknown chunk '%c%c%c%c'\n", *dataptr, *(dataptr+1), *(dataptr+2), *(dataptr+3)); dataptr = dataptr + 8 + chunkLen; /* skip unknown chunk */ } } if (decomp_mem) free(decomp_mem); if (databuf) free(databuf); if (rv != 1) free(picptr); return rv; } /************************************************************************** void decomprle(source, destination, source length, buffer size) Decompress run-length encoded data from source to destination. Terminates when source is decoded completely or destination buffer is full. The decruncher is as optimized as I could make it, without risking safety in case of corrupt BODY chunks. ***************************************************************************/ /*******************************************/ static void decomprle(sptr, dptr, slen, dlen) byte *sptr, *dptr; register long slen, dlen; { register byte codeByte, dataByte; while ((slen > 0) && (dlen > 0)) { /* read control byte */ codeByte = *sptr++; if (codeByte < 0x80) { codeByte++; if ((slen > (long) codeByte) && (dlen >= (long) codeByte)) { slen -= codeByte + 1; dlen -= codeByte; while (codeByte > 0) { *dptr++ = *sptr++; codeByte--; } } else slen = 0; } else if (codeByte > 0x80) { codeByte = 0x81 - (codeByte & 0x7f); if ((slen > (long) 0) && (dlen >= (long) codeByte)) { dataByte = *sptr++; slen -= 2; dlen -= codeByte; while (codeByte > 0) { *dptr++ = dataByte; codeByte--; } } else slen = 0; } } } /*******************************************/ static unsigned int iff_getword(ptr) byte *ptr; { register unsigned int v; v = *ptr++; v = (v << 8) + *ptr; return v; } /*******************************************/ static unsigned long iff_getlong(ptr) byte *ptr; { register unsigned long l; l = *ptr++; l = (l << 8) + *ptr++; l = (l << 8) + *ptr++; l = (l << 8) + *ptr; return l; } /*******************************************/ static int iffError(fname, st) char *fname, *st; { SetISTR(ISTR_WARNING,"%s: %s", fname, st); return 0; }