
#define PARANOID

/**********************************************************************
*
*			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 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!
*
* 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 "jiff.h"

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

/*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... */


static struct ILBM_info *read_ilbm(), *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 ... ) */
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 *
read_iff(name, just_colors)
char *name;
short just_colors;
{
struct ILBM_info *info = &root_info;
FILE *file;
struct form_chunk chunk;

if ((file = fopen(name, "r") ) == 0)
	{
#ifdef PARANOID
	printf("couldn't Open %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

info = read_ilbm(file, info, chunk.fc_length - sizeof(chunk), just_colors);
fclose(file);
#ifdef DEBUG
printf("info = %lx\n", info);
#endif DEBUG
return(info);
}

static
struct ILBM_info *
read_ilbm(file, info, length, just_colors)
FILE *file;
struct ILBM_info *info;
long length;
short just_colors;
{
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 befor 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, 3*MAXCOL, 1, file) != 1)
					{
#ifdef PARANOID
					iff_truncated(4);
#endif PARANOID
					return(NULL);
					}
				bit_bucket(file, chunk.iff_length - sizeof(3*MAXCOL));
				}
			got_cmap = TRUE;
			if (just_colors)
				return(info);
			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( read_body(file, info, chunk.iff_length) );

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



static
struct ILBM_info *
read_body(file, info, length)
FILE *file;
register struct ILBM_info *info;
long length;
{
struct ILBM_header *header;
struct BitMap *bm;
int i, j;
int rlength;
int plane_offset;

#ifdef DEBUG
printf("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 read_body\n",i);
#endif PARANOID
		free_planes( &info->bitmap );
		return(NULL);
		}
	}
plane_offset = 0;
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 in C */
						*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;
	}
return(info);
}


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

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

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




