/******************************************************************************
**
**  Coded by Dino Papararo 02-Feb-1998
**
**  Based on NewIff package relased by Commodore
**
**  FUNCTION
**
**    QueryMandPic -- Examine an IFF picture.
**
**  SYNOPSIS
**
**    LONG QueryMandPic (struct ILBMInfo *,struct MandelChunk **,UBYTE *);
**
**  DESCRIPTION
**
**    Passed an initilized ILBMInfo with a not-in-use IFFHandle, a MandelChunk
**
**    structure and a filename, will open an ILBM, fill in ilbm->camg and
**
**    ilbm->bmhd, and close the ILBM.
**
**    This allows you to determine if the ILBM is a size and
**
**    type you want to deal with.
**
**    For succes is necessary the ID_MAND into the iff file !
**
**    Returns 0 for success or an IFFERR (libraries/iffparse.h).
**
**
**  FUNCTION
**
**    LoadMandPic -- Load an IFF picture.
**
**  SYNOPSIS
**
**    LONG LoadMandPic (struct ILBMInfo *,UBYTE *);
**
**  DESCRIPTION
**
**    Function uses a ILBMInfo struct with record ParseInfo.iff initialized
**
**    with AllocIFF() function, and a pointer to a FileName.
**
**    It's necessary a valid Window RPort and ViewPort initialized in the passed
**
**    ILBM Structure for the BODY and COLORS, at end all memory will be freed.
**
**
**  FUNCTION
**
**    SaveMandPic -- save a screen as IFF picture.
**
**  SYNOPSIS
**
**    LONG SaveMandPic (struct ILBMInfo *,struct Chunk *,struct Chunk *,UBYTE *);
**
**  DESCRIPTION
**
**    Function uses a ILBMInfo struct with record ParseInfo.iff initialized
**
**    with AllocIFF() function, two custom chunks, and a pointer to a FileName.
**
**    In the 1st custom chunk I put the copyright infos and in the 2nd the
**
**    special chunk MAND used for window limits, fractal limits, iterations
**
**    type...
**
**    Function will save the icon file too with support for NewIcons.
**
******************************************************************************/

#define INTUI_V36_NAMES_ONLY
#define __USE_SYSBASE


#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/icon.h>
#include <proto/iffparse.h>
#include <proto/wb.h>

#include <exec/types.h>
#include <intuition/intuitionbase.h>
#include <intuition/screens.h>
#include <graphics/gfxbase.h>
#include <workbench/workbench.h>

#include <iffp/ilbm.h>
#include <iffp/ilbmapp.h>
#include <iffp/iff.h>
#include <iffp/packer.h>
#include <iffp/iffpstrings.h>

UBYTE *omodes [2] = {"r","w"};

static BYTE *PutDump(BYTE *,int);

static BYTE *PutRun(BYTE *,int,int);

#define MaxSrcPlanes (25)

#define ID_MAND MAKE_ID('M','A','N','D')

#define NOMAND 4L

#define DUMP	0
#define RUN	1
#define MinRun 3
#define MaxRun 128
#define MaxDat 128
#define INBUFSZ 128
#define BODYBUFSZ	5004
#define GetByte()       (*source++)
#define PutByte(c)      { *dest++ = (c);   ++PackPutSize; }
#define OutDump(nn)     dest = PutDump(dest, nn)
#define OutRun(nn,cc)   dest = PutRun(dest, nn, cc)
#define UGetByte()	(*source++)
#define UPutByte(c)	(*dest++ = (c))

/* local function prototypes */

struct  CatCompArrayType CatCompArray [8];

LONG QueryMandPic (struct ILBMInfo *,struct MandelChunk **,UBYTE *filename);

LONG LoadMandPic (struct ILBMInfo *,UBYTE *);

LONG SaveMandPic (struct ILBMInfo *,struct Chunk *,struct Chunk *,UBYTE *);

LONG SavePalette (struct ILBMInfo *,struct Chunk *,UBYTE *);

LONG LoadPalette (struct ILBMInfo *,UBYTE *);

static LONG __saveds __asm stdio_stream (register __a0 struct Hook *,register __a2 struct IFFHandle *,register __a1 struct IFFStreamCmd *);

LONG saveilbm (struct ILBMInfo *,struct BitMap *,ULONG,WORD,WORD,WORD,WORD,APTR,UWORD,UWORD,WORD,WORD,struct Chunk *,struct Chunk *,UBYTE *);

LONG initbmhd (BitMapHeader *,struct BitMap *,WORD,WORD,WORD,WORD,WORD,WORD,WORD,ULONG);

LONG loadcmap (struct ILBMInfo *);

LONG putcmap (struct IFFHandle *,APTR,UWORD,UWORD);

LONG loadbody (struct IFFHandle *,struct BitMap *,BitMapHeader *);

LONG loadbody2 (struct IFFHandle *,struct BitMap *,BYTE *,BitMapHeader *,BYTE *,ULONG);

LONG putbody (struct IFFHandle *,struct BitMap *,BYTE *,BitMapHeader *,BYTE *,LONG);

ULONG getcamg (struct ILBMInfo *);

LONG openifile (struct ParseInfo *,UBYTE *,ULONG);

LONG parseifile (struct	ParseInfo *,LONG,LONG,LONG *,LONG *,LONG *);

VOID closeifile (struct ParseInfo *);

VOID initiffasstdio (struct IFFHandle *);

LONG PutCk (struct IFFHandle *,LONG,LONG,VOID *);

LONG getcolors (struct ILBMInfo *);

LONG alloccolortable (struct ILBMInfo *);

LONG setcolors (struct ILBMInfo *,struct ViewPort *);

VOID freecolors (struct ILBMInfo *);

LONG chkcnt (LONG *);

LONG getcontext (struct IFFHandle *);

LONG contextis (struct IFFHandle *,LONG,LONG);

LONG currentchunkis (struct IFFHandle *,LONG,LONG);

UBYTE *findpropdata (struct IFFHandle *,LONG,LONG);

static BYTE *PutDump (BYTE *,int);

static BYTE *PutRun (BYTE *,int,int);

LONG PackRow (BYTE **,BYTE **,LONG);

BOOL UnPackRow (BYTE **,BYTE **,WORD,WORD);

LONG PackPutSize;

char PackBuffer [256];	/* [TBD] should be 128?  on stack?*/

/* Data for project icon for saved ILBM */

UWORD ILBMI1Data [] =
{
  /* Plane 0*/
  0x0000,0x0000,0x0000,0x0001,0x7fff,0xffff,0xffff,0xffff,  0x1fff,0xffff,0xffff,0xffff,0x7fff,0xffff,0xffff,0xffff,
  0x7fff,0xffff,0xffff,0xffff,0x3fff,0xffff,0xffff,0xffff,  0x7fff,0xffff,0xffff,0xffff,0x7fff,0xffff,0xffdf,0xffff,
  0x7fff,0xffff,0xffff,0x7fff,0x7fff,0xffff,0xffff,0x7fff,  0x7fff,0xffff,0xffff,0xffff,0x7fff,0xffff,0xffff,0xffff,
  0x7fff,0xffff,0xfdff,0xbfff,0x7fff,0xffff,0xfdff,0xffff,  0x7fff,0xffff,0xf7ff,0xfbff,0x7fff,0xffff,0xffff,0xffff,
  0x7fff,0xffff,0xffff,0xffbf,0x7fff,0xfffe,0x7fff,0xffff,  0x7fff,0xfffe,0xffff,0xffff,0x7fff,0xffff,0xffff,0xffff,
  0x7fff,0xfffd,0xffff,0xffef,0x7fff,0xffff,0xffff,0xffcf,  0x7fff,0xffff,0xffff,0xffdf,0x7fff,0xee3f,0xffff,0xffff,
  0x7fff,0xffdf,0xffff,0xffff,0x7fff,0xffff,0xffff,0xffff,  0x7fff,0xfff7,0xffff,0xffff,0x7fff,0x7ff3,0xffff,0xffff,
  0x7ffb,0xffff,0xffff,0xffff,0x7ffe,0xffff,0xffff,0xffff,  0x6b7f,0xffff,0xffff,0xffff,0x7ffb,0xffff,0xffff,0xffff,
  0x7fff,0xfff3,0xffff,0xffff,0x7fff,0xfff7,0xffff,0xffff,  0x7fff,0xffff,0xffff,0xffff,0x7fff,0xffef,0xffff,0xffff,
  0x7fff,0xffdf,0xffff,0xffff,0x7fff,0xdfff,0xffff,0xffdf,  0x7fff,0xfdff,0xffff,0xffdf,0x7fff,0xffff,0xffff,0xffff,
  0x7fff,0xffff,0xffff,0xffff,0x7fff,0xffff,0xffff,0xffff,  0x7fff,0xfffe,0xffff,0xffff,0x7fff,0xffff,0xffff,0xffff,
  0x7fff,0xffff,0xffff,0xffff,0x7fff,0xffff,0xffff,0xfdff,  0x7fff,0xffff,0xf7ff,0xfbbf,0x7fff,0xffff,0xcfff,0xffff,
  0x7fff,0xffff,0xffff,0xffff,0x7fff,0xffff,0xffee,0xffff,  0x7fff,0xffff,0xffff,0x7fff,0x7fff,0xffff,0xffff,0x7fff,
  0x7fff,0xffff,0xffff,0x7fff,0x7fff,0xffff,0xffcf,0xffff,  0x7fff,0xffff,0xfffd,0xffff,0x3fff,0xffff,0xffff,0xffff,
  0x7fff,0xffff,0xffff,0xffff,0x7fff,0xffff,0xffff,0xffff,  0x5fff,0xffff,0xffff,0xffff,0x1fff,0xffff,0xffff,0xffff,
  0x4fff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
  /* Plane 1*/
  0xffff,0xffff,0xffff,0xfffe,0xcfff,0xffff,0xffff,0xfffe,  0xffff,0xffff,0xffff,0x7ffe,0xbfff,0xffff,0xffff,0xfffe,
  0xffff,0xffff,0xffff,0xfffe,0xffff,0xffff,0xffff,0xfffe,  0xbfff,0xffff,0xfffb,0xfffe,0xffff,0xffff,0xffc1,0xfffe,
  0xffff,0xffff,0xffe0,0x7ffe,0xffff,0xffff,0xffe0,0x7ffe,  0xffff,0xffff,0xffe1,0xfffe,0xffff,0xffff,0xfff3,0xfffe,
  0xffff,0xffff,0xfdc0,0x3ffe,0xffff,0xffff,0xdc00,0x07fe,  0xffff,0xffff,0xc400,0x03fe,0xffff,0xffff,0xe000,0x003e,
  0xffff,0xffff,0xc000,0x003e,0xffff,0xfffe,0x4000,0x007e,  0xffff,0xfffe,0x0000,0x007e,0xffff,0xffff,0x0000,0x003e,
  0xffff,0xfffc,0x0000,0x000e,0xffff,0xfffe,0x0000,0x000e,  0xffff,0xddfc,0x0000,0x001e,0xffff,0xc83c,0x0000,0x001e,
  0xffff,0xc01c,0x0000,0x001e,0xffff,0xc01c,0x0000,0x003e,  0xffff,0x8004,0x0000,0x003e,0xffff,0x0000,0x0000,0x003e,
  0xfffb,0x0004,0x0000,0x003e,0xfff8,0x0000,0x0000,0x007e,  0xa360,0x0000,0x0000,0x01fe,0xfff9,0x0000,0x0000,0x003e,
  0xffff,0x0000,0x0000,0x003e,0xffff,0x8004,0x0000,0x003e,  0xffff,0x800c,0x0000,0x001e,0xffff,0xc00c,0x0000,0x001e,
  0xffff,0xc01c,0x0000,0x001e,0xffff,0xddfc,0x0000,0x001e,  0xffff,0xfdfe,0x0000,0x001e,0xffff,0xfffe,0x0000,0x001e,
  0xffff,0xffff,0x0000,0x003e,0xffff,0xffff,0x0000,0x003e,  0xffff,0xfffe,0x0000,0x007e,0xffff,0xffff,0xc000,0x00fe,
  0xffff,0xffff,0xe000,0x003e,0xffff,0xffff,0xc000,0x003e,  0xffff,0xffff,0xc400,0x023e,0xffff,0xffff,0xcc00,0x07fe,
  0xffff,0xffff,0xfff1,0xfffe,0xffff,0xffff,0xffe0,0xfffe,  0xffff,0xffff,0xffe0,0x7ffe,0xffff,0xffff,0xffe0,0x7ffe,
  0xffff,0xffff,0xffe0,0x7ffe,0xffff,0xffff,0xffc1,0xfffe,  0xffff,0xffff,0xfff9,0xfffe,0xffff,0xffff,0xffff,0xfffe,
  0xbfff,0xffff,0xffff,0xfffe,0xffff,0xffff,0xffff,0xfffe,  0xffff,0xffff,0xffff,0xfffe,0xffff,0xffff,0xffd7,0xfffe,
  0xffff,0xffff,0xffff,0xfffe,0x0000,0x0000,0x0000,0x0000
};

UWORD ILBMI2Data [] =
{
  /* Plane 0*/
  0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xfffe,  0x9ddd,0xdddd,0xdddd,0xdddc,0xffff,0xffff,0xffff,0xfffe,
  0xf777,0x7777,0x7777,0x7776,0xbfff,0xffff,0xffff,0xfffe,  0xdddd,0xdddd,0xdddd,0xdddc,0xffff,0xffff,0xffc3,0xfffe,
  0xf777,0x7777,0x7775,0x7776,0xffff,0xffff,0xffe8,0x7ffe,  0xdddd,0xdddd,0xddd5,0xdddc,0xffff,0xffff,0xfff3,0xfffe,
  0xf777,0x7777,0x7555,0x3776,0xffff,0xffff,0xdc88,0x8ffe,  0xdddd,0xdddd,0xd555,0x51dc,0xffff,0xffff,0xe222,0x223e,
  0xf777,0x7777,0x5555,0x5536,0xffff,0xfffe,0x4888,0x88fe,  0xdddd,0xdddc,0x5555,0x555c,0xffff,0xffff,0x2222,0x223e,
  0xf777,0x7775,0x5555,0x5546,0xffff,0xfffe,0x8888,0x888e,  0xdddd,0xdddd,0x5555,0x555c,0xffff,0xea3e,0x2222,0x223e,
  0xf777,0x5555,0x5555,0x5556,0xffff,0xc89c,0x8888,0x88be,  0xdddd,0xd555,0x5555,0x555c,0xffff,0x2222,0x2222,0x223e,
  0xf773,0x5555,0x5555,0x5576,0xfff8,0x8888,0x8888,0x88fe,  0xc955,0x5555,0x5555,0x55dc,0xfffb,0x2222,0x2222,0x223e,
  0xf777,0x5551,0x5555,0x5576,0xffff,0x8884,0x8888,0x88be,  0xdddd,0xd55d,0x5555,0x555c,0xffff,0xe22e,0x2222,0x223e,
  0xf777,0x5555,0x5555,0x5556,0xffff,0xddfc,0x8888,0x889e,  0xdddd,0xdddd,0x5555,0x555c,0xffff,0xfffe,0x2222,0x223e,
  0xf777,0x7777,0x5555,0x5576,0xffff,0xffff,0x8888,0x88be,  0xdddd,0xdddc,0x5555,0x555c,0xffff,0xffff,0xe222,0x22fe,
  0xf777,0x7777,0x7555,0x5576,0xffff,0xffff,0xc888,0x88be,  0xdddd,0xdddd,0xd555,0x511c,0xffff,0xffff,0xce22,0x27fe,
  0xf777,0x7777,0x7775,0x7776,0xffff,0xffff,0xffe8,0xfffe,  0xdddd,0xdddd,0xddd5,0x5ddc,0xffff,0xffff,0xffe2,0x7ffe,
  0xf777,0x7777,0x7775,0x7776,0xffff,0xffff,0xffc9,0xfffe,  0xdddd,0xdddd,0xdddd,0xdddc,0xbfff,0xffff,0xffff,0xfffe,
  0xf777,0x7777,0x7777,0x7776,0xffff,0xffff,0xffff,0xfffe,  0xdddd,0xdddd,0xdddd,0xdddc,0x9fff,0xffff,0xffff,0xfffe,
  0xc777,0x7777,0x7777,0x7776,0x0000,0x0000,0x0000,0x0000,
  /* Plane 1*/
  0x0000,0x0000,0x0000,0x0000,0x4fff,0xffff,0xffff,0xffff,  0x7fff,0xffff,0xffff,0x7fff,0x3fff,0xffff,0xffff,0xffff,
  0x7fff,0xffff,0xffff,0xffff,0x7fff,0xffff,0xffff,0xffff,  0x3fff,0xffff,0xfffb,0xffff,0x7fff,0xffff,0xffdd,0xffff,
  0x7fff,0xffff,0xffea,0x7fff,0x7fff,0xffff,0xfff7,0x7fff,  0x7fff,0xffff,0xffeb,0xffff,0x7fff,0xffff,0xffff,0xffff,
  0x7fff,0xffff,0xfdea,0xbfff,0x7fff,0xffff,0xfd77,0x77ff,  0x7fff,0xffff,0xe6aa,0xabff,0x7fff,0xffff,0xfddd,0xddff,
  0x7fff,0xffff,0xeaaa,0xaabf,0x7fff,0xfffe,0x7777,0x777f,  0x7fff,0xfffe,0xaaaa,0xaaff,0x7fff,0xffff,0xdddd,0xddff,
  0x7fff,0xfffc,0xaaaa,0xaaaf,0x7fff,0xffff,0x7777,0x774f,  0x7fff,0xfffe,0xaaaa,0xaa9f,0x7fff,0xcc3d,0xdddd,0xdddf,
  0x7fff,0xea9e,0xaaaa,0xaabf,0x7fff,0xf77f,0x7777,0x777f,  0x7fff,0xaaa6,0xaaaa,0xaabf,0x7fff,0x5dd1,0xdddd,0xddff,
  0x7ffb,0xaaae,0xaaaa,0xaabf,0x7ffe,0x7777,0x7777,0x777f,  0x236a,0xaaaa,0xaaaa,0xabff,0x7ff9,0xdddd,0xdddd,0xddff,
  0x7fff,0xaaa2,0xaaaa,0xaabf,0x7fff,0xf777,0x7777,0x777f,  0x7fff,0xaaae,0xaaaa,0xaabf,0x7fff,0xddcd,0xdddd,0xdddf,
  0x7fff,0xea9e,0xaaaa,0xaabf,0x7fff,0xdfff,0x7777,0x775f,  0x7fff,0xfdfe,0xaaaa,0xaa9f,0x7fff,0xffff,0xdddd,0xdddf,
  0x7fff,0xffff,0xaaaa,0xaabf,0x7fff,0xffff,0x7777,0x777f,  0x7fff,0xfffe,0xaaaa,0xaaff,0x7fff,0xffff,0xdddd,0xddff,
  0x7fff,0xffff,0xeaaa,0xaabf,0x7fff,0xffff,0xf777,0x757f,  0x7fff,0xffff,0xe6aa,0xaabf,0x7fff,0xffff,0xcddd,0xdfff,
  0x7fff,0xffff,0xfffb,0xffff,0x7fff,0xffff,0xffe6,0xffff,  0x7fff,0xffff,0xffea,0x7fff,0x7fff,0xffff,0xfffd,0x7fff,
  0x7fff,0xffff,0xffea,0x7fff,0x7fff,0xffff,0xffc7,0xffff,  0x7fff,0xffff,0xfff9,0xffff,0x7fff,0xffff,0xffff,0xffff,
  0x3fff,0xffff,0xffff,0xffff,0x7fff,0xffff,0xffff,0xffff,  0x7fff,0xffff,0xffff,0xffff,0x7fff,0xffff,0xffd7,0xffff,
  0x7fff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff
};

