/* :ts=8 bk=0 * * iff.c: A cheesy IFF ILBM reader. * * Leo L. Schwab 8705.11 */ #include #include #include #include #include "myiff.h" static struct ViewPort *viewport; /* Working viewport */ /* * Okay, here's how this works. This routine assumes that the file * descriptor points to just past the FORM type and size fields in the file. * It returns a pointer to a ViewPort structure with all necessary * substructures, suitable for MakeVPort()ing. It assumes someone else will * know how to deallocate all said structures and resources. Fortunately, * there's enough information in ViewPorts to be able to intelligently free * anything they may use. */ struct ViewPort * readform (fd, formsize) FILE *fd; /* File descriptor */ long formsize; /* Size of this FORM */ { struct BitMapHeader bmhd; struct ChunkHeader ch; long subtype; register int i, n; char gotheader = 0, gotcmap = 0, gotcamg = 0; void *tmp; if (!getsubtype (fd, &subtype)) return (NULL); formsize -= sizeof (subtype); if (subtype != ILBM) { puts ("FORM not an ILBM, skipping..."); skipchunk (fd, formsize - sizeof (subtype)); return (NULL); } if (!(viewport = AllocMem ((long) sizeof (*viewport), MEMF_CLEAR))) ackphft ("ViewPort allocation failed."); InitVPort (viewport); while (formsize > 0) { if (!getchunkheader (fd, &ch)) ackphft ("Malformed IFF FORM."); formsize -= sizeof (ch); switch (ch.TYPE) { case BMHD: { register struct BitMap *bm; register struct RasInfo *ri; fread (&bmhd, (int) ch.chunksize, 1, fd); if (!(bm = AllocMem ((long) sizeof (*bm), MEMF_CLEAR))) ackphft ("BitMap allocation failed."); InitBitMap (bm, (long) bmhd.nplanes, (long) bmhd.w, (long) bmhd.h); if (!(ri = AllocMem ((long) sizeof (*ri), MEMF_CLEAR))) ackphft ("RasInfo allocation failed."); ri -> BitMap = bm; ri -> RxOffset = ri -> RyOffset = NULL; ri -> Next = NULL; viewport -> DWidth = bmhd.w; viewport -> DHeight = bmhd.h; viewport -> RasInfo = ri; gotheader = 1; break; } case CMAP: { register UBYTE *ctable; register UWORD *cmap; if (!(ctable = AllocMem (ch.chunksize, NULL))) ackphft ("EA colortable alloc failed."); fread (ctable, (int) ch.chunksize, 1, fd); if (!(cmap = AllocMem (ch.chunksize * 2 / 3, NULL))) ackphft ("Colormap alloc failed."); for (i = n = 0; n < ch.chunksize; i++, n+=3) cmap[i] = ((ctable[n] >> 4) << 8) + ((ctable[n+1] >> 4) << 4) + ( ctable[n+2] >> 4); if ((1 << bmhd.nplanes) != i) puts ("Warning: Colormap not sized to nplanes, hope it's HAM."); viewport -> ColorMap = GetColorMap ((long) i); LoadRGB4 (viewport, cmap, (long) i); FreeMem (cmap, ch.chunksize * 2 / 3); FreeMem (ctable, ch.chunksize); gotcmap = 1; break; } case CAMG: /* Use subtype as a temporary buffer */ fread (&subtype, (int) ch.chunksize, 1, fd); viewport -> Modes = (UWORD) (subtype & 0xffff); gotcamg = 1; break; case BODY: if (!gotheader || !gotcmap) ackphft ("BODY before BMHD or CMAP."); loadbitmap (fd, viewport, &bmhd); break; case CRNG: case GRAB: case DEST: case SPRT: case DPPV: /* Anyone know what this one is for? */ skipchunk (fd, ch.chunksize); break; default: printf ("Unrecognized chunk: 0x%lx\n", ch.TYPE); skipchunk (fd, ch.chunksize); } formsize -= ch.chunksize; if (ch.chunksize & 1) { /* Odd length chunk */ formsize --; fseek (fd, 1L, 1); } } /* Post-processing in case of lack of CAMG chunk */ if (!gotcamg) { if (bmhd.w > 370) /* Arbitrary limit */ viewport -> Modes |= HIRES; if (bmhd.h > 256) viewport -> Modes |= LACE; } tmp = viewport; viewport = NULL; return (tmp); } loadbitmap (fd, vp, header) FILE *fd; struct ViewPort *vp; struct BitMapHeader *header; { register struct BitMap *bm; register int i, n; int plane_offset = 0; bm = vp -> RasInfo -> BitMap; if (header->Compression != cmpNone && header->Compression != cmpByteRun1) ackphft ("Unrecognized compression technique."); for (i=0; i < bm->Depth; i++) if (!(bm -> Planes[i] = AllocRaster ((long) vp -> DWidth, (long) vp -> DHeight))) ackphft ("Bitplane allocation failed."); for (i=0; i < bm->Rows; i++) { for (n=0; n < bm->Depth; n++) { /*- - - - - - - - - - -*/ if (!header->Compression) { /* No compression */ if (!fread (bm -> Planes[n] + plane_offset, bm -> BytesPerRow, 1, fd)) ackphft ("Failure in BODY read."); } else { int so_far; register UBYTE *dest = bm -> Planes[n] + plane_offset; char len; /* * Note: All file I/O after this point is assumed to be sucessful. * This is clearly a poor assumption, but it saves on typing. * And besides, putting the checking in is simple :-) :-). */ so_far = bm -> BytesPerRow; while (so_far > 0) { if ((len = getc (fd)) >= 0) { /* Literal byte copy */ so_far -= ++len; fread (dest, len, 1, fd); dest += len; } else if ((UBYTE) len == 128) /* NOP */ ; else if (len < 0) { /* Replication count */ UBYTE byte; len = -len + 1; so_far -= len; byte = getc (fd); while (--len >= 0) *dest++ = byte; } } if (so_far) ackphft ("Compression quite screwed up."); } /*- - - - - - - - - - -*/ } plane_offset += bm -> BytesPerRow; } } getchunkheader (fd, header) FILE *fd; /* File descriptor (that's what 'fd' stands for) */ struct ChunkHeader *header; { return (fread (header, sizeof (*header), 1, fd)); } getsubtype (fd, type) FILE *fd; long *type; { /* !! NOT PORTABLE !! */ return (fread (type, sizeof (*type), 1, fd)); } skipchunk (fd, size) FILE *fd; long size; { fseek (fd, size, 1); } /* * This function assumes the existence of the global variable viewport, which * is a pointer to a working ViewPort structure. This is to allow graceful * cleanup of allocated resources in case of an exceptional failure. */ freepict () { register struct BitMap *bm; register int i; if (viewport) { if (viewport -> RasInfo) { /*- - - - - - - - - - -*/ if (bm = viewport -> RasInfo -> BitMap) { for (i=0; i < bm->Depth; i++) if (bm -> Planes[i]) FreeRaster (bm -> Planes[i], (long) viewport -> DWidth, (long) viewport -> DHeight); FreeMem (bm, (long) sizeof (*bm)); } /*- - - - - - - - - - -*/ FreeMem (viewport -> RasInfo, (long) sizeof (struct RasInfo)); } if (viewport -> ColorMap) FreeColorMap (viewport -> ColorMap); FreeVPortCopLists (viewport); FreeMem (viewport, (long) sizeof (*viewport)); viewport = NULL; } } closeviewport (vp) struct ViewPort *vp; { viewport = vp; freepict (); } /* * Premature termination routine. */ ackphft (str) char *str; { freepict (); die (str); }