
/* 
	
	Show ILBM module 

	(c) Copyright 1995-2001 Grzegorz Calkowski

	This file is part of SysPic.

	SysPic  is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2, or (at your option)
	any later version.

	Bison is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Bison; see the file COPYING.  If not, write to
	the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

*/


#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>

#include <graphics/modeid.h>
#include <graphics/videocontrol.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/graphics.h>

#include "ilbm.h"
#include "ilbminfo.h"

#ifndef MAX
#define	MAX(a,b)	((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define	MIN(a,b)	((a) < (b) ? (a) : (b))
#endif

#define MAXSRCPLANES 9
#define MaxPackedSize(rowSize)  ( (rowSize) + ( ((rowSize)+127) >> 7 ) )

                               /* Prototypes */


static LONG readbmhd(struct ILBMInfo *ilbm, ULONG len);
static LONG readcmap(struct ILBMInfo *ilbm, ULONG len);
static LONG readcamg(struct ILBMInfo *ilbm, ULONG len);
static LONG readbody(struct ILBMInfo *ilbm, ULONG len);
LONG displayit(struct ILBMInfo *ilbm);
void closedisplay(struct ILBMInfo *ilbm);

LONG openscreen(struct ILBMInfo *ilbm, WORD wide, WORD high, WORD deep, ULONG modeid, struct Screen **scr);
ULONG modefallback(struct ILBMInfo *ilbm, ULONG oldmode, WORD wide, WORD high, WORD deep);
void clipit(WORD wide, WORD high,
				struct Rectangle *spos, struct Rectangle *dclip,
				struct Rectangle *txto, struct Rectangle *stdo,
				struct Rectangle *maxo, BOOL nohcenter, BOOL novcenter);
BOOL VideoControlTags(struct ColorMap *cm, ULONG tags, ...);

LONG processbody(BYTE *body, LONG len, struct BitMap *bitmap, BitMapHeader *bmhd);
void setcolors(struct ILBMInfo *ilbm);


/* decomp.s */

BOOL __asm unpackbody(register __a0 BYTE *, register __a1 BYTE *, register __a2 BYTE **,
							 register __d0	LONG, register __d4 WORD, register __d5 WORD,
							 register __d6 WORD, register __d7 WORD);

BOOL __asm copybody(register __a0 BYTE *, register __a1 BYTE **,
							 register __d0	LONG, register __d4 WORD, register __d5 WORD,
							 register __d6 WORD, register __d7 WORD);

/* support.s */

void __asm scale32(register __a0 ULONG *, register __d0 ULONG);

/*****************************************************************************/


extern struct GfxBase *GfxBase;
extern struct IntuitionBase *IntuitionBase;

extern UBYTE keepptr, bblank, onlymon, pubflag;


struct TextAttr SafeFont = {(UBYTE *)"topaz.font", 8, 0, 0};
ULONG	fakepoint[2];

/* default new window if none supplied in ilbm->nw */
struct NewWindow defnw = { 0, 0,												/* LeftEdge and TopEdge */
   								0, 0,												/* Width and Height */
									(UBYTE)-1, (UBYTE)-1,						/* DetailPen and BlockPen */
									IDCMP_MOUSEBUTTONS | IDCMP_VANILLAKEY, /* IDCMP Flags with Flags below */
   								WFLG_BACKDROP | WFLG_BORDERLESS |
									WFLG_SIMPLE_REFRESH | WFLG_NOCAREREFRESH |
								   WFLG_RMBTRAP | WFLG_ACTIVATE,
								   NULL, NULL,										/* Gadget and Image pointers */
								   NULL,												/* Title string */
								   NULL,												/* Screen ptr null till opened */
								   NULL,												/* BitMap pointer */
								   50, 20,                                /* MinWidth and MinHeight */
								   0 , 0,											/* MaxWidth and MaxHeight */
								   CUSTOMSCREEN									/* Type of window */
								 };


/*****************************************************************************/