struct Image ILBMI1 =
{
    0,0,           /* Upper left corner */
    64,62,2,       /* Width, Height, Depth */
    ILBMI1Data,    /* Image data */
    0x0004,0x0000, /* PlanePick, PlaneOnOff */
    NULL           /* Next image */
};

struct Image ILBMI2 =
{
    0,0,           /* Upper left corner */
    64,62,2,       /* Width, Height, Depth */
    ILBMI2Data,    /* Image data */
    0x0004,0x0000, /* PlanePick, PlaneOnOff */
    NULL           /* Next image */
};

UBYTE *ILBMTools [] =
{
  "FILETYPE=ILBM",
  
  /* NewIcons tooltypes for NewIcons icon */
  
  "*** DON'T EDIT THE FOLLOWING LINES!! ***",
  "IM1=BEI!*£»5#<@¸®®¢;¤9n1(dFJ«¡!m9Ó&TBÇÑ",
  "IM1=ÛdBá(g[®(ß(ÈdB.ÅÞ(Éc9m(ZÝ(ÉS$B6T=Û1(ÉPeJ¦(«.`Ù1¿ÉP¦DB2¥M'@×\"1Â(P¥dS9hj6c¡Ö\"=Á·P¥fW[­·E+A¹Õ\">0²(¥fÈ[®¿Ä2e¡ÅÔ\">,²J¥fÈ[®¿ÈYiB¹",
  "IM1=ZÓ\">,1I(fÈ[®¿È[­µ¢T=Ò\">,5I,·È[®¿È[®·E+A¹Ñ\">,51-¿È[®¿È[®¿RH«.`\">,51m¿È[®¿È[®¿Ä2e¡Å\">,51®¿È[®¿È[®¿Ä2e¡ÅÑ!/&*h·È[®¿È[®¿ÈYiB¹ZÒg",
  "IM1=c%DC[®¿È[®¿È[lj6c¡ÒC²b¦1l¿È[®¿È[­µ¢T=Ó1Á²*hdW[®¿È[®·E+A¹Ó(ÉS%J£;®¿È[®·E+A¹Ô$\\Ab1I-¿È[®·E+A¹Õ\"=Á·P¥fW[­·E+A¹Ö!(Éd8JB9lµE+A¹Ø$",
  "IM1=[¯,5I(d¦+A¹ÁØ\"1/&(¦J¢+A¹1[`Ø.gc8B1SA¹1(e±Ö!¿1C³,·TA¹1(dNÖ=³1(¢À(d=¹1(e¿Õ#­B1(dHg[®(1(®±Õ[dB1(dB1B1\"1+­Õ'XdB1(dB1(dB1[`Õ'(dB",
  "IM1=(dB1(dB1+­×<B1(dB1(dB1[`Ø.Â(dB1(dB7XÛ[dB1(dBÈÝ!¿1(dNÁß'Xe¿â=±ñ",
  "IM2=BEI!-£»5#<@¸®®¢;¤9n1(d9l·VS9ll¦U@#:RS9l±Ò!m(®`Ö",
  "IM2=ÛdBÞ*¦J¦J¦I(Û!J¦JÈ(eJ¦DÚ)D®¿Â!(dUE£Ù5D¦¿È((dB:£»`×5D®³1!+®¿È5IU@Ö5D¦¿1((®¿ÈY*¥«¡Õ)D®³\"1+­dB1,ÆJM0Ô!D¦¿,1(®µB1(¸J¦6hÓ!D®³,·[",
  "IM2=[hfS9n¦J¦J¥`Ò!J®³,·[­dS9lÆJ¦IUJ@Òj­@L·S(fZ¦J¦J¦J¥«®0Ñ*®³\"·S8dS¦J¦J¦J¦J¦ºÒ¥@BÇS9fZ¦J¦J¦J¦JMNhÑEB\"7S9lS¦J¦J¦J¦J¦JÅ`EHBÈ39l»¦J",
  "IM2=¦J¦J¦J¦E¦0Ñ$¡e¿FS9¦J¦J¦J¦J¦J¦NhÑ%B\"7S9l´¦J¦J¦J¦J¦JÅ`ÑJ¡1[ªlS=J¦J¦J¦J¦J7DÒ5X`enB1,ÆJ¦J¦J¦J¢Z@Ñ!Jd$NÃ1(¸J¦J¦J¦J¢Z@Ó¦¿01[®µC9l",
  "IM2=9»¦JMNhÓ!J­@BÈ[hfS9n¦:MNhÕ¥¿Â!(e¿B1(g:¦JhÕ\"JW((dNÃ1(dZ¦Fi@Õ!5K­B01[®¿Ë¤2¤A¦Ö*¦B1!+®À:¦1iI(f`ÖJ¦[d$UJ¦Jhµ¥dTÖJb:¦J¦JMF¦DRJ¥Õ%",
  "IM2=¦$B1(e)J¥»UJC:`Ô*¡dB1(dB4B0U:¦9m@Ò!J(dB1(dB1(dB:¦Jl·¡Ñ!A(dB1(dB1(dCJ%2¦FSHÑ*(dB1(dB1(dU@Ñ2iJ¤9lÑ%HB1(dB1(f¥Ó)D¦Ia@Ò5A(dB1(»",
  "IM2=¡Ô!4j¦J`Ó5A(dCJ×jEJhÕ¦$U@ØE2hÖ\"¥Ú2iDâ)D¡Ó",
  NULL
};

struct DiskObject ILBMobject =
{
  WB_DISKMAGIC,                      /* Magic Number */
  WB_DISKVERSION,                    /* Version */
  {                                  /* Embedded Gadget Structure */
    NULL,                            /* Next Gadget Pointer */
    0, 0, 64, 62,                    /* Left,Top,Width,Height */
    GFLG_GADGIMAGE | GADGBACKFILL,   /* Flags */
    GACT_RELVERIFY | GACT_IMMEDIATE, /* Activation Flags */
    GTYP_BOOLGADGET,                 /* Gadget Type */
    (APTR)&ILBMI1,                   /* Render Image */
    (APTR)&ILBMI2,                   /* Select Image */
    NULL,                            /* Text for Gadget*/
    NULL,                            /* Mutual Exclude */
    NULL,                            /* Special Info */
    0,                               /* Gadget ID */
    NULL                             /* User Data */
  },
  WBPROJECT,                        /* Icon Type */
  "MultiView",                      /* Default Tool */
  ILBMTools,                        /* Tool Type Array */
  NO_ICON_POSITION,                 /* Current X */
  NO_ICON_POSITION,                 /* Current Y */
  NULL,                             /* Drawer Structure */
  NULL,                             /* Tool Window */
  NULL                              /* Stack Size */
};

