/*----------------------------------------------------------------------*
 * ILBMR.C  Support routines for reading ILBM files.           11/27/85
 * (IFF is Interchange Format File.)
 *
 * By Jerry Morrison and Steve Shaw, Electronic Arts.
 * This software is in the public domain.
 *
 * This version for the Commodore-Amiga computer.
 *----------------------------------------------------------------------*/
#include <exec/memory.h>
#include <graphics/rastport.h>
#include <iff/packer.h>
#include <iff/ilbm.h>
#include <iff/readpict.h>

#include "picture.h"
#include <mymacros.h>

/* ---------- GetCMAP ------------------------------------------------*/
/* pNColorRegs is passed in as a pointer to the number of ColorRegisters
 * caller has space to hold.  GetCMAP sets to the number actually read.*/
IFFP GetCMAP(ilbmContext, colorMap, pNColorRegs)   
	GroupContext	*ilbmContext;
	WORD		*colorMap;
	WORD		*pNColorRegs;
{
	register int	nColorRegs;   
	register IFFP	iffp;
	ColorRegister	colorReg;

	nColorRegs = ilbmContext->ckHdr.ckSize / sizeofColorRegister;
	if (*pNColorRegs < nColorRegs) {
		nColorRegs = *pNColorRegs;
	}
	*pNColorRegs = nColorRegs;	/* Set to the number actually there.*/

	for ( ;  nColorRegs > 0;  --nColorRegs)  {
	iffp = IFFReadBytes(ilbmContext, (BYTE *)&colorReg,sizeofColorRegister);
	CheckIFFP();
	*colorMap++ = ( ( colorReg.red   >> 4 ) << 8 ) |
		      ( ( colorReg.green >> 4 ) << 4 ) |
		      ( ( colorReg.blue  >> 4 )      );
	}
	return(IFF_OKAY);
}

/*---------- GetBODY ---------------------------------------------------*/
/* NOTE: This implementation could be a LOT faster if it used more of the
 * supplied buffer. It would make far fewer calls to IFFReadBytes (and
 * therefore to DOS Read) and to movemem. */
IFFP GetBODY(context, ilbmframe, mask, buffer, bufsize, srcbm)
	GroupContext	*context;
	ILBMFrame	*ilbmframe;
	BYTE		*mask;
	BYTE		*buffer;
	LONG		bufsize;
	struct BitMap	*srcbm;
{
	register IFFP	iffp;
	BitMapHeader	*bmHdr      = &ilbmframe->bmHdr;
	UBYTE		srcPlaneCnt = bmHdr->nPlanes;
	WORD		srcRowBytes = RowBytes(bmHdr->w);
	LONG		bufRowBytes = MaxPackedSize(srcRowBytes);
	int		nRows       = bmHdr->h;
	Compression	compression = bmHdr->compression;
	register int	iPlane, iRow, nEmpty;
	register WORD	nFilled;
	BYTE		*buf, *nullDest, *nullBuf, **pDest;
	BYTE		*planes[MaxSrcPlanes]; /* array of ptrs to planes & mask */
	long		gtemp;
	NewImage	*ni         = ilbmframe->ni;
	struct BitMap	*bitmap	    = (struct BitMap *)ni->ImagePtr;
	struct RastPort *rp         = (struct RastPort *)ni->ImagePtr;
	BYTE		*gDest;

/**********************
 **
 ** Added for SimGen.
 **
 *********************/
	AboutToLoadBody (ilbmframe);

	if (compression > cmpByteRun1)
		return(CLIENT_ERROR);

   /*
    *  if mode = CREATEPIC
    *  Allocate and Initialize a bitmap for this picture
    */

	if (ni->Flags & (NI_CREATEPIC | NI_CREATESHAPE)) {
		int  i;
		int  w;
		int  h;

		if (ni->Flags & NI_CLIPIMAGE) {
			w = ni->Width;
			h = ni->Height;
		} else {
			w = bmHdr->w;
			h = bmHdr->h;
		}

		ni->Flags   |= NI_USEBITMAP;
		bitmap       = &ilbmframe->pic->BitMap;
		ni->ImagePtr = (APTR)bitmap;

		InitBitMap (bitmap, bmHdr->nPlanes, w, h);
		for (i=0; i<bmHdr->nPlanes; i++) {
			if (!( bitmap->Planes[i] = 
				(*ilbmframe->allocmem)(RASSIZE(w, h), MEMF_CHIP|MEMF_CLEAR))) {
				return (CLIENT_ERROR);
        		}
		}
	} else {
		if (!(ni->Flags & NI_USEBITMAP)) {
			bitmap = rp->BitMap;
		}
	}

	/* Complain if client asked for a conversion GetBODY doesn't handle.*/

