/*  :ts=8 bk=0
 *
 * iff.c:	A cheesy IFF ILBM reader.
 *
 * Leo L. Schwab			8705.11
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/view.h>
#include <stdio.h>
#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);
}