struct MandelChunk { WORD LeftEdge,TopEdge,Width,Height;
                     long double RMin,RMax,IMin,IMax;
                     long double RConst,IConst;
                     ULONG Iterations;
                     ULONG Special;
                   };

/* saveilbm
 *
 * Given an ILBMInfo with a currently available (not-in-use)
 *   ParseInfo->iff IFFHandle, a BitMap ptr, modeid, widths/heights,
 *   colortable, ncolors, bitspergun, masking, transparent color,
 *   optional chunklists, and filename, will save the bitmap as an ILBM.
 *
 *  if bitspergun=4,  colortable is words, each with nibbles 0RGB
 *  if bitspergun=8,  colortable is byte guns of RGBRGB etc. (like a CMAP)
 *  if bitspergun=32, colortable is ULONG guns of RGBRGB etc.
 *     Only the high eight bits of each gun will be written to CMAP.
 *     Four bit guns n will be saved as nn
 *
 * The struct Chunk *chunklist is for chunks you wish written
 * other than BMHD, CMAP, and CAMG (they will be screened out)
 * because they are calculated and written separately
 *
 * Returns 0 for success, or an IFFERR
 */

LONG saveilbm (struct ILBMInfo *ilbm,struct BitMap *bitmap,
               ULONG modeid,WORD width,WORD height,WORD pagewidth,WORD pageheight,
               APTR colortable,UWORD ncolors,UWORD bitspergun,
               WORD masking,WORD transparentColor,
               struct Chunk *chunklist1,struct Chunk *chunklist2,
               UBYTE *filename)
{
struct IFFHandle *iff;

struct Chunk *chunk;

ULONG chunkID;

UBYTE *bodybuf;

LONG size, error;

  iff = ilbm->ParseInfo.iff;

  if (! (modeid & 0xFFFF0000)) modeid &= OLDCAMGMASK;

  if (! (bodybuf = AllocVec (BODYBUFSZ,MEMF_PUBLIC))) return (IFFERR_NOMEM);

  error = openifile (&(ilbm->ParseInfo), filename, IFFF_WRITE);

  if (! error)
  {
    error = PushChunk (iff, ID_ILBM, ID_FORM, IFFSIZE_UNKNOWN);

    initbmhd (&ilbm->Bmhd, bitmap, masking, cmpByteRun1, transparentColor, width, height, pagewidth, pageheight, modeid);

    CkErr (putbmhd (iff,&ilbm->Bmhd));

    if (colortable) CkErr (putcmap (iff,colortable,ncolors,bitspergun));

    if (ilbm->IFFPFlags & IFFPF_NOMONITOR) modeid &= (~MONITOR_ID_MASK);

    ilbm->camg = modeid;

    CkErr (putcamg (iff,&modeid));

	/* Write out chunklists 1 & 2 (if any), except for

	 * any BMHD, CMAP, or CAMG (computed/written separately) */

    for (chunk = chunklist1; chunk; chunk = chunk->ch_Next)
    {
      chunkID = chunk->ch_ID;

      if ((chunkID != ID_BMHD) && (chunkID != ID_CMAP) && (chunkID != ID_CAMG))
      {
        size = chunk->ch_Size == IFFSIZE_UNKNOWN ? strlen (chunk->ch_Data) : chunk->ch_Size;

        CkErr (PutCk (iff, chunkID, size, chunk->ch_Data));
      }
    }

    for (chunk = chunklist2; chunk; chunk = chunk->ch_Next)
    {
      chunkID = chunk->ch_ID;

      if ((chunkID != ID_BMHD) && (chunkID != ID_CMAP) && (chunkID != ID_CAMG))
      {
        size = chunk->ch_Size == IFFSIZE_UNKNOWN ? strlen (chunk->ch_Data) : chunk->ch_Size;

        CkErr (PutCk (iff, chunkID, size, chunk->ch_Data));
      }
    }

    /* Write out the BODY */

    CkErr (putbody (iff, bitmap, NULL, &ilbm->Bmhd, bodybuf, BODYBUFSZ));

    CkErr (PopChunk (iff));	/* close out the FORM */

    closeifile (&(ilbm->ParseInfo));	/* and the file */
  }

  FreeVec (bodybuf);

  return (error);
}

/* openifile
 *
 * Passed a ParseInfo structure with a not-in-use IFFHandle, filename
 *   and IFF open mode (IFFF_READ or IFFF_WRITE) opens file for use with
 *   iffparse.library support modules.
 *
 * Returns 0 for success or an IFFERR (libraries/iffparse.h)
 */

LONG openifile (struct ParseInfo *pi, UBYTE *filename, ULONG iffopenmode)
{
struct IFFHandle *iff;

LONG error = CLIENT_ERROR;

  if (! pi) return (error);

  if (! (iff = pi->iff)) return (error);

  pi->clipboard = FALSE;

  /* Set up IFFHandle for buffered stdio I/O. */

  if (! (iff->iff_Stream = (ULONG) fopen (filename,omodes [iffopenmode & 1]))) return (NOFILE);

  else initiffasstdio (iff);

  pi->filename = filename;

  error = OpenIFF (iff,iffopenmode);

  pi->opened = error ? FALSE : TRUE;	/* currently open handle */

  return (error);
}

/* closeifile
 *
 * closes file or clipboard opened with openifile, and frees all
 *   iffparse context parsed by parseifile.
 *
 */

void closeifile (struct ParseInfo *pi)
{
struct IFFHandle *iff;

  if (! pi) return;

  if (! (iff = pi->iff)) return;

  if (pi->opened) CloseIFF (iff);

  if (iff->iff_Stream)
  {
    if (pi->clipboard) CloseClipboard ((struct ClipboardHandle *) (iff->iff_Stream));

    else fclose ((FILE *) (iff->iff_Stream));
  }

  iff->iff_Stream = NULL;

  pi->clipboard = NULL;

  pi->opened = NULL;
}

/*
 * File I/O hook functions which the IFF library will call.
 * A return of 0 indicates success (no error).
 *
 * Iffparse.library calls this code via struct Hook and Hook.asm
 */
static LONG __saveds __asm stdio_stream (register __a0 struct Hook *hook,register __a2 struct IFFHandle *iff,register __a1 struct IFFStreamCmd *actionpkt)
{
register FILE *stream;
register LONG nbytes;
register LONG actual;
register UBYTE *buf;
LONG len;

  stream = (FILE *) iff->iff_Stream;

  if (! stream) return (1L);

  nbytes = actionpkt->sc_NBytes;

  buf = (UBYTE *) actionpkt->sc_Buf;

  switch (actionpkt->sc_Command)
  {
    case IFFSCC_READ: do
                      {  actual = (nbytes > 32767 ? 32767 : nbytes);

                         if ((len = fread (buf, 1, actual, stream)) != actual) break;

                         nbytes -= actual;

                         buf += actual;

                      } while (nbytes > NULL);

                      return (nbytes ? IFFERR_READ : NULL);

    case IFFSCC_WRITE: do
                       {  actual = (nbytes > 32767 ? 32767 : nbytes);

                          if ((len = fwrite (buf, 1, actual, stream)) != actual) break;

                          nbytes -= actual;

                          buf += actual;

                       } while (nbytes > NULL);

                       return (nbytes ? IFFERR_WRITE : NULL);

    case IFFSCC_SEEK:  return ((fseek (stream, nbytes, 1) == -1) ? IFFERR_SEEK : NULL);

    default:           return (NULL);  /*  No _INIT or _CLEANUP required.  */
  }
}

/* initiffasstdio (ie. init iff as stdio)
 *
 * sets up hook callback for the file stream handler above
 */

void initiffasstdio (struct IFFHandle *iff)
{
static struct Hook stdiohook = {{NULL},(ULONG (*)()) stdio_stream,NULL,NULL};

/*
 * Initialize the IFF structure to point to the buffered I/O
 * routines.  Unbuffered I/O is terribly slow.
 */

  InitIFF (iff, IFFF_FSEEK | IFFF_RSEEK, &stdiohook);
}

/* PutCk
 * Writes one chunk of data to an iffhandle
 */

LONG PutCk (struct IFFHandle *iff,long id,long size,void *data)
{
LONG error,wlen;

  if (! (error = PushChunk (iff,0,id,size)))
  {
     /* Write the actual data */

     if ((wlen = WriteChunkBytes (iff,data,size)) != size) error = IFFERR_WRITE;

     else error = PopChunk (iff);
  }

  return (error);
}

/*---------- initbmhd -------------------------------------------------*/