LONG showilbm(struct ILBMInfo *ilbm, STRPTR filename)
{
	LONG	 error;
	ULONG	 buf[3], len;

	if (!(ilbm->fh = Open(filename, MODE_OLDFILE)))
		return IFFERR_NOFILE;

	/* read header (first 3 longs) */
	if (FRead(ilbm->fh, buf, 4, 3) == 3)
	{
		/* must be equal to FORM????ILBM */
		if (buf[0] == ID_FORM && buf[2] == ID_ILBM)
		{
			len = buf[1] - 4;

			while (len)
			{
				/* buf[0] - id, buf[1] - len */
				if (FRead(ilbm->fh, buf, 4, 2) != 2)
				{
					error = IFFERR_IO;
					goto bailoff;
				}

				switch (buf[0])
				{
					case ID_BMHD:
						if (error = readbmhd(ilbm, buf[1]))
							goto bailoff;
						break;

					case ID_CMAP:
						if (error = readcmap(ilbm, buf[1]))
							goto bailoff;
						break;
						
					case ID_CAMG:
						if (error = readcamg(ilbm, buf[1]))
							goto bailoff;
						break;

					case ID_BODY:
						if (error = readbody(ilbm, buf[1]))
							goto bailoff;

						error = displayit(ilbm);
						goto bailoff;

					default:
						Seek(ilbm->fh, buf[1], OFFSET_CURRENT);
				}

				len -= buf[1];

				if (buf[1] & 1)
				{
					Seek(ilbm->fh, 1, OFFSET_CURRENT);
					len--;
				}
			}


		}
		else
			error = ILBMERR_NOTILBM;

	}		
	else
		error = IFFERR_IO;


	bailoff:

	if (ilbm->bodybuf)
	{	
		FreeMem(ilbm->bodybuf, ilbm->bodylen);
		ilbm->bodybuf = NULL;
		ilbm->bodylen = NULL;
	}

	if (error)
		unshowilbm(ilbm);

	if (ilbm->fh)
		Close(ilbm->fh);

	return error;
}


/*****************************************************************************/

void unshowilbm(struct ILBMInfo *ilbm)
{
	if (ilbm)
	{
		closedisplay(ilbm);

		if (ilbm->colorrecord)
		{
			FreeMem(ilbm->colorrecord, ilbm->crecsize);
			ilbm->colorrecord = NULL;
		}
	}
}


/*****************************************************************************/

static LONG readbmhd(struct ILBMInfo *ilbm, ULONG len)
{
	if (len != sizeof(BitMapHeader) || ilbm->bmhdread)
		return IFFERR_MANGLED;

	if (FRead(ilbm->fh, &ilbm->bmhd, len, 1) != 1)
		return IFFERR_IO;

	ilbm->bmhdread = TRUE;

	if (ilbm->bmhd.nPlanes == 24)
		return ILBMERR_24BIT;

	return 0; /* ok */
}

/*****************************************************************************/

static LONG readcmap(struct ILBMInfo *ilbm, ULONG len)
{
	UWORD	 ncolors, total, ncheck, i;
	ULONG	 ctabsize;
	UBYTE	 rb, gb, bb;
	BOOL	 allshifted;
	UBYTE	 *cmapbuf, *rgb;
	LONG	 error = 0; /* ok */

	if (ilbm->cmapread || !ilbm->bmhdread)
		return IFFERR_MANGLED;

	if (cmapbuf = AllocMem(len, MEMF_ANY))
	{
		if (FRead(ilbm->fh, cmapbuf, len, 1) == 1)
		{
			ncolors = len / 3;
			total = MAX(ncolors, 32);	/* at least 32 colortable */

			/* table size for LoadRGB32() */
			ctabsize = total * sizeof(Color32) + 4 * sizeof(WORD);

			if (ilbm->colorrecord = AllocMem(ctabsize, MEMF_CLEAR))
			{
				ilbm->crecsize = ctabsize; 
				ilbm->colortable32 = (Color32 *)(&ilbm->colorrecord[2]);
				ilbm->colorrecord[0] = total; /* For LoadRGB32 */
				ilbm->colorrecord[1] = 0;

				allshifted = TRUE; ncheck = ncolors; i = 0;
				rgb = cmapbuf;

				while (ncheck--)
				{
					ilbm->colortable32[i].r = rb = *rgb++;
					ilbm->colortable32[i].g = gb = *rgb++;
					ilbm->colortable32[i].b = bb = *rgb++;

					if ((rb & 0x0F) || (gb & 0x0F) || (bb & 0x0F))
						allshifted = FALSE;

					i++;
				}

				if	(!(ilbm->flags & ILBMF_CMAPOK) && !(ilbm->bmhd.flags & BMHDF_CMAPOK))
					/* If all nibbles appear shifted (4 bit), duplicate the nibbles */
					if (allshifted)
						for(i = 0; i < ncolors; i++)
						{
							ilbm->colortable32[i].r |= ilbm->colortable32[i].r >> 4;
							ilbm->colortable32[i].g |= ilbm->colortable32[i].g >> 4;
							ilbm->colortable32[i].b |= ilbm->colortable32[i].b >> 4;
						}

				scale32((ULONG *)ilbm->colortable32, ncolors);

				ilbm->cmapread = TRUE;					
			}
			else
				error = IFFERR_NOMEM;				
		}
		else
			error = IFFERR_IO;

		FreeMem(cmapbuf, len);
	}
	else
		error = IFFERR_NOMEM;

	return error;
}


