
/*
 * JIFF.H
 */

#define XMAX 640
#define LOXMAX 320
#define YMAX 200
#define XASPECT 5
#define YASPECT 11

/*
 * EA handy make a long from 4 chars macros redone to work with Aztec
 */
#define MAKE_ID(a, b, c, d)\
( ((long)(a)<<24) | ((long)(b)<<16) | ((long)(c)<<8) | (long)(d) )

/*
 * These are the IFF types I deal with
 */
#define FORM MAKE_ID('F', 'O', 'R', 'M')
#define ILBM MAKE_ID('I', 'L', 'B', 'M')
#define BMHD MAKE_ID('B', 'M', 'H', 'D')
#define CMAP MAKE_ID('C', 'M', 'A', 'P')
#define BODY MAKE_ID('B', 'O', 'D', 'Y')

/*
 * And these are the IFF types I ignore but don't squawk about
 */
#define GRAB MAKE_ID('G', 'R', 'A', 'B')
#define DEST MAKE_ID('D', 'E', 'S', 'T')
#define SPRT MAKE_ID('S', 'P', 'R', 'T')
#define CAMG MAKE_ID('C', 'A', 'M', 'G')
#define CRNG MAKE_ID('C', 'R', 'N', 'G')
#define CCRT MAKE_ID('C', 'C', 'R', 'T')

#define EVEN(x) (((x) + 1) & ~1)
#define MANDEL

/*
 * Some macros for raster memory allocation ... redefine if you're
 * sensible and manage memory locally
 */

#ifndef MANDEL

/*
 * ralloc - raster alloc
 */
# define ralloc(amount)  (PLANEPTR)AllocMem((long)(amount), MEMF_CHIP)
/*
 * rfree - raster free
 */
# define rfree(pt, amount)  FreeMem( (pt), (long)(amount) )

#else				/* MANDEL */

# include <mandel.h>

/*
 * We don't want to allocate a complete raster for the picture, since we
 * already have a screen with a window where we want to have it.
 * Therefore, we allocate some small buffers, which get blitted into our
 * window as soon as they fill up.
 */
# define MAXPLANESIZE	1040L
/*
 * ralloc - raster alloc
 */
# define ralloc(amount) (PLANEPTR)AllocMem(MAXPLANESIZE, MEMF_CHIP | MEMF_CLEAR)
/*
 * rfree - raster free
 */
# define rfree(pt, amount)  FreeMem( (pt), MAXPLANESIZE )

#endif				/* !MANDEL */

/*
 * line_bytes = the number of words * 2 (for bytes) a raster line takes up
 */
#define line_bytes(width)   ((((width) + 15) >> 3) & ~0x0001)

/*
 * psize - plane size in bytes (an even number) of a raster given width
 * and height
 */
#define psize(width, height) ( line_bytes(width)*height)

/*
 * The place to throw excess bits
 */
#define bit_bucket(file, length) fseek(file, (long)EVEN(length), 1)


union bytes4 {
    char	    b4_name[4];
    LONG	    b4_type;
};

struct iff_chunk {
    union bytes4    iff_type;
    LONG	    iff_length;
};

struct form_chunk {
    union bytes4    fc_type;	/* == FORM */
    LONG	    fc_length;
    union bytes4    fc_subtype;
};

struct CommodoreAmiga {
    struct iff_chunk camg_iffc; /* == CAMG */
    LONG	    camg_data;
};

#ifndef MANDEL			/* We have this already in Mandel.h */
struct BitMapHeader {
    UWORD	    w,
		    h;
    UWORD	    x,
		    y;
    UBYTE	    nPlanes;
    UBYTE	    masking;
    UBYTE	    compression;
    UBYTE	    pad1;
    UWORD	    transparentColor;
    UBYTE	    xAspect,
		    yAspect;
    WORD	    pageWidth,
		    pageHeight;
};

/*
 * ILBM_info is the structure win_read_iff returns, and is hopefully all
 * you need to deal with out of the iff reader routines below
 */
struct ILBM_info {
    struct BitMapHeader header;
    UBYTE	    cmap[MAXCOL * 3];
    struct BitMap   bitmap;
#ifdef MANDEL
    struct Mand    *mand;
    long	    mandsize;
#endif
};

#endif				/* !MANDEL */

/*-
 * I sure wish C function "prototypes" were real and not just ANSI
 *
 * extern struct ILBM_info *win_read_iff();
 * win_read_iff( char *filename, short just_colors, int MandSize,
 *		  APTR MandPointer );
 * extern void free_planes();  free_planes( struct BitMap *bitmap);
 * extern int write_iff();
 * write_iff(char *name, unsigned char *colors, struct Window *window,
 *  short xoff, short yoff, short compressed);
 */

/*
 * Anyone know where some useful minterms are defined?
 */
#define COPY_MINTERM	    0x0C0L

/***

	A meditation for the guru from the Diamond Sutra -

	So shall you think of all this fleeting world:
	A star at dawn, a bubble in a stream;
	A flash of lightning in a summer cloud,
	A flickering lamp, a phantom, and a dream.

***/


/*
 * jiff.c   Jim Kent's iff - ilbm  reader
 *
 * This is the (sortof) short (sortof) simple no-frills IFF reader to get
 * something out of DPaint, Images, or the Animator.  It works well with
 * the Aztec C compiler.  It should work with Lattice but you never know
 * until you try it.	I haven't.
 *
 * I've included a simple main program.  This is just to make it stand alone.
 * Since amiga screen initializations are massive, all it does as is is
 * read it into a BitMap, and then free up the BitMap. Should crash it if
 * it's gonna crash though.
 *
 * The main interface to this is through the routine win_read_iff(filename).
 * This returns a ILBM_info structure-pointer on success, and NULL on
 * failure.  It cleans up after itself on failure.
 *
 * I hope you will find this useful and easy to use.  Please forgive my funky
 * indentation style?  Well at least I'm consistent! (* Run through the
 * C-Beautifier by Olaf Seibert !! *)
 *
 * To demonstrate what a nice guy I am even though I'm far from wild about
 * the IFF standard I'm placing this in the public domain.  When you
 * remove the DEBUG and PARANOID definitions the code is only 1536 bytes
 * long.
 *
 * -Jim Kent  April 22, 1986
 */


#include <stdio.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <libraries/dos.h>
#include <intuition/intuition.h>
/* #include "jiff.h"    */


/*
 * This is an all too common state of software development.  Get rid of
 * this define as soon as it runs.
 */
#undef DEBUG
/* #define DEBUG/* */

/*
 * This is the normal state of software development.  Seriously undefine
 * this to make it shut up about errors and reduce code size half way
 * through beta testing...
 */
#undef PARANOID

/*
 * This is nice if you want to use a debugger on the STATIC data and
 * routines in this file. Redefine only if you don't need a debugger.
 */

#ifdef DEBUG
# undef STATIC
# define STATIC
#endif

STATIC struct ILBM_info *win_read_ilbm();
STATIC struct ILBM_info *win_read_body();

/*
 * OK this code is almost re-entrant.  Pass this guy from above to make it
 * really re-entrant.  (Why do you need a reentrant ILBM reader though??
 * Maybe for Dale ... ) Well, look in the IFF specs for instance... [Olaf
 * Seibert, KosmoSoft]
 */
STATIC struct ILBM_info root_info;	/* static so get initialized to
					 * zero */


#ifdef PARANOID
/*
 * a little paranoid routine that say's where we got before EOF
 */
STATIC void
iff_truncated(where)
int		where;
{
    printf("ILBM truncated %d\n", where);
    free_planes(&root_info.bitmap);
}

#endif PARANOID


struct ILBM_info *
win_read_iff(name, just_colors, window, MandSize, MandPointer)
char	       *name;
short		just_colors;
struct Window  *window;
int		MandSize;
APTR		MandPointer;
{
    struct ILBM_info *info = &root_info;
    FILE	   *file;
    struct form_chunk chunk;

    if ((file = fopen(name, "r")) == 0) {
#ifdef PARANOID
	printf("couldn't fopen %s to read\n", name);
#endif PARANOID
	return NULL;
    }
    if (fread(&chunk, sizeof (struct form_chunk), 1, file) != 1) {
#ifdef PARANOID
	iff_truncated(0);
#endif PARANOID
	fclose(file);
	return NULL;
    }
    if (chunk.fc_type.b4_type != FORM) {
#ifdef PARANOID
	printf("not a FORM - %s\n", name);
#endif PARANOID
	fclose(file);
	return NULL;
    }
    if (chunk.fc_subtype.b4_type != ILBM) {
#ifdef PARANOID
	printf("FORM not an ILBM - %s\n", name);
#endif PARANOID
	fclose(file);
	return NULL;
    }
#ifdef DEBUG
    printf("FORM %ld ILBM\n", chunk.fc_length);
#endif DEBUG

#ifdef MANDEL
    info->mand = (struct Mand *) MandPointer;
    info->mandsize = MandSize;
    info->mand->MandID = 0;	/* we have not yet read it */
#endif

    info = win_read_ilbm(file, info, chunk.fc_length - sizeof (chunk),
			 just_colors, window);
#ifdef DEBUG
    printf("info = %lx\n", info);
#endif DEBUG

#ifdef MANDEL
    /*
     * Backward compatibility with non-standard code: We may want to read
     * the extra MAND if we have not read it by now.
     */
    if (info && MandSize && info->mand->MandID != MAND) {
	fread(MandPointer, 1, MandSize, file);
	info->mand->Size -= 2 * sizeof (long);  /* patch bug */
    }
#endif MANDEL

    fclose(file);

    return info;
}

STATIC struct ILBM_info *
win_read_ilbm(file, info, length, just_colors, window)
FILE	       *file;
struct ILBM_info *info;
long		length;
short		just_colors;
struct Window  *window;
{
    struct iff_chunk chunk;
    int 	    i;
    long	    read_in = 0;
    int 	    got_header = FALSE; /* To make sure gots the header
					 * first */
    int 	    got_cmap = FALSE;	/* Make sure get cmap before
					 * "BODY" */

    /*
     * Make sure the Planes are all NULL so can free up memory easily on
     * error abort
     */
    for (i = 0; i < 8; i++)
	info->bitmap.Planes[i] = NULL;

    while (read_in < length) {
	if (fread(&chunk, sizeof (chunk), 1, file) != 1) {
#ifdef PARANOID
	    iff_truncated(1);
#endif PARANOID
	    return NULL;
	}
	switch (chunk.iff_type.b4_type) {
	case BMHD:
#ifdef DEBUG
	    printf("\tBMHD %ld\n", chunk.iff_length);
#endif DEBUG
	    if (fread(&info->header, sizeof (info->header), 1, file) != 1) {
#ifdef PARANOID
		iff_truncated(2);
#endif PARANOID
		return NULL;
	    }
	    got_header = TRUE;
	    break;
	case CMAP:
#ifdef DEBUG
	    printf("\tCMAP %ld\n", chunk.iff_length);
#endif DEBUG
	    if (!got_header) {
#ifdef PARANOID
		printf("CMAP before BMHD\n");
#endif PARANOID
		return NULL;
	    }
	    if (chunk.iff_length <= 3 * MAXCOL) {
		if (fread(info->cmap, (int) chunk.iff_length, 1, file) != 1) {
#ifdef PARANOID
		    iff_truncated(3);
#endif PARANOID
		    return NULL;
		}
	    } else {
#ifdef PARANOID
		printf("warning, more than %d colors in ILBM CMAP\n",
		       MAXCOL);
#endif PARANOID
		if (fread(info->cmap, (int) 3 * MAXCOL, 1, file) != 1) {
#ifdef PARANOID
		    iff_truncated(4);
#endif PARANOID
		    return NULL;
		}
		bit_bucket(file, chunk.iff_length - 3 * MAXCOL);
	    }
	    got_cmap = TRUE;
	    if (just_colors)
		return info;
	    break;
	case MAND:
#ifdef DEBUG
	    printf("\tMAND %ld\n", chunk.iff_length);
#endif DEBUG
	    if (chunk.iff_length + sizeof (chunk) <= info->mandsize) {
		if (fread((char *) info->mand + sizeof (chunk),
			  (int) chunk.iff_length, 1, file) != 1) {
#ifdef DEBUG
		    printf("fread MAND fails; filepos %ld ferror %d\n",
			   (long) ftell(file), (int) ferror(file));
#endif
		    return NULL;
		}
		*(struct iff_chunk *) info->mand = chunk;

		/*
		 * skip padding byte
		 */
		if (chunk.iff_length & 1)
		    getc(file);
	    } else {
#ifdef DEBUG
		printf("skipping MAND; too large for buffer (%ld)\n",
		       (long) info->mandsize);
#endif
		bit_bucket(file, chunk.iff_length);
	    }
	    break;
	case BODY:
	    if (!got_cmap) {
#ifdef PARANOID
		printf("BODY before CMAP\n");
#endif PARANOID
		return NULL;
	    }
#ifdef DEBUG
	    printf("\tBODY %ld\n", chunk.iff_length);
#endif DEBUG
	    return win_read_body(file, info, chunk.iff_length, window);

	default:		/* Squawk about unknown types if PARANOID */
#ifdef PARANOID
	    printf("\t unknown type %lx of b4_type\n", chunk.iff_type.b4_type);
	case GRAB:		/* Ignore documented but unwanted types */
	case DEST:
	case SPRT:
	case CAMG:
	case CRNG:
	case CCRT:
#endif PARANOID
	    bit_bucket(file, chunk.iff_length);
	    break;
	}
	read_in += EVEN(chunk.iff_length) + sizeof (chunk);
    }
#ifdef PARANOID
    printf("no BODY in ILBM\n");
#endif PARANOID
    return NULL;
}