LONG initbmhd (BitMapHeader *bmhd, struct BitMap *bitmap,
               WORD masking, WORD compression, WORD transparentColor,
               WORD width, WORD height, WORD pageWidth, WORD pageHeight,
               ULONG modeid)
{
  struct DisplayInfo DI;

  if ((! bmhd) || (! bitmap) || (! width) || (! height)) return (CLIENT_ERROR);

  bmhd->w = width;

  bmhd->h = height;

  bmhd->x = bmhd->y = 0;	/* Default position is (0,0).*/

  bmhd->nPlanes = bitmap->Depth;

  bmhd->masking = masking;

  bmhd->compression = compression;

  bmhd->flags = BMHDF_CMAPOK;	/* we will store 8 significant bits */

  bmhd->transparentColor = transparentColor;

  bmhd->pageWidth = pageWidth;

  bmhd->pageHeight = pageHeight;

  bmhd->xAspect = 0;	/* So we can tell when we've got it */

  if (GetDisplayInfoData (NULL, (UBYTE *) &DI, sizeof (struct DisplayInfo), DTAG_DISP, modeid))
  {
     bmhd->xAspect = DI.Resolution.x;

     bmhd->yAspect = DI.Resolution.y;
  }

    /* If running under 1.3 or GetDisplayInfoData failed, use old method
     * of guessing aspect ratio */

  if (! bmhd->xAspect)
  {
    bmhd->xAspect =  44;

    bmhd->yAspect = ((struct GfxBase *) GfxBase)->DisplayFlags & PAL ? 44 : 52;

    if (modeid & HIRES) bmhd->xAspect = bmhd->xAspect >> 1;

    if (modeid & LACE)  bmhd->yAspect = bmhd->yAspect >> 1;
  }

  return (IFF_OKAY);
}

/*---------- putcmap ---------------------------------------------------*/
/*  This function will accept a table of color values in one of the
 *  following forms:
 *  if bitspergun=4,  colortable is words, each with nibbles 0RGB
 *  if bitspergun=8,  colortable is bytes of RGBRGB etc. (like a CMAP)
 *  if bitspergun=32, colortable is ULONGS of RGBRGB etc.
 *  (only the high eight bits of each gun will be written to CMAP)
 */

LONG putcmap (struct IFFHandle *iff,APTR colortable,UWORD ncolors,UWORD bitspergun)
{
LONG error, offs;

WORD  *tabw;

UBYTE *tab8;

ColorRegister cmapReg;

  if ((! iff) || (! colortable)) return (CLIENT_ERROR);

  /* size of CMAP is 3 bytes * ncolors */

  if (error = PushChunk (iff,NULL,ID_CMAP,ncolors * sizeofColorRegister)) return (error);

  if (bitspergun == 4)
  {
    /* Store each 4-bit value n as nn */

    tabw = (UWORD *) colortable;

    for (; ncolors; --ncolors)
    {
      cmapReg.red    = ( *tabw >> 4 ) & 0xf0;

      cmapReg.red   |= (cmapReg.red >> 4);

      cmapReg.green  = ( *tabw      ) & 0xf0;

      cmapReg.green |= (cmapReg.green >> 4);

      cmapReg.blue   = ( *tabw << 4 ) & 0xf0;

      cmapReg.blue  |= (cmapReg.blue >> 4);

      if ((WriteChunkBytes (iff,(BYTE *) &cmapReg,sizeofColorRegister)) != sizeofColorRegister) return (IFFERR_WRITE);

      ++tabw;
    }
  }

  else

    if ((bitspergun == 8) || (bitspergun == 32))
    {
      tab8 = (UBYTE *) colortable;

      offs = (bitspergun == 8) ? 1 : 4;

      for ( ;  ncolors;  --ncolors )
      {
        cmapReg.red   = *tab8;

        tab8 += offs;

        cmapReg.green = *tab8;

        tab8 += offs;

        cmapReg.blue  = *tab8;

        tab8 += offs;

        if ((WriteChunkBytes (iff,(BYTE *) &cmapReg,sizeofColorRegister)) != sizeofColorRegister) return (IFFERR_WRITE);
      }
    }

  error = PopChunk (iff);

  return (error);
}

/*---------- putbody ---------------------------------------------------*/
/* NOTE: This implementation could be a LOT faster if it used more of the
 * supplied buffer. It would make far fewer calls to IFFWriteBytes (and
 * therefore to DOS Write).
 *
 * Incorporates modification by Jesper Steen Moller to accept source
 * rows wider than dest rows, with one modulo variable for source bitplane
 * rows and one for the ILBM bitmap rows.
 */

LONG putbody (struct IFFHandle *iff, struct BitMap *bitmap, BYTE *mask,BitMapHeader *bmhd, BYTE *buffer, LONG bufsize)
{
LONG error;

LONG rowBytes = bitmap->BytesPerRow;   /* for source modulo only */

LONG FileRowBytes = RowBytes (bmhd->w); /* width to write in bytes */

int dstDepth = bmhd->nPlanes;

UBYTE compression = bmhd->compression;

int planeCnt;                          /* number of bit planes including mask */

register int iPlane, iRow;

register LONG packedRowBytes;

BYTE *buf;

BYTE *planes [MAXSAVEDEPTH + 1];        /* array of ptrs to planes & mask */

  if ( bufsize < MaxPackedSize (FileRowBytes) ||  /* Must buffer a comprsd row*/

       compression > cmpByteRun1              ||  /* bad arg */

       bitmap->Rows != bmhd->h                ||  /* inconsistent */

       rowBytes < FileRowBytes                ||  /* inconsistent*/

       bitmap->Depth < dstDepth               ||  /* inconsistent */

       dstDepth > MAXSAVEDEPTH )                  /* too many for this routine*/

    return (CLIENT_ERROR);

  planeCnt = dstDepth + (mask == NULL ? 0 : 1);

  /* Copy the ptrs to bit & mask planes into local array "planes" */

  for (iPlane = 0; iPlane < dstDepth; iPlane++) planes [iPlane] = (BYTE *) bitmap->Planes [iPlane];

  if (mask != NULL) planes [dstDepth] = mask;

  /* Write out a BODY chunk header */

  if (error = PushChunk (iff, NULL, ID_BODY, IFFSIZE_UNKNOWN)) return (error);

  /* Write out the BODY contents */

  for (iRow = bmhd->h; iRow > 0; iRow--)
  {
    for (iPlane = 0; iPlane < planeCnt; iPlane++)
    {
      /* Write next row.*/

      if (compression == cmpNone)
      {
        if (WriteChunkBytes (iff,planes [iPlane],FileRowBytes) != FileRowBytes) error = IFFERR_WRITE;

        planes [iPlane] += rowBytes; /* Possibly skipping unused bytes */
      }

      else                     /* Compress and write next row.*/
      {
        buf = buffer;

        packedRowBytes = PackRow (&planes [iPlane], &buf, FileRowBytes);

        /* Note that packrow incremented planes already by FileRowBytes */

        planes [iPlane] += rowBytes-FileRowBytes; /* Possibly skipping unused bytes */

        if (WriteChunkBytes (iff,buffer,packedRowBytes) != packedRowBytes) error = IFFERR_WRITE;
      }

      if (error) return (error);
    }
  }

  /* Finish the chunk */

  error = PopChunk (iff);

  return (error);
}

LONG loadcmap (struct ILBMInfo *ilbm)
{
struct StoredProperty *sp;

struct IFFHandle *iff;

BOOL AllShifted;

UBYTE *rgb, rb, gb, bb;

LONG k;

ULONG ncolors, gun, ncheck, nc, r, g, b;

  if (! (iff = ilbm->ParseInfo.iff)) return (CLIENT_ERROR);

  if (! (ilbm->colortable)) return (1L);

  if (! (sp = FindProp (iff, ID_ILBM, ID_CMAP))) return (1L);

  rgb = sp->sp_Data;

  /* file has this many colors */

  nc = sp->sp_Size / sizeofColorRegister;

  ncolors = nc;

  /* if ILBMInfo can't hold that many, we'll load less */

  if (ilbm->ncolors < ncolors) ncolors = ilbm->ncolors;

  /* set to how many we are loading */

  ilbm->ncolors = ncolors;

  /* how many colors to check for shifted nibbles (i.e. used colors) */

  ncheck = 1L << ilbm->Bmhd.nPlanes;

  if (ncheck > ncolors) ncheck = ncolors;

  if ((! (ilbm->IFFPFlags & IFFPF_NOCOLOR32)) && (ilbm->colorrecord))
  {
    ilbm->colorrecord [0] = ncolors << 16L;

    /* Assign to 32-bit table, examine for all-shifted nibbles at same time */

    AllShifted = TRUE;

    k = 0;

    while (ncheck--)
    {
      ilbm->colortable32 [k].r = rb = *rgb++;

      ilbm->colortable32 [k].g = gb = *rgb++;

      ilbm->colortable32 [k].b = bb = *rgb++;

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

      k++;
    }

    /* If no file/user indication that this is an 8-bit significant CMAP... */

    if ((! (ilbm->IFFPFlags & IFFPF_CMAPOK)) &&	(! (ilbm->Bmhd.flags & BMHDF_CMAPOK)))
    {
      /* If all nibbles appear shifted (4 bit), duplicate the nibbles */

      if (AllShifted)
      {
        for (k = 0; k < nc; k++)
        {
          ilbm->colortable32 [k].r |= (ilbm->colortable32 [k].r >> 4);

          ilbm->colortable32 [k].g |= (ilbm->colortable32 [k].g >> 4);

          ilbm->colortable32 [k].b |= (ilbm->colortable32 [k].b >> 4);
        }
      }
    }

    /* Now scale to 32 bits */

    for (k = 0; k < nc; k++)
    {
      gun = ilbm->colortable32 [k].r;

      ilbm->colortable32 [k].r |= ((gun << 24) | (gun << 16) | (gun << 8));

      gun = ilbm->colortable32 [k].g;

      ilbm->colortable32 [k].g |= ((gun << 24) | (gun << 16) | (gun << 8));

      gun = ilbm->colortable32 [k].b;

      ilbm->colortable32 [k].b |= ((gun << 24) | (gun << 16) | (gun << 8));
    }
  }

  /* always make old-style table */

  rgb = sp->sp_Data;

  ncolors = nc;

  k = 0;

  while (ncolors--)
  {
    r = (*rgb++ & 0xF0) << 4;

    g = *rgb++ & 0xF0;

    b = *rgb++ >> 4;

    ilbm->colortable [k] = r | g | b;

    k++;
  }

  return (NULL);
}

