/**********************************************************************
*
*			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 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... */
#define PARANOID

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);
} /* iff_truncated */
#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);
} /* read_iff */

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) );

		case GRAB:  /*ignore documented but unwanted types*/
		case DEST:
		case SPRT:
		case CAMG:
		case CRNG:
		case CCRT:
			bit_bucket(file, chunk.iff_length);
			break;

		default:	/*squawk about unknown types if PARANOID */
#ifdef PARANOID
			printf("\t unk type %lx of b4_type\n", chunk.iff_type.b4_type);
#endif PARANOID

		} /* switch */

		read_in += chunk.iff_length + sizeof(chunk);
	} /* while */
#ifdef PARANOID
	printf("no BODY in ILBM\n");
#endif PARANOID
	return(NULL); 
} /* read_ilbm */

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);
		}
	} /* for i */

	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);
				}
			} /* for j */
		}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 value */
				} /* while */
				if (so_far != 0){
#ifdef PARANOID
					printf("compression quite screwed up, aborting %d\n", 
							so_far);
#endif PARANOID
					free_planes( &info->bitmap);
					return(NULL);
				} /* if so_far */
			} /* for j */
		} /* if info->header.compression */
		plane_offset += info->bitmap.BytesPerRow;
	} /* for i */
	return(info);
} /* read_body */

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);
} /* free_planes */