STATIC struct ILBM_info *
win_read_body(file, info, length, window)
FILE	       *file;
register struct ILBM_info *info;
long		length;
struct Window  *window;
{
    struct ILBM_header *header;
    struct BitMap  *bm;
    int 	    i,
		    j;
    int 	    rlength;
    int 	    plane_offset;
    ULONG	    YSize,
		    DestX,
		    DestY;

#ifdef DEBUG
    printf("win_read_body( %lx %lx %ld)\n", file, info, length);
#endif DEBUG

#ifdef PARANOID
    /*
     * When paranoid do a little error checking first ... fail fast!
     */
    if (info->header.nPlanes > 8) {
	printf("Whoa, woe  Dale only speaks 8 planes boy, not %d\n",
	       info->header.nPlanes);
	return NULL;
    }
#endif PARANOID

    /*
     * Ok a little more error checking
     */
    if (info->header.compression != 0 && info->header.compression != 1) {
#ifdef PARANOID
	printf("unrecognized compression type %d\n", info->header.compression);
#endif PARANOID
	return NULL;
    }
    /*
     * Set up the bitmap part that doesn't involve memory allocation first
     * - hey this part does get done, and let's be optimistic...
     */
    info->bitmap.BytesPerRow = line_bytes(info->header.w);
    info->bitmap.Rows = info->header.h;
    info->bitmap.Depth = info->header.nPlanes;
    info->bitmap.Flags = info->bitmap.pad = 0;

    rlength = info->bitmap.Rows * info->bitmap.BytesPerRow;

    for (i = 0; i < info->header.nPlanes; i++) {
	if ((info->bitmap.Planes[i] = ralloc(rlength)) == NULL) {
#ifdef PARANOID
	    printf("couldn't alloc plane %d in win_read_body\n", i);
#endif PARANOID
	    free_planes(&info->bitmap);
	    return NULL;
	}
    }

    plane_offset = 0;
    YSize = (MAXPLANESIZE / info->bitmap.BytesPerRow);
    if (window->Flags & GIMMEZEROZERO) {
	DestX = 0;
	DestY = 0;
    } else {
	DestX = window->BorderLeft;
	DestY = window->BorderTop;
    }