LONG getcolors (struct ILBMInfo *ilbm)
{
struct IFFHandle *iff;

LONG error = CLIENT_ERROR;

  if (! (iff = ilbm->ParseInfo.iff)) return (error);

  if (! (error = alloccolortable (ilbm))) error = loadcmap (ilbm);

  if (error) freecolors (ilbm);

  return (error);
}

/* alloccolortable - allocates ilbm->colortable and sets ilbm->ncolors
 *	to the number of colors we have room for in the table.
 *
 * V39 and above: unless ilbm->IFFPFlags & IFFPF_NOCOLOR32, will also
 *  allocate and build a 32-bit per gun colortable (ilbm->colortable32)
 *  and ilbm->colorrecord for LoadRGB32()
 */

LONG alloccolortable (struct ILBMInfo *ilbm)
{
struct IFFHandle *iff;

struct StoredProperty *sp;

LONG error = NULL;

ULONG ctabsize;

UWORD ncolors;

  if (! (iff = ilbm->ParseInfo.iff)) return (CLIENT_ERROR);

  if (sp = FindProp (iff,ID_ILBM,ID_CMAP))
  {
     /*
      * Compute the size table we need
      */

     ncolors = sp->sp_Size / 3;		/* how many in CMAP */

     ncolors = MAX (ncolors,16);	/* alloc at least 16 */

     ctabsize = ncolors * sizeof (Color4);

     if (ilbm->colortable = (Color4 *) AllocVec (ctabsize,MEMF_PUBLIC | MEMF_CLEAR))
     {
	ilbm->ncolors = ncolors;

	ilbm->ctabsize = ctabsize;

        if ((! (ilbm->IFFPFlags & IFFPF_NOCOLOR32)))
	{
           ctabsize = (ncolors * sizeof (Color32)) + (2 * sizeof (LONG));

	   if (ilbm->colorrecord = (ULONG *) AllocVec (ctabsize,MEMF_PUBLIC | MEMF_CLEAR))
           {
	      ilbm->crecsize = ctabsize;

	      ilbm->colortable32 = (Color32 *) (&ilbm->colorrecord [1L]);

	      ilbm->colorrecord [0L] = ncolors << 16L;	 /* For LoadRGB32 */

              ilbm->colorrecord [ncolors * sizeof (Color32) + 1L] = NULL;
           }

           else error = IFFERR_NOMEM;
        }
     }

     else error = IFFERR_NOMEM;
  }

  if (error) freecolors (ilbm);

  return (error);
}

VOID freecolors (struct ILBMInfo *ilbm)
{
   if (ilbm->colortable) FreeVec (ilbm->colortable);

   ilbm->colortable = NULL;

   ilbm->ctabsize = 0;

   if (ilbm->colorrecord) FreeVec (ilbm->colorrecord);

   ilbm->colorrecord  = NULL;

   ilbm->colortable32 = NULL;

   ilbm->crecsize = 0;
}

LONG currentchunkis (struct IFFHandle *iff,LONG type,LONG id)
{
register struct ContextNode *cn;

LONG result = 0;

  if (cn = CurrentChunk (iff))

    if ((cn->cn_Type == type) && (cn->cn_ID == id)) result = 1;

  return (result);
}

/*---------- loadbody ---------------------------------------------------*/

LONG loadbody (struct IFFHandle *iff,struct BitMap *bitmap,BitMapHeader *bmhd)
{
BYTE *buffer;

ULONG bufsize;

LONG error = 1L;

  if (! (currentchunkis (iff,ID_ILBM,ID_BODY))) return (IFF_OKAY);

  if ((bitmap) && (bmhd))
  {
     bufsize = MaxPackedSize (RowBytes (bmhd->w)) << 4L;

     if (! (buffer = AllocVec (bufsize,0L))) return (IFFERR_NOMEM);

     error = loadbody2 (iff,bitmap,NULL,bmhd,buffer,bufsize);

     FreeVec (buffer);
  }

  return (error);
}

LONG loadbody2 (struct IFFHandle *iff,struct BitMap *bitmap,BYTE *mask,BitMapHeader *bmhd,BYTE *buffer,ULONG bufsize)
{
UBYTE srcPlaneCnt = bmhd->nPlanes;   /* Haven't counted for mask plane yet*/

WORD srcRowBytes = RowBytes (bmhd->w);

WORD destRowBytes = bitmap->BytesPerRow;   /* used as a modulo only */

LONG bufRowBytes = MaxPackedSize (srcRowBytes);

WORD nRows = bmhd->h;

WORD destWidthBytes;			/* used for width check */

WORD compression = bmhd->compression;

register WORD iPlane, iRow, nEmpty;

register WORD nFilled;

BYTE *buf, *nullDest, *nullBuf, **pDest;

BYTE *planes [MaxSrcPlanes]; /* array of ptrs to planes & mask */

struct ContextNode *cn;

   cn = CurrentChunk (iff);

   if (compression > cmpByteRun1) return (CLIENT_ERROR);

   /* If >=V39, this may be an interleaved bitmap with a BytesPerRow
    * which is truly just a modulo and actually includes ALL planes.
    * So instead, for bounds checking, we use the pixel width of
    * the BitMap rounded up to nearest WORD, since saved ILBMs
    * are always saved as their width rounded up to nearest WORD.
    */

   destWidthBytes = RowBytes (GetBitMapAttr (bitmap,BMA_WIDTH));

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

   if (srcRowBytes > destWidthBytes || bufsize < (bufRowBytes * 2) || srcPlaneCnt > MaxSrcPlanes) return (CLIENT_ERROR);

   if (nRows > bitmap->Rows) nRows = 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;

   /* Copy any mask plane ptr into corresponding "planes" slot.*/

   if (bmhd->masking == mskHasMask)
   {
      if (mask) 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 = nRows; iRow > 0; iRow--)
   {
       for (iPlane = 0; iPlane < srcPlaneCnt; iPlane++)
       {
 	    pDest = &planes [iPlane];

            /* 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 (cn))
		{
               	    /* There aren't enough bytes left to fill the buffer.*/

             	    nEmpty = ChunkMoreBytes (cn);

               	    bufsize = nFilled + nEmpty;  /* heh-heh */
               	}

	    	/* Append new data to the existing data.*/

            	if (ReadChunkBytes (iff,&buffer [nFilled],nEmpty) < nEmpty) return (CLIENT_ERROR);

            	buf = buffer;

	    	nFilled = bufsize;
            }

 	    /* Copy uncompressed row to destination plane.*/

            if (compression == cmpNone)
            {
            	if (nFilled < srcRowBytes)  return (IFFERR_MANGLED);

	    	movmem (buf,*pDest,srcRowBytes);

	    	buf    += srcRowBytes;

            	*pDest += destRowBytes;
            }

	    else
            {
         	/* Decompress row to destination plane.*/

            	if (UnPackRow (&buf,pDest,nFilled,srcRowBytes)) return (IFFERR_MANGLED);

                           /*  pSource, pDest, srcBytes, dstBytes  */

	    	else *pDest += (destRowBytes - srcRowBytes);

            }
	}
   }

   return (IFF_OKAY);
}