/*****************************************************************************/

static LONG readcamg(struct ILBMInfo *ilbm, ULONG len)
{
	ULONG camg;

	if (len != 4 || ilbm->camgread)
		return IFFERR_MANGLED;

	if (FRead(ilbm->fh, &camg, 4, 1) != 1)
		return IFFERR_IO;

	 /* knock bad bits out of old-style 16-bit viewmode CAMGs */

	 if ( (!(camg & MONITOR_ID_MASK)) || ((camg & EXTENDED_MODE) && !(camg & 0xFFFF0000)))
		 camg &= (~(EXTENDED_MODE|BADFLAGS));

	 /* check for bogus CAMG like DPaintII brushes
	  * with junk in upper word and extended bit
	  * not set in lower word.
	  */

	if ((camg & 0xFFFF0000) && (!(camg & 0x00001000)))
		camg = 0;

	ilbm->camg = camg;
	ilbm->camgread = TRUE;

	return 0; /* ok */
}

/*****************************************************************************/

static LONG readbody(struct ILBMInfo *ilbm, ULONG len)
{
	LONG error = 0;

	if (ilbm->bmhdread && ilbm->cmapread)
	{
		if (ilbm->bodybuf = AllocMem(len, MEMF_ANY))
		{
			Flush(ilbm->fh);

			if (Read(ilbm->fh, ilbm->bodybuf, len) != len)
				error = IFFERR_IO;

			ilbm->bodylen = len;
		}
		else
			error = IFFERR_NOMEM;
	}
	else
		error = IFFERR_MANGLED;

	return error;
}

/*****************************************************************************/

LONG displayit(struct ILBMInfo *ilbm)
{
	BitMapHeader *bmhd = &ilbm->bmhd;
	UWORD	 wide, high, deep;	
	ULONG	 modeid;
	LONG	 error;
	struct Screen *scr = NULL;
	struct NewWindow *nw, nw_copy;

//	wide = (RowBytes(bmhd->w)) < (RowBytes(bmhd->pageWidth)) ? bmhd->w : bmhd->pageWidth;
//	high = MIN(bmhd->h, bmhd->pageHeight);
	wide = bmhd->w;
	high = bmhd->h;
	deep = bmhd->nPlanes;
	modeid = ilbm->camg;

	if (!modeid)
	{
		/* No CAMG (or bad CAMG) present; use computed modes. */
	
		if (wide >= 640) modeid = HIRES;
		if (high >= 400) modeid |= LACE;
		if (deep == 6)	modeid |= HAM;	/* we prefer HAM */
	}

	if (error = openscreen(ilbm, wide, high, deep, modeid, &scr))
		return error;

	ilbm->scr = scr;

//	if (ilbm->windef)
//		nw = ilbm->windef;
//	else
	{
		nw_copy = defnw;
		nw = &nw_copy;
	}

	nw->Screen	= scr;
	nw->Width	= wide;
	nw->Height	= high;

	/* if public we want smart refresh */
	if (pubflag)
		nw->Flags &= (~WFLG_REFRESHBITS);

	if (!(ilbm->win = OpenWindow(nw)))
	{
		closedisplay(ilbm);
		return ILBMERR_CANTOPENDISP;
	}

//	if (ilbm->win->Flags & WFLG_BACKDROP)
//	{
//		ShowTitle(ilbm->scr, FALSE);
//		ilbm->TBState = FALSE;
//	}

	if (!keepptr)
	{
		/* CLEAR POINTER */
		WaitTOF();
		SetPointer(ilbm->win, (UWORD *)fakepoint, 1, 1, 0, 0);
	}

	ilbm->vp  = &ilbm->scr->ViewPort;

	if (error = processbody(ilbm->bodybuf, ilbm->bodylen, ilbm->scr->RastPort.BitMap, &ilbm->bmhd))
	{
		closedisplay(ilbm);
		return error;
	}

//	setcolors(ilbm);

	return error;
}