    for (i = 0; i < info->bitmap.Rows; i++) {
	/*
	 * This test should be in the inner loop for shortest code, in the
	 * outer loop for greatest speed, so sue me I compromised
	 */
	if (info->header.compression == 0) {
	    for (j = 0; j < info->bitmap.Depth; j++) {
		if (fread(info->bitmap.Planes[j] + plane_offset,
			  info->bitmap.BytesPerRow, 1, file) != 1) {
#ifdef PARANOID
		    iff_truncated(6);
#endif PARANOID
		    free_planes(&info->bitmap);
		    return NULL;
		}
	    }
	} else {
	    register char  *dest,
			    value;
	    register int    so_far,
			    count;	/* How much have unpacked so far */

	    for (j = 0; j < info->bitmap.Depth; j++) {
		so_far = info->bitmap.BytesPerRow;
		dest = (char *) info->bitmap.Planes[j] + plane_offset;
		while (so_far > 0) {
		    if ((value = getc(file)) == 128) {
#ifdef DEBUG
			printf("NOP\n");
#endif DEBUG
		    } else if (value > 0) {
			count = (int) value + 1;
			so_far -= count;
			if (fread(dest, count, 1, file) != 1) {
#ifdef PARANOID
			    iff_truncated(7);
#endif PARANOID
			    free_planes(&info->bitmap);
			    return NULL;
			}
			dest += count;
		    } else {
			count = (int) -value + 1;
			so_far -= count;
			value = getc(file);
			while (--count >= 0)    /* This is fastest loop on
						 * the 68000 */
			    *dest++ = value;
		    }
		}
		if (so_far != 0) {
#ifdef PARANOID
		    printf("compression quite screwed up, aborting %d\n", so_far);
#endif PARANOID
		    free_planes(&info->bitmap);
		    return NULL;
		}
	    }
	}
	plane_offset += info->bitmap.BytesPerRow;

	if (plane_offset > MAXPLANESIZE - info->bitmap.BytesPerRow) {
	    BltBitMapRastPort(&info->bitmap, 0L, 0L,
			      window->RPort, DestX, DestY,
			      info->bitmap.BytesPerRow * 8L, YSize,
			      COPY_MINTERM);
	    plane_offset = 0;
	    DestY += YSize;
	}
    }

    if (plane_offset) {
	BltBitMapRastPort(&info->bitmap, 0L, 0L,
			  window->RPort, DestX, DestY,
			  info->bitmap.BytesPerRow * 8L,
			  (long) plane_offset / info->bitmap.BytesPerRow,
			  COPY_MINTERM);
    }
    if (length & 1) {
	/*
	 * Skip padding byte
	 */
	getc(file);
    }
    free_planes(&info->bitmap);
    return info;
}


void
free_planes(bmap)
register struct BitMap *bmap;
{
    PLANEPTR	    plane;
    long	    length;
    short	    i;

    length = bmap->BytesPerRow * bmap->Rows;

    for (i = bmap->Depth; --i >= 0;) {
	if ((plane = bmap->Planes[i]) != NULL) {
	    rfree(plane, length);
	    bmap->Planes[i] = NULL;
	}
    }
}






#undef DEBUG
/* #define DEBUG/* */
#undef PARANOID

/*----------------------------------------------------------------------*
 * jpacker.c Convert data to "cmpByteRun1" run compression.
 *
 * pack_row() is an adaptation of PackRow()
 * by Jerry Morrison and Steve Shaw, Electronic Arts,
 * modified and tweaked by Jim Kent, Dancing Flame 05/02/86
 *
 *  control bytes:
 *   [0..127]	: followed by n+1 bytes of data.
 *   [-1..-127] : followed by byte to be repeated(-n)+1 times.
 *   -128	: NOOP.
 *
 *
 * write_iff() is the only function you can access in this module.
 *----------------------------------------------------------------------*/

/*-
#include <exec/types.h>
#include <graphics/gfx.h>
#include <stdio.h>
#include "jiff.h"
*/

#define DUMP	0
#define RUN 1

#define MINRUN 3
#define MAXRUN 128
#define MAXDAT 128

/*
 * pack_row - pass source line pointer, length of line, and file. Returns
 * # of bytes after compression.  Returns 0 on write error. Pass file =
 * NULL to just find out length, otherwise will write compressed row to
 * file.
 */