ULONG getcamg (struct ILBMInfo *ilbm)
{
struct IFFHandle *iff;

struct StoredProperty *sp;

UWORD wide,high,deep;

ULONG modeid = NULL;

  if (! (iff = ilbm->ParseInfo.iff)) return (NULL);

  wide = ilbm->Bmhd.pageWidth;

  high = ilbm->Bmhd.pageHeight;

  deep = ilbm->Bmhd.nPlanes;

  /* Grab CAMG's idea of the viewmodes */

  if (sp = FindProp (iff, ID_ILBM, ID_CAMG))
  {
     modeid = (* (ULONG *) sp->sp_Data);

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

     if ((! (modeid & MONITOR_ID_MASK)) || ((modeid & EXTENDED_MODE) && (! (modeid & 0xFFFF0000)))) modeid &= (~(EXTENDED_MODE | SPRITES | GENLOCK_AUDIO | GENLOCK_VIDEO | VP_HIDE));

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

     if ((modeid & 0xFFFF0000) && (! (modeid & 0x00001000))) sp = NULL;
  }

  if (! sp)
  {
     /*
      * No CAMG (or bad CAMG) present; use computed modes.
      */

     modeid = NULL;		/* added in 39.6 */

     if (wide >= 640) modeid = HIRES;

     if (high >= 400) modeid |= LACE;

     if (deep == 6)
     {
        modeid |= ilbm->EHB ? EXTRA_HALFBRITE : HAM;
     }
  }

  if (ilbm->IFFPFlags & IFFPF_NOMONITOR) modeid &= (~MONITOR_ID_MASK);

  return (modeid);
}

LONG chkcnt (LONG *TaggedArray)
{
LONG k = 0;

  while (TaggedArray [k] != TAG_END) k++;

  return (k >> 1);
}

LONG getcontext (struct IFFHandle *iff)
{
LONG error;

  /* Based on our parse initialization, ParseIFF() will return on a stop chunk
   * (error = 0) or end of context for an ILBM FORM (error = IFFERR_EOC) or end of
   * file (error = IFFERR_EOF)
   */

  return (error = ParseIFF (iff,IFFPARSE_SCAN));
}

LONG parseifile (struct	ParseInfo *pi,LONG groupid,LONG grouptype,LONG *propchks,LONG *collectchks,LONG *stopchks)
{
struct IFFHandle *iff;

register struct ContextNode *cn;

LONG error;

  if (! (iff = pi->iff)) return (CLIENT_ERROR);

  if (! iff->iff_Stream) return (IFFERR_READ);

  pi->hunt = FALSE;

  /* Declare property, collection and stop chunks.*/

  if (propchks)

	  if (error = PropChunks (iff, propchks, chkcnt (propchks))) return (error);

  if (collectchks)

          if (error = CollectionChunks (iff, collectchks, chkcnt (collectchks))) return (error);

  if (stopchks)

          if (error = StopChunks (iff, stopchks, chkcnt (stopchks))) return (error);

	/* We want to stop at the end of an ILBM context. */

  if (grouptype)

	  if (error = StopOnExit (iff, grouptype, groupid)) return (error);

	/* Take first parse step to enter main chunk. */

  if (error = ParseIFF (iff,IFFPARSE_STEP)) return (error);

	/* Test the chunk info to see if simple form of type we want (ILBM).*/

  if (! (cn = CurrentChunk (iff))) return (NOFILE); /* This really should never happen.  If it does, it means our parser is broken. */

  if (cn->cn_ID != groupid || cn->cn_Type != grouptype) pi->hunt = TRUE; /* Warning - this is a complex file */

  if (! error) error = getcontext (iff);

  return (error);
}

LONG contextis (struct IFFHandle *iff,LONG type,LONG id)
{
register struct ContextNode *cn;

LONG result = NULL;

  if (cn = (CurrentChunk (iff)))
  {
    if (cn = (ParentChunk (cn)))
    {
      if ((cn->cn_Type == type) && (cn->cn_ID == id)) result = 1L;
    }
  }

  return (result);
}

UBYTE *findpropdata (struct IFFHandle *iff,LONG type,LONG id)
{
register struct StoredProperty *sp;

  if (sp = FindProp (iff,type,id)) return (sp->sp_Data);

  return (0);
}

LONG setcolors (struct ILBMInfo *ilbm,struct ViewPort *vp)
{
LONG ncolors;

  if (! vp) return (CLIENT_ERROR);

  ncolors = MIN (ilbm->ncolors,vp->ColorMap->Count);

  if ((! (ilbm->IFFPFlags & IFFPF_NOCOLOR32)) && (ilbm->colorrecord))

     LoadRGB32 (vp,ilbm->colorrecord);

  else

     if (ilbm->colortable) LoadRGB4 (vp,(UWORD *) ilbm->colortable,ncolors);

  return (NULL);
}

/**********  ByteRun1 ***************************************************/

static BYTE *PutDump (BYTE *dest, int nn)
{
  LONG i;

  PutByte (nn-1);

  for (i = 0;  i < nn;  i++) PutByte (PackBuffer [i]);

  return (dest);
}

static BYTE *PutRun (BYTE *dest, int nn, int cc)
{
  PutByte (-(nn-1));

  PutByte (cc);

  return( dest);
}

/*----------- packrow --------------------------------------------------*/

/* Given POINTERS TO POINTERS, packs one row, updating the source and
 * destination pointers.  RETURNs count of packed bytes. */

LONG PackRow (BYTE **pSource, BYTE **pDest, LONG rowSize)
{
BYTE *source, *dest;
char c,lastc;
BOOL mode = DUMP;
WORD nbuf;       /* number of chars in buffer */
WORD rstart = 0; /* buffer index current run starts */

  source = *pSource;

  dest = *pDest;

  PackPutSize = 0;

  PackBuffer [0] = lastc = c = GetByte ();  /* so have valid lastc */

  nbuf = 1;   rowSize--;	/* since one byte eaten.*/

  for (;  rowSize;  --rowSize)
  {
    PackBuffer [nbuf++] = c = GetByte ();

    switch (mode)
    {
      case DUMP:

        /* If the buffer is full, write the length byte, then the data */
        if (nbuf>MaxDat)
        {
          OutDump(nbuf-1);
          PackBuffer[0] = c;
          nbuf = 1;
          rstart = 0;
          break;
        }

        if (c == lastc)
        {
          if (nbuf-rstart >= MinRun)
          {
            if (rstart > 0)
              OutDump(rstart);
            mode = RUN;
          }
			    else
            if (rstart == 0)
              mode = RUN; /* no dump in progress, so can't lose by making these 2 a run.*/
        }
        else
          rstart = nbuf-1;		/* first of run */
        break;

      case RUN:

        if ( (c != lastc)|| ( nbuf-rstart > MaxRun))
        {
          /* output run */
          OutRun(nbuf-1-rstart,lastc);
          PackBuffer[0] = c;
          nbuf = 1;
          rstart = 0;
          mode = DUMP;
        }
        break;
    }

    lastc = c;
  }

  switch (mode)
  {
    case DUMP: OutDump (nbuf);

               break;

    case RUN:  OutRun (nbuf-rstart,lastc);

               break;
  }

  *pSource = source;

  *pDest = dest;

  return (PackPutSize);
}

BOOL UnPackRow (BYTE **pSource, BYTE **pDest, WORD srcBytes0, WORD dstBytes0)
{
  register BYTE *source = *pSource;
  register BYTE *dest   = *pDest;
  register WORD n;
  register WORD srcBytes = srcBytes0;
  register WORD dstBytes = dstBytes0;
  BOOL error = TRUE;	/* assume error until we make it through the loop */
  WORD minus128 = -128;  /* get the compiler to generate a CMP.W */
  register BYTE c;

  while( dstBytes > 0 )
  {
    if ( (srcBytes -= 1) < 0 )
    {
      *pSource = source;
      *pDest = dest;
      return(error);
    }
    n = UGetByte();

    if (n >= 0)
    {
      n += 1;
      if ( (srcBytes -= n) < 0 )
      {
        *pSource = source;
        *pDest = dest;
        return(error);
      }
      if ( (dstBytes -= n) < 0 )
      {
        *pSource = source;
        *pDest = dest;
        return(error);
      }
      do
      {
        UPutByte(UGetByte());
      } while (--n > 0);
    }
    else
    if (n != minus128)
    {
      n = -n + 1;
      if ( (srcBytes -= 1) < 0 )
      {
        *pSource = source;
        *pDest = dest;
        return(error);
      }
      if ( (dstBytes -= n) < 0 )
      {
        *pSource = source;
        *pDest = dest;
        return(error);
      }
      c = UGetByte();
      do
      {
        UPutByte(c);
      } while (--n > 0);
    }
  }
  error = FALSE;	/* success! */
  *pSource = source;
  *pDest = dest;
  return(error);
}

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

/* queryilbm
 *
 * Passed an initilized ILBMInfo with a not-in-use IFFHandle,
 *   and a filename,
 *   will open an ILBM, fill in ilbm->camg and ilbm->bmhd,
 *   and close the ILBM.
 *
 * This allows you to determine if the ILBM is a size and
 *   type you want to deal with.
 *
 * Returns 0 for success or an IFFERR (libraries/iffparse.h)
 */