void closedisplay(struct ILBMInfo *ilbm)
{
	struct Screen *scr = ilbm->scr;

	if (scr)
	{
		ScreenToBack(scr);

	  	if (ilbm->win)
		{
			CloseWindow(ilbm->win);
			ilbm->win = NULL;
		}

		if (scr->Flags & PUBLICSCREEN)
		{
			PubScreenStatus(scr, PSNF_PRIVATE);
			while (!CloseScreen(scr))
				Wait(1L << ilbm->pubsig);
		}
		else
			CloseScreen(scr);

		ilbm->scr = NULL;	
		ilbm->vp  = NULL;
	}
}

/*****************************************************************************/

/*
 * openscreen
 *
 */
 
LONG openscreen(struct ILBMInfo *ilbm, WORD wide, WORD high, WORD deep, ULONG mode, struct Screen **rscr)
{
	DisplayInfoHandle displayhandle;
	struct DimensionInfo dimensioninfo;
	struct DisplayInfo displayinfo;
	struct Rectangle spos, dclip, txto, stdo, maxo;  /* display rectangles */
	struct Screen *scr;
	LONG	 error;
	ULONG	 *tmptab, tmp;
	WORD	 tdeep, i;

	if (ilbm->flags & ILBMF_USERMODE)
		mode = ilbm->usermodeid;

	if (ilbm->flags & ILBMF_BESTFIT || onlymon)
		mode = modefallback(ilbm, mode, wide, high, deep);

	if (error = ModeNotAvailable(mode))
	{
		/* if not available, try fall back mode */
		mode = modefallback(ilbm, mode, wide, high, deep);
		if (error = ModeNotAvailable(mode))
			return ILBMERR_MODENOTAVAIL;
	}

	if (!error)	/* mode is available - how about depth ? */
		if (displayhandle = FindDisplayInfo(mode))
		{
			if (GetDisplayInfoData(displayhandle, (UBYTE *)&dimensioninfo,
															  sizeof(struct DimensionInfo), DTAG_DIMS, NULL))
				if (dimensioninfo.MaxDepth < deep)
					return ILBMERR_TOODEEP;

			if (GetDisplayInfoData(displayhandle, (UBYTE *)&displayinfo,
															  sizeof(struct DisplayInfo), DTAG_DISP, NULL))
			{
				tmp = displayinfo.PropertyFlags;
				if (tmp & DIPF_IS_HAM || tmp & DIPF_IS_EXTRAHALFBRITE)
					ilbm->nonstd = TRUE;
			}
		}
		else
			return ILBMERR_MODENOTAVAIL;

	if (!(QueryOverscan(mode, &txto, OSCAN_TEXT) &&
			QueryOverscan(mode, &stdo, OSCAN_STANDARD) &&
			QueryOverscan(mode, &maxo, OSCAN_MAX)))
		return ILBMERR_MODENOTAVAIL;
	
	clipit(wide, high, &spos, &dclip, &txto, &stdo, &maxo,
			 FALSE,	//	 ilbm->flags & ILBMF_NOCENTER ? TRUE : FALSE,
			 ilbm->flags & ILBMF_NOVCENTER ? TRUE : FALSE);

	tdeep = (!keepptr) ? MAX(deep, 5) : deep;

	if (tmptab = AllocVec((1<<tdeep)*12+6, MEMF_CLEAR))
		tmptab[0] = (((ULONG)(1<<tdeep))<<16+0);
	
	scr = (struct Screen *)OpenScreenTags(NULL,
												        pubflag ? SA_PubName : TAG_IGNORE, ilbm->pubname,
														  pubflag ? SA_PubSig : TAG_IGNORE, ilbm->pubsig,
														  SA_DisplayID,	mode,
														  SA_Type,			ilbm->stype,
														  SA_Behind,		TRUE,
														  SA_Top,			spos.MinY,
														  SA_Left,			spos.MinX,
														  SA_Width,			wide,
														  SA_Height,		high,
														  SA_Depth,			deep,
														  SA_DClip,			&dclip,
//														  SA_Font,			&SafeFont,
														  SA_SysFont,		1,
														  SA_ErrorCode,	&error,
														  SA_Draggable,	FALSE,
														  SA_Exclusive,	TRUE,
														  SA_Quiet,			TRUE,
														  SA_ShowTitle,	FALSE,
														  tmptab ? SA_Colors32 : TAG_IGNORE, tmptab,
														  TAG_DONE);

		if (tmptab)
			FreeVec(tmptab);

	if (scr)
	{
		if (pubflag)
			PubScreenStatus(scr, NULL);

		if (keepptr && deep < 5)
		{
			GetRGB32(scr->ViewPort.ColorMap, 17, 4, (ULONG *)&ilbm->colortable32[17].r);

			for (i=17; i<20; i++)
				SetRGB4(&scr->ViewPort, i, 0, 0, 0);
		}

		if (bblank)
		{
			LONG mustrethink = TRUE;

			VideoControlTags(scr->ViewPort.ColorMap, VTAG_BORDERBLANK_SET, -1,
																  VTAG_IMMEDIATE, &mustrethink,
																  VTAG_END_CM, NULL);

			if (mustrethink) RethinkDisplay();
		}
	}
	else
		return ILBMERR_CANTOPENDISP;

	*rscr = scr;

	return 0; /* ok */
}