STATIC unsigned int
pack_row(file, source, size)
FILE	       *file;
char	       *source;
int		size;
{
    char	    c,
		    lastc = '\0';
    short	    mode = DUMP;
    short	    nbuf = 0;	/* Number of chars in buffer */
    short	    rstart = 0; /* Buffer index current run starts */
    unsigned short  putsize;

#if OLD
    char	    buf[MAXDAT * 3 / 2];	/* I think MAXDAT+1 would
						 * suffice */

#else
    /*
     * And I think that buf can be changed into a pointer to the source
     * line, to the beginning of a dump. Saves stack space and copying.
     */
    char	   *buf;

#endif

    putsize = 0;
#if OLD
    buf[0] = lastc = *source++; /* So have valid lastc */
#else
    buf = source;
    lastc = *source++;		/* So have valid lastc */
#endif
    nbuf = 1;
    size--;			/* Since one byte eaten */


    for (; size; --size) {
#if OLD
	buf[nbuf++] = c = *source++;
#else
	nbuf++;
	c = *source++;
#endif
	switch (mode) {
	case DUMP:
	    /*
	     * If the buffer is full, write the length byte, then the data
	     */
	    if (nbuf > MAXDAT) {
		if (file != NULL) {
		    if (putc(nbuf - 2, file) == EOF)
			return 0;
		    if (fwrite(buf, nbuf - 1, 1, file) != 1)
			return 0;
		}
		putsize += nbuf;
#if OLD
		buf[0] = c;
#else
		buf = source - 1;	/* Undo the previous source++ */
#endif
		nbuf = 1;
		rstart = 0;
		break;
	    }
	    if (c == lastc) {
		if (nbuf - rstart >= MINRUN) {
		    if (rstart > 0) {
			if (file != NULL) {
			    if (putc(rstart - 1, file) == EOF)
				return 0;
			    if (fwrite(buf, rstart, 1, file) != 1)
				return 0;
			}
			putsize += rstart + 1;
		    }
		    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
		 */
		if (file != NULL) {
		    if (putc(-(nbuf - rstart - 2), file) == EOF)
			return 0;
		    if (putc(lastc, file) == EOF)
			return 0;
		}
		putsize += 2;
#if OLD
		buf[0] = c;
#else
		buf = source - 1;	/* Undo the previous source++ */
#endif
		nbuf = 1;
		rstart = 0;
		mode = DUMP;
	    }
	    break;
	}

	lastc = c;
    }

    switch (mode) {
    case DUMP:
	if (file != NULL) {
	    if (putc(nbuf - 1, file) == EOF)
		return 0;
	    if (fwrite(buf, nbuf, 1, file) != 1)
		return 0;
	}
	putsize += nbuf + 1;
	break;
    case RUN:
	if (file != NULL) {
	    if (putc(-(nbuf - rstart - 1), file) == EOF)
		return 0;
	    if (putc(lastc, file) == EOF)
		return 0;
	}
	putsize += 2;
	break;
    }
    return putsize;
}

/*
 * write_row - pass source line pointer, length of line, and file. Returns
 * # of bytes after not compressing.  Returns 0 on write error. Pass file
 * = NULL to just find out length, otherwise will write non-compressed row
 * to file.
 */

STATIC unsigned int
write_row(file, source, size)
FILE	       *file;
char	       *source;
int		size;
{
    if (file) {
	if (fwrite(source, size, 1, file) != 1)
	    return 0;
    }
    return size;
}


STATIC unsigned long
pack_window(file, window, writer)
FILE	       *file;
register struct Window *window;
register unsigned int (*writer) ();

{
    unsigned short  i,
		    j;
    unsigned	    row_length;
    unsigned long   compressed_length;
    unsigned	    plane_offset;
    int 	    BytesPerRow;
    int 	    YSize;
    ULONG	    SrcX,
		    SrcY;
    struct RastPort Rp;
    struct BitMap   Bitmap;

#ifdef DEBUG
    printf("pack_window( %lx %lx)\n", file, window);
#endif DEBUG

    compressed_length = 0;
    plane_offset = 0;

    BytesPerRow = line_bytes(window->GZZWidth);
    Bitmap.Depth = window->WScreen->BitMap.Depth;
    if (window->Flags & GIMMEZEROZERO) {
	SrcX = 0;
	SrcY = 0;
    } else {
	SrcX = window->BorderLeft;
	SrcY = window->BorderTop;
    }
    YSize = MAXPLANESIZE / BytesPerRow;
    InitBitMap(&Bitmap, (ULONG) Bitmap.Depth,
	       (ULONG) (8 * BytesPerRow), (ULONG) YSize);

    /*
     * Make sure the Planes are all NULL so can free up memory easily on
     * error abort
     */
    for (i = 0; i < 8; i++)
	Bitmap.Planes[i] = NULL;

    for (i = 0; i < Bitmap.Depth; i++) {
	if ((Bitmap.Planes[i] = ralloc(MAXPLANESIZE)) == NULL) {
#ifdef DEBUG
	    printf("couldn't alloc plane %d in pack_window\n", i);
	    printf("pack_window: aborting; free_planes\n");
#endif
	    free_planes(&Bitmap);
	    return 0;
	}
    }
    InitRastPort(&Rp);
    Rp.BitMap = &Bitmap;
#ifdef DEBUG
    printf("pack_window BytesPerRow=%ld Depth=%ld\n", (long) BytesPerRow,
	   (long) Bitmap.Depth);
#endif

    for (i = 0; i < window->GZZHeight; i++) {
	if (plane_offset == 0) {
#ifdef DEBUG
	    printf("pack_window ClipBlit SrcX=%ld SrcY=%ld YSize=%ld\n",
		   SrcX, SrcY, (ULONG) YSize);
#endif
#define DestX	0L
#define DestY	0L
	    ClipBlit(window->RPort, SrcX, SrcY,
		     &Rp, DestX, DestY,
		     (ULONG) window->GZZWidth, (ULONG) YSize,
		     COPY_MINTERM);
#undef DestX
#undef DestY
	    SrcY += YSize;
	}
	for (j = 0; j < Bitmap.Depth; j++) {
	    if ((row_length = (*writer) (file, Bitmap.Planes[j] + plane_offset,
					 BytesPerRow)) == 0) {
#ifdef DEBUG
		printf("error packing row %d plane %d\n", i, j);
		printf("pack_window: aborting; free_planes\n");
#endif
		free_planes(&Bitmap);
		return 0;
	    }
	    compressed_length += row_length;
	}

	plane_offset += BytesPerRow;
	if (plane_offset > MAXPLANESIZE - BytesPerRow)
	    plane_offset = 0;
    }

#ifdef DEBUG
    printf("pack_window: free_planes\n");
#endif
    free_planes(&Bitmap);

    if (compressed_length & 1) {/* Check to see odd length */
	if (file != NULL) {
	    if (putc(0, file) == EOF) {
		return 0;
	    }
	}
	/*
	 * compressed_length++; Deleted!!! Padding should NOT be included
	 * in the chunk size !!!
	 */
    }
    return compressed_length;
}

int
write_iff(name, colors, window, xoff, yoff, compressed, MandSize, MandPointer)
char	       *name;
unsigned char  *colors;
register struct Window *window;
short		xoff,
		yoff;
short		compressed;
int		MandSize;
APTR		MandPointer;
{
    FILE	   *file;
    struct form_chunk chunk;
    struct iff_chunk ichunk;
    struct BitMapHeader header;
    long	    bits_size;
    short	    i;
    int 	    width = 0;
    int 	    Depth;
    int 	    BytesPerRow;

#ifdef DEBUG
    printf("write_iff\n");
#endif

    if ((file = fopen(name, "w")) == 0) {
#ifdef PARANOID
	printf("couldn't fopen %s to write\n", name);
#endif PARANOID
	goto abort;
    }
    /*
     * Say its a FORM ILBM
     */
    chunk.fc_type.b4_type = FORM;
    chunk.fc_subtype.b4_type = ILBM;
#ifdef MANDEL
    chunk.fc_length = 4 + 3 * sizeof (struct iff_chunk) + MAXCOL * 3 +
	sizeof (struct BitMapHeader) + sizeof (struct CommodoreAmiga) + MandSize;
#else
    chunk.fc_length = 4 + 3 * sizeof (struct iff_chunk) + MAXCOL * 3 +
	sizeof (struct BitMapHeader) + sizeof (struct CommodoreAmiga);
#endif MANDEL
    if (window) {
	width = window->GZZWidth;
	BytesPerRow = line_bytes(width);
	Depth = window->WScreen->BitMap.Depth;
	if (compressed) {
#ifdef DEBUG
	    printf("write_iff: call pack_window for sizing\n");
#endif
	    if ((bits_size = pack_window(NULL, window, pack_row)) == 0)
		goto abort;
	} else {
	    bits_size = BytesPerRow * window->GZZHeight * Depth;
	}
	chunk.fc_length += bits_size;
	if (bits_size & 1)
	    chunk.fc_length++;
    }
    if (fwrite(&chunk, sizeof (chunk), 1, file) != 1)
	goto abort;

    /*
     * Here comes a BitMapHeader
     */
    {
	struct iff_chunk ichunk;

	ichunk.iff_type.b4_type = BMHD;
	ichunk.iff_length = sizeof (header);
	if (fwrite(&ichunk, sizeof (ichunk), 1, file) != 1)
	    goto abort;
    }

    /*
     * Initialize the BitMapHeader to normal values
     */
    header.masking = 0;
    header.pad1 = 0;
    header.transparentColor = 0;
    if (compressed)
	header.compression = 1;
    else
	header.compression = 0;
    header.pageWidth = width;
    header.pageHeight = window ? window->GZZHeight : YMAX;
    header.xAspect = width > LOXMAX ? XASPECT : XASPECT * 2;
    header.yAspect = YASPECT;
    /*
     * If it's not just a color map give the dimensions of rasters
     */
    if (window) {
	header.w = width;
	header.h = window->GZZHeight;
	header.nPlanes = Depth;
	header.x = xoff;
	header.y = yoff;
    }
    if (fwrite(&header, sizeof (header), 1, file) != 1)
	goto abort;

    /*
     * Squirt out the color map
     */
    {
	struct iff_chunk ichunk;

	ichunk.iff_type.b4_type = CMAP;
	ichunk.iff_length = MAXCOL * 3;
	if (fwrite(&ichunk, sizeof (ichunk), 1, file) != 1)
	    goto abort;
	if (fwrite(colors, (int) 3 * MAXCOL, 1, file) != 1)
	    goto abort;
    }

    /*
     * Write a CAMG chunk with ViewPort modes
     */
    {
	struct CommodoreAmiga camg;

	camg.camg_iffc.iff_type.b4_type = CAMG;
	camg.camg_iffc.iff_length = sizeof (camg) - sizeof (camg.camg_iffc);
	camg.camg_data = window->WScreen->ViewPort.Modes;
	if (fwrite(&camg, sizeof (camg), 1, file) != 1)
	    goto abort;
    }

#ifdef MANDEL
    /*
     * We want to write our own private MAND chunk
     */
    if (MandSize) {
	if (fwrite(MandPointer, MandSize, 1, file) != 1)
	    goto abort;
    }
#endif MANDEL

    /*
     * If they be bits then squirt out the bits
     */
    if (window) {
	struct iff_chunk ichunk;

	ichunk.iff_type.b4_type = BODY;
	ichunk.iff_length = bits_size;
	if (fwrite(&ichunk, sizeof (ichunk), 1, file) != 1)
	    goto abort;
#ifdef DEBUG
	printf("write_iff: call pack_window for real\n");
#endif
	if (compressed) {
	    if (pack_window(file, window, pack_row) == 0)
		goto abort;
	} else {
	    if (pack_window(file, window, write_row) == 0)
		goto abort;
	}
    }
    fclose(file);

    return 1;
abort:
#ifdef DEBUG
    printf("write_iff: aborting !!!\n");
#endif
    fclose(file);
    return 0;
}


/*
 * put_ea_cmap given an ea-type color map:
 *
 * an array of unsigned chars of form ea_cmap[] = {r, g, b, r, g, b...}
 *
 * turn it into an amiga-type color map:
 *
 * an array of unsigned short of form amiga_cmap = {0xrgb, 0xrgb, ...}
 *
 * and then tell Dale this is the colors we want for our viewport
 */

void
put_ea_cmap(ea_cmap, colors, Screen)
unsigned char  *ea_cmap;
int		colors;
struct Screen  *Screen;
{
    unsigned short  amy_cmap[MAXCOL];
    register int    i;
    register short  color;

    if (colors > MAXCOL)        /* Color clipping */
	colors = MAXCOL;
    for (i = 0; i < colors; i++) {
	color = (*ea_cmap++ & 0xF0) << 4;
	color |= *ea_cmap++ & 0xF0;
	color |= (*ea_cmap++ & 0xF0) >> 4;
	amy_cmap[i] = color;
    }
    LoadRGB4(&Screen->ViewPort, amy_cmap, (long) colors);
}

void
get_ea_cmap(ea_cmap, colors, Screen)
unsigned char  *ea_cmap;
int		colors;
struct Screen  *Screen;
{
    register long   i;
    register unsigned short rgb;

    if (colors > MAXCOL)        /* Color clipping */
	colors = MAXCOL;
    for (i = 0; i < colors; i++) {
	rgb = GetRGB4(Screen->ViewPort.ColorMap, i);
	*ea_cmap++ = (rgb & 0xF00) >> 4;
	*ea_cmap++ = (rgb & 0x0F0);
	*ea_cmap++ = (rgb & 0x00F) << 4;
    }
}
