
#define DEBUG
#define 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 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 */
short putsize;
char    buf[MAXDAT*3/2];		/* I think MAXDAT+1 would suffice */

putsize = 0;
buf[0] = lastc = *source++;/* so have valid lastc */
nbuf = 1;
size--;			/* since one byte eaten. */


for (; size; --size)
	{
	buf[nbuf++] = c = *source++;
	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 (write(buf, nbuf-1, 1, file) != 1)
						return(0);
					}
				putsize += nbuf;
			    buf[0] = c;
			    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;
			    buf[0] = c;
			    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);
}


static int
pack_bitmap(file, bm)
FILE *file;
struct BitMap *bm;
{
short i, j;
short row_length, compressed_length;
short plane_offset;

#ifdef DEBUG
printf("pack_bitmap( %lx %lx)\n", file, bm);
#endif DEBUG

#ifdef PARANOID
/* when paranoid do a little error checking first ... fail fast! */
if (bm->BytesPerRow <= 0 || bm->Rows <= 0 || bm->BytesPerRow > MAXDAT)
	{
	printf("bitmap %d bytes by %d rows?????\n", bm->BytesPerRow,
		bm->Rows);
	return(0);
	}
if (bm->Depth <= 0 || bm->Depth > 8)
	{
	printf("bitmap with %d planes ??????\n", bm->Depth);
	return(0);
	}
#endif PARANOID

compressed_length = 0;
plane_offset = 0;
for (i=0; i<bm->Rows; i++)
	{
	for (j = 0; j < bm->Depth; j++)
		{
		if ( (row_length = pack_row(file, bm->Planes[j] + plane_offset, 
			bm->BytesPerRow)) == 0)
			{
#ifdef PARANOID
			printf("error packing row %d plane %d\n", i, j);
#endif PARANOID
			return(0);
			}
		compressed_length += row_length;
		}
	plane_offset += bm->BytesPerRow;
	}
if ( compressed_length & 1)  /*check to see odd length */
	{
	if (file != NULL)		/*pad with 0 to make it even */
		{
		if ( putc( 0, file) == EOF)
			{
			return(0);
			}
		}
	compressed_length++;
	}
return(compressed_length);
}

int
write_iff(name, colors, bits, xoff, yoff, width, compressed)
char *name;
unsigned char *colors;
register struct BitMap *bits;
short xoff, yoff;
short compressed;
{
FILE *file;
struct form_chunk chunk;
struct iff_chunk ichunk;
struct BitMapHeader header;
long bits_size;
short i;
register short j;
register short row_offset;

if ((file = fopen(name, "w") ) == 0)
	{
#ifdef PARANOID
	printf("couldn't Open %s to write\n", name);
#endif PARANOID
	return(0);
	}

/* say its a FORM ILBM */
chunk.fc_type.b4_type = FORM;
chunk.fc_subtype.b4_type = ILBM;
chunk.fc_length = 4 + 3*sizeof(struct iff_chunk) + MAXCOL*3 + 
	sizeof(struct BitMapHeader);
if (bits)
	{
	if (compressed)
		{
		if ( (bits_size = pack_bitmap(NULL, bits)) == 0)
			return(0);
		}
	else
		{
		bits_size =  bits->BytesPerRow*bits->Rows*bits->Depth;
		}
	chunk.fc_length += bits_size;
	}
if (fwrite(&chunk, sizeof(chunk), 1, file) != 1)
	return(0);

/* here comes a BitMapHeader */
ichunk.iff_type.b4_type = BMHD;
ichunk.iff_length = sizeof(header);
if (fwrite(&ichunk, sizeof(ichunk), 1, file) != 1)
	return(0);

/* 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 = XMAX;
header.pageHeight = YMAX;
header.xAspect = XASPECT;
header.yAspect = YASPECT;
/* if it's not just a color map give the dimensions of rasters */
if (bits)
	{
	header.w = width;
	header.h = bits->Rows;
	header.nPlanes = bits->Depth;
	header.x = xoff;
	header.y = yoff;
	}
if (fwrite(&header, sizeof(header), 1, file) != 1)
	return(0);

/* squirt out the color map */
ichunk.iff_type.b4_type = CMAP;
ichunk.iff_length = MAXCOL*3;
if (fwrite(&ichunk, sizeof(ichunk), 1, file) != 1)
	return(0);
if (fwrite(colors, 3*MAXCOL, 1, file) != 1)
	return(0);

/* if they be bits then squirt out the bits */
if (bits)
	{
	ichunk.iff_type.b4_type = BODY;
	ichunk.iff_length = bits_size;
	if (fwrite(&ichunk, sizeof(ichunk), 1, file) != 1)
		return(0);
	if (compressed)
		{
		if (pack_bitmap(file, bits) == 0)
			return(0);
		}
	else
		{
		i = bits->Rows;
		row_offset = 0;
		while (--i >= 0)
			{
			for (j=0; j<bits->Depth; j++)
				{
				if (fwrite( bits->Planes[j]+row_offset, bits->BytesPerRow,
						1, file) != 1)
					return(0);
				}
			row_offset += bits->BytesPerRow;
			}
		}
	}
fclose(file);
return(1);
}