/*
 * modefallback - passed a mode id, attempts to provide a
 *                suitable old mode to use instead
 */

#define MODE_ID_MASK (LACE|HIRES|HAM|EXTRA_HALFBRITE)

ULONG modefallback(struct ILBMInfo *ilbm, ULONG oldmode, WORD wide, WORD high, WORD deep)
{
	ULONG  newmode, bestmode, tmp;
	WORD	 i = 7;
	struct TagItem tags[9];

	tags[0].ti_Tag = BIDTAG_DIPFMustHave;
	tags[0].ti_Data = (oldmode & HAM ? DIPF_IS_HAM : 0);
	tags[0].ti_Data |= (oldmode & EXTRA_HALFBRITE ? DIPF_IS_EXTRAHALFBRITE : 0);

	tags[1].ti_Tag = BIDTAG_NominalWidth;
	tags[1].ti_Data = ilbm->bmhd.xAspect;
	tags[2].ti_Tag = BIDTAG_NominalHeight;
	tags[2].ti_Data = ilbm->bmhd.yAspect;

	tags[3].ti_Tag = BIDTAG_DesiredWidth;
	tags[3].ti_Data = wide;
	tags[4].ti_Tag = BIDTAG_DesiredHeight;
	tags[4].ti_Data = high;
	tags[5].ti_Tag = BIDTAG_Depth;
	tags[5].ti_Data = deep;
	tags[6].ti_Tag = BIDTAG_SourceID;
	tags[6].ti_Data = (FindDisplayInfo(oldmode) ? oldmode : (oldmode & (~(MONITOR_ID_MASK | GENLOCK_AUDIO|GENLOCK_VIDEO|VP_HIDE))));

	if (onlymon)
	{
		tmp = ilbm->usermodeid & MONITOR_ID_MASK;
		if (FindDisplayInfo(tmp))
		{
			tags[i].ti_Tag = BIDTAG_MonitorID;
			tags[i++].ti_Data = tmp;
		}
	}

	tags[i].ti_Tag = TAG_DONE;

	if ((bestmode = BestModeIDA(tags)) != INVALID_ID) newmode = bestmode;
	else newmode = oldmode & MODE_ID_MASK;

	return(newmode);
}


/*
 * clipit - passed width and height of a display, and the text, std, and
 *          max overscan rectangles for the mode, clipit fills in the
 *          spos (screen pos) and dclip rectangles to use in centering.
 *          Centered around smallest containing user-editable oscan pref,
 *          with dclip confined to legal maxoscan limits.
 *          Screens which center such that their top is below text
 *          oscan top, will be moved up.
 *          If a non-null uclip is passed, that clip is used instead.
 */

void clipit(WORD wide, WORD high,
				struct Rectangle *spos, struct Rectangle *dclip,
				struct Rectangle *txto, struct Rectangle *stdo,
				struct Rectangle *maxo, BOOL nohcenter, BOOL novcenter)
{
	struct  Rectangle *besto;
	WORD	minx, maxx, miny, maxy;
	WORD	txtw, txth, stdw, stdh, bestw, besth;