LONG QueryMandPic (struct ILBMInfo *ilbm,struct MandelChunk **ManChk,UBYTE *filename)
{
LONG error;

BitMapHeader *bmhd;

  if (! (ilbm->ParseInfo.iff)) return (CLIENT_ERROR);

  error = openifile (&(ilbm->ParseInfo),filename,IFFF_READ);

  if (! error)
  {
     error = parseifile (&(ilbm->ParseInfo),ID_FORM,ID_ILBM,ilbm->ParseInfo.propchks,ilbm->ParseInfo.collectchks,ilbm->ParseInfo.stopchks);

     if ((! error) || (error == IFFERR_EOC) || (error == IFFERR_EOF))
     {
	if (contextis (ilbm->ParseInfo.iff,ID_ILBM,ID_FORM))
	{
	   if (*ManChk = (struct MandelChunk *) findpropdata (ilbm->ParseInfo.iff,ID_ILBM,ID_MAND))
	   {
              if (bmhd = (BitMapHeader *) findpropdata (ilbm->ParseInfo.iff,ID_ILBM,ID_BMHD))
              {
                 *(&ilbm->Bmhd) = *bmhd;

                 ilbm->camg = getcamg (ilbm);
              }

              else error = NOFILE;
	   }

	   else error = NOMAND;
	}

	else error = NOFILE;
     }

     closeifile (&(ilbm->ParseInfo));
  }

  return (error);
}

/* LoadMandPic
 *
 * Passed a not-in-use IFFHandle, an initialized ILBMInfo, and filename,
 *   will load an ILBM into your already opened ilbm->scr, setting up
 *   ilbm->Bmhd, ilbm->camg, ilbm->colortable, and ilbm->ncolors
 *   and loading the colors into the screen's viewport
 *
 *   Note that ncolors may be more colors than you can LoadRGB4.
 *   Use MIN(ilbm->ncolors,vp->ColorMap->Count) for color count if
 *   you change the colors yourself using 1.3/2.0 functions.
 *
 * V39 - unless ilbm->IFFPFlags & IFFPF_NOCOLOR32, will do 32-bit
 *   color load under V39 and higher
 *
 * Returns 0 for success or an IFFERR (libraries/iffparse.h)
 *
 * NOTE - LoadMandPic () keeps the IFFHandle open so you can copy
 *   or examine other chunks.  You must call closeifile(iff,ilbm)
 *   to close the file and deallocate the parsed context
 *
 */

LONG LoadMandPic (struct ILBMInfo *ilbm,UBYTE *filename)
{
struct BitMap *TmpBM;

LONG error;

  if (! (ilbm->ParseInfo.iff)) return (CLIENT_ERROR);

  if (! ilbm->scr) return (CLIENT_ERROR);

  if (! (ilbm->vp)) ilbm->vp = &ilbm->scr->ViewPort;

  error = openifile (&(ilbm->ParseInfo),filename,IFFF_READ);

  if (! error)
  {
     error = parseifile (&(ilbm->ParseInfo),ID_FORM,ID_ILBM,ilbm->ParseInfo.propchks,ilbm->ParseInfo.collectchks,ilbm->ParseInfo.stopchks);

     if ((! error) || (error == IFFERR_EOC) || (error == IFFERR_EOF))
     {
        if (contextis (ilbm->ParseInfo.iff,ID_ILBM,ID_FORM))
        {
           if (GetBitMapAttr (ilbm->wrp->BitMap,BMA_FLAGS) & BMF_STANDARD)
           {
              if (! (error = loadbody (ilbm->ParseInfo.iff,ilbm->wrp->BitMap,&ilbm->Bmhd)))

              if (! (getcolors (ilbm)))
              {
                 setcolors (ilbm,ilbm->vp);

                 freecolors (ilbm);
              }
           }

           else
           {
              if (TmpBM = AllocBitMap (ilbm->win->Width,ilbm->win->Height,ilbm->wrp->BitMap->Depth,BMF_INTERLEAVED | BMF_CLEAR | BMF_MINPLANES,NULL))
              {
                 if (! (error = loadbody (ilbm->ParseInfo.iff,TmpBM,&ilbm->Bmhd)))

                    BltBitMapRastPort (TmpBM,ilbm->win->LeftEdge,ilbm->win->TopEdge,ilbm->wrp,ilbm->win->LeftEdge,ilbm->win->TopEdge,ilbm->win->Width,ilbm->win->Height,0xC0);

                 FreeBitMap (TmpBM);

                 if (! (getcolors (ilbm)))
                 {
                    setcolors (ilbm,ilbm->vp);

                    freecolors (ilbm);
                 }
              }
           }
        }

        else error = NOFILE;
     }

     closeifile (&(ilbm->ParseInfo));
  }

  return (error);
}

LONG SaveMandPic (struct ILBMInfo *ilbm,struct Chunk *chunklist1,struct Chunk *chunklist2,UBYTE *filename)
{
struct BitMap *TmpBM;

Color32 *colortable32;

UWORD count;

ULONG modeid;

LONG error = IFFERR_NOMEM;

  modeid = GetVPModeID (ilbm->vp);

  count = ilbm->vp->ColorMap->Count;

  if (colortable32 = (Color32 *) AllocVec (sizeof (Color32) * count,MEMF_CLEAR))
  {
     if (GetBitMapAttr (ilbm->wrp->BitMap,BMA_FLAGS) & BMF_STANDARD)
     {
        GetRGB32 (ilbm->vp->ColorMap,0L,count,(ULONG *) colortable32);

        error = saveilbm (ilbm,ilbm->wrp->BitMap,modeid,ilbm->win->Width,ilbm->win->Height,ilbm->win->Width,ilbm->win->Height,colortable32,count,32,mskNone,0,chunklist1,chunklist2,filename);
     }

     else
     {
        if (TmpBM = AllocBitMap (ilbm->win->Width,ilbm->win->Height,ilbm->wrp->BitMap->Depth,BMF_INTERLEAVED | BMF_CLEAR | BMF_MINPLANES,NULL))
        {
           GetRGB32 (ilbm->vp->ColorMap,0L,count,(ULONG *) colortable32);

           BltBitMap (ilbm->wrp->BitMap,ilbm->win->LeftEdge,ilbm->win->TopEdge,TmpBM,ilbm->win->LeftEdge,ilbm->win->TopEdge,ilbm->win->Width,ilbm->win->Height,0xC0,0xFF,NULL);

           error = saveilbm (ilbm,TmpBM,modeid,ilbm->win->Width,ilbm->win->Height,ilbm->win->Width,ilbm->win->Height,colortable32,count,32,mskNone,0,chunklist1,chunklist2,filename);

           FreeBitMap (TmpBM);
        }
     }
     FreeVec (colortable32);
  }

  if (! error) PutDiskObject (filename,&ILBMobject);

  return (error);
}

LONG LoadPalette (struct ILBMInfo *ilbm,UBYTE *filename)
{
LONG error;

  error = openifile (&(ilbm->ParseInfo),filename,IFFF_READ);

  if (! error)
  {
     error = parseifile (&(ilbm->ParseInfo),ID_FORM,ID_ILBM,ilbm->ParseInfo.propchks,ilbm->ParseInfo.collectchks,ilbm->ParseInfo.stopchks);

     if ((! error) || (error == IFFERR_EOC) || (error == IFFERR_EOF))
     {
        if (! (getcolors (ilbm)))
        {
           setcolors (ilbm,ilbm->vp);

           freecolors (ilbm);
        }
     }

     closeifile (&(ilbm->ParseInfo));
  }

  return (error);
}

LONG SavePalette (struct ILBMInfo *ilbm,struct Chunk *chunklist,UBYTE *filename)
{
struct IFFHandle *iff;

struct Chunk *chunk;

Color32 *colortable32;

UWORD ncolors;

LONG size,error;

ULONG chunkID;

  iff = ilbm->ParseInfo.iff;

  ncolors = ilbm->vp->ColorMap->Count;

  error = openifile (&(ilbm->ParseInfo),filename,IFFF_WRITE);

  if (! error)
  {
    error = PushChunk (iff,ID_ILBM,ID_FORM,IFFSIZE_UNKNOWN);

    if (colortable32 = (Color32 *) AllocVec (sizeof (Color32) * ncolors,MEMF_CLEAR))
    {
       GetRGB32 (ilbm->vp->ColorMap,0L,ncolors,(ULONG *) colortable32);

       CkErr (putcmap (iff,colortable32,ncolors,32));

       FreeVec (colortable32);
    }

    for (chunk = chunklist; chunk; chunk = chunk->ch_Next)
    {
      chunkID = chunk->ch_ID;

      if ((chunkID != ID_BMHD) && (chunkID != ID_CMAP) && (chunkID != ID_CAMG))
      {
        size = chunk->ch_Size == IFFSIZE_UNKNOWN ? strlen (chunk->ch_Data) : chunk->ch_Size;

        CkErr (PutCk (iff,chunkID,size,chunk->ch_Data));
      }
    }

    CkErr (PopChunk (iff));	/* close out the FORM */

    closeifile (&(ilbm->ParseInfo));	/* and the file */
  }

  return (error);
}