	if (!ni->Flags & NI_CLIPIMAGE) {
		if (srcPlaneCnt > MaxSrcPlanes ||
		    srcRowBytes > bitmap->BytesPerRow ||
		    nRows       > bitmap->Rows)
			return (CLIENT_ERROR);
	}

	/* Initialize array "planes" with bitmap ptrs; NULL in empty slots.*/
	for (iPlane = 0; iPlane < srcbm->Depth; iPlane++)
		planes[iPlane] = (BYTE *)srcbm->Planes[iPlane];
	for ( ;  iPlane < MaxSrcPlanes;  iPlane++)
		planes[iPlane] = NULL;

	/* Copy any mask plane ptr into corresponding "planes" slot.*/
	if (bmHdr->masking == mskHasMask) {
		if (mask != NULL)
			planes[srcPlaneCnt] = mask;
			/* If there are more srcPlanes than
			 * dstPlanes, there will be NULL
			 * plane-pointers before this. */
		else
			planes[srcPlaneCnt] = NULL;
			/* In case more dstPlanes than src.*/
		srcPlaneCnt += 1;  /* Include mask plane in count.*/
	}

	/* Setup a sink for dummy destination of rows from unwanted planes.*/
	nullDest = buffer;
	buffer  += srcRowBytes;
	bufsize -= srcRowBytes;

	/* Read the BODY contents into client's bitmap.
	 * De-interleave planes and decompress rows.
	 * MODIFIES: Last iteration modifies bufsize.*/

	buf = buffer + bufsize;  /* Buffer is currently empty.*/
	for (iRow = 0; iRow < nRows; iRow++)  {
		for (iPlane = 0; iPlane < srcPlaneCnt; iPlane++)  {

			gDest = (BYTE *)srcbm->Planes[iPlane];
			pDest = &gDest;

			/* Establish a sink for any unwanted plane.*/
			if (*pDest == NULL) {
				nullBuf = nullDest;
				pDest   = &nullBuf;
			}

			/* Read in at least enough bytes to
			   uncompress next row. */

			nEmpty  = buf - buffer;	  /* size of empty part of buffer.*/
			nFilled = bufsize - nEmpty;	  /* this part has data.*/
			if (nFilled < bufRowBytes) {
				/* Need to read more.*/

				/* Move the existing data to the front of the buffer.*/
				/* Now covers range buffer[0]..buffer[nFilled-1].*/
				movmem(buf, buffer, nFilled);  /* Could be moving 0 bytes.*/

				if (nEmpty > ChunkMoreBytes(context)) {
					/* There aren't enough bytes left to fill the buffer.*/
					nEmpty = ChunkMoreBytes(context);
					bufsize = nFilled + nEmpty;  /* heh-heh */
				}

				/* Append new data to the existing data.*/
				iffp = IFFReadBytes(context, &buffer[nFilled], nEmpty);
				CheckIFFP();
	
				buf     = buffer;
				nFilled = bufsize;
				nEmpty  = 0;
			}

			/* Copy uncompressed row to destination plane.*/
			if (compression == cmpNone) {
				if (nFilled < srcRowBytes)  return(BAD_FORM);
				movmem(buf, *pDest, srcRowBytes);
				buf    += srcRowBytes;
			} else {
				/* Decompress row to destination plane.*/

				if ( UnPackRow(&buf, pDest, nFilled,  srcRowBytes) )
		                    /*  pSource, pDest, srcBytes, dstBytes  */
			               return(BAD_FORM);
			}
		}
		if (ni->Flags & NI_USEBITMAP) {
			if (ni->Flags & NI_CLIPIMAGE) {
				if (iRow >= ni->FTop &&
				    iRow < ni->FTop + ni->Height) {
					BltBitMap (srcbm,
						ni->FLeft,
						0,
						bitmap,
						ni->Left,
						ni->Top + iRow - ni->FTop,
						min(ni->Width, bmHdr->w), 1,
						0xC0, 0xFF, NULL);
				}
			} else {
				BltBitMap (srcbm, 0, 0,
					   bitmap, 0, iRow, bmHdr->w, 1,
					   0xC0, 0xFF, NULL);
			}
		} else {
			if (ni->Flags & NI_CLIPIMAGE) {
				if (iRow >= ni->FTop &&
				    iRow < ni->FTop + ni->Height) {
					BltBitMapRastPort (srcbm,
						ni->FLeft,
						0,
						rp,
						ni->Left,
						ni->Top + iRow - ni->FTop,
						ni->Width, 1,
						0xC0, 0xFF, NULL);
				}
			} else {
				BltBitMapRastPort (srcbm, 0, 0,
						rp, 0, iRow, bmHdr->w, 1,
						0xC0, 0xFF, NULL);
			}
		}
	}
	return(IFF_OKAY);
}