	/* get the txt, std and max widths and heights */
	txtw = txto->MaxX - txto->MinX + 1;
	txth = txto->MaxY - txto->MinY + 1;
	stdw = stdo->MaxX - stdo->MinX + 1;
	stdh = stdo->MaxY - stdo->MinY + 1;

	if (wide <= txtw && high <= txth)
		{	besto = txto;	bestw = txtw; besth = txth; }
	else
		{	besto = stdo; bestw = stdw; besth = stdh; }

	/* CENTER the screen based on best oscan prefs
 	 * but confine dclip within max oscan limits
	 *
	 * FIX MinX first */

	spos->MinX = minx = (besto->MinX + ((bestw - wide) >> 1));

	maxx = wide + minx - 1;
	if (maxx > maxo->MaxX) maxx = maxo->MaxX;	/* too right */
	if (minx < maxo->MinX)
	{
		minx = maxo->MinX;	/* too left  */
		/* if we want left edge of screen not clipped */
//		if (nohcenter) spos->MinX = minx;
	}

	/* FIX MinY */
	spos->MinY = miny = besto->MinY - ((high - besth) >> 1);
	/* if lower than top of txto, move up */

	if (novcenter)
		spos->MinY = miny = MIN(spos->MinY, txto->MinY);

	maxy = high + miny - 1;
	if(maxy > maxo->MaxY)  maxy = maxo->MaxY;	/* too down  */
	if(miny < maxo->MinY)
	{
		miny = maxo->MinY;	/* too up    */
		/* if we want top of screen not clipped */
//		if (nohcenter) spos->MinY = miny;
	}

	/* SET up dclip */
	dclip->MinX = minx;
	dclip->MinY = miny;
	dclip->MaxX = maxx;
	dclip->MaxY = maxy;
}

/*----------------------------------------------------------------------*/

BOOL VideoControlTags(struct ColorMap *cm, ULONG tags, ...)
{
	return (VideoControl(cm, (struct TagItem *)&tags));
}

/*****************************************************************************/

LONG processbody(BYTE *body, LONG len, struct BitMap *bitmap, BitMapHeader *bmhd)
{
	UBYTE	 srcplanes = bmhd->nPlanes;
	WORD	 srcbytes = RowBytes(bmhd->w),
			 destbytes = bitmap->BytesPerRow,  /* modulo */
			 rows = bmhd->h,
			 destwidth = RowBytes(GetBitMapAttr(bitmap, BMA_WIDTH)),
			 compression = bmhd->compression;

	register WORD iplane;
	BYTE	 *planes[MAXSRCPLANES];
	LONG	 error = 0; /* ok */

	if (compression > cmpByteRun1 || srcbytes > destwidth || srcplanes > MAXSRCPLANES)
		return ILBMERR_UNSUPPORTED;

	if (rows > bitmap->Rows) rows = bitmap->Rows;

	/* Initialize array "planes" with bitmap ptrs; NULL in empty slots.*/
	for (iplane = 0; iplane < bitmap->Depth; iplane++)
		planes[iplane] = (BYTE *)bitmap->Planes[iplane];
	for (; iplane < MAXSRCPLANES; iplane++)
		planes[iplane] = NULL;
	
	/* We'll skip mask */
	if (bmhd->masking == mskHasMask)
		planes[srcplanes++] = NULL;

	if (compression == cmpNone)
		copybody(body, planes, len, rows, srcplanes, srcbytes, destbytes);
	else
	{
		BYTE	 *buffer = NULL;
		ULONG	 bufsize;

		if (bmhd->masking == mskHasMask)
		{	
			bufsize = MaxPackedSize(RowBytes(bmhd->w)) << 4;
			if (!(buffer = AllocMem(bufsize, MEMF_ANY)))
				return IFFERR_NOMEM;
		}

		if (!unpackbody(body, buffer, planes, len, rows, srcplanes, srcbytes, destbytes))
			error = IFFERR_MANGLED;

		if (buffer)
			FreeMem(buffer, bufsize);
	}

	return error;
}

/*****************************************************************************/

void setcolors(struct ILBMInfo *ilbm)
{
	WaitTOF();
	LoadRGB32(ilbm->vp, (ULONG *)ilbm->colorrecord);
}

