/* Ilbm.library C application, PicInfo.c:

	Set your editor's TAB width to 3

	cc +p IFFinfo.c
	as -cd IlbmInterface.asm
	ln -o IFFinfo IFFinfo.o IlbmInterface.o -lcl32

	This program can only be run from the CLI. It takes 1 arg, the name of a
file. This program will then print information about the file. For ILBMs and
ANIMs, it will print out information from the BHMD(s) such as image width,
height, depth, etc.  For other IFF files, it will simply print the chunk IDs
as it encounters them.  For ANIMs, it will total the number of "frames"
(ILBMs) in the file.  Note that the library automatically dives into LISTS
and CATS on our behalf.  We don't even have to know that we are inside of a
LIST or CAT.

This is an example of using the library's mid-level routine LoadILBM() with
our own FORM and PROP handlers instead of lib's default routines. This is the
way to go if you need an ANIM reader or some non-ILBM reader/writer. Note
that if our FORMHandler wasn't interested in ILBMs or ANIMs, we wouldn't need
an ILBMFrame, and would use LoadIFF() instead of LoadILBM().

*/

#include "math.h"
#include "functions.h"	  /* Manx C declarations */

#include "exec/tasks.h"
#include "exec/types.h"
#include "exec/memory.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"

 /* The ilbm lib C INCLUDE file */
#include "ILBM_Lib.h"


/*-------------------------------defines---------------------------------*/

IFFP myForm();
IFFP myProp();

 /* Data for the Dissidents ILBM library */
struct ILBMBase	*ILBMBase=0L;
ILBMFrame			myILBMFrame;   /* Whenever we plan on parsing an ILBM
												or ANIM, we need an ILBMFrame */
 /* Data for our program */
struct FileHandle   *fp = 0;
SHORT  numFrames = -1;			/* To count the # of frames in an ANIM */
UBYTE handled_anim = 0;			/* To tell main() that we found an ANIM */

/*------------------------------start of main()----------------------------*/

main( argc, argv )
LONG argc;
UBYTE *argv[];
{
	IFFP  Result;
 /* LoadILBM() and LoadIFF() need a Vectors structure */
	Vectors myVectors;

 /* We expect the filename for the first arg. */
	if( argc != 2 )
	{
		printf("USAGE: IFFinfo filename\n");
		exit();
	}

 /* Open the ILBM lib */
	if( (ILBMBase=(struct ILBMBase *)OpenLibrary("ilbm.library", 0L)) == NULL )
	{
		printf("Need the dissidents ilbm.library on boot disk\n");
		exit();
	}

 /* Note that we are using the lib's middle level functions so that we can
	 install our own handlers for each encountered group and context. Also,
	 we can parse any IFF FORM, not just ILBMs and ANIMs using this level.
	 Unlike the lib's high level ILBM functions LoadIFFToWindow() and
	 SaveWindowToIFF(), the mid level functions don't open the IFF file for us.
	 We are going to install our own function, myForm(), as the routine
	 which the library calls whenever it encounters a FORM ID. Our function,
	 myProp() will be called for non-ILBM PROPS. (Remember, the library
	 automatically handles all ILBM PROPS by parsing info into a PropILBMFrame.)
	 We "install" these routines by setting up the appropriate fields in our
	 Vectors structure. Let's do all this now. */

 /* Open the IFF File */
	fp = Open( argv[1], MODE_OLDFILE );

	if (fp)
	{

		/* Setup the Vectors structure. (i.e. "install" myForm() and myProp()) */
		myVectors.PROPhandler = myProp;
		myVectors.FORMhandler = myForm;
		myVectors.CHUNKhandler = 0;
		myVectors.NonILBMhandler = 0;
		/* Don't care about CHUNKhandler or NonILBMhandler as long as we have
			our own FORMhandler. */

		myILBMFrame.iUserFlags=0;

		/* Start parsing the file */
		Result=LoadILBM( fp, &myVectors, &myILBMFrame );

		/* O.K. we're back. Did we successfully parse as much as we wanted?
			Well, our FORM routine should have returned IFF_OKAY if we parsed
			the entire file. Otherwise, myForm, myProp, or an internal library
			routine knocked us out by returning some other IFFP error code. Let's
			get an informative error msg to display.
		*/

	 	/* If some error, get the IFFP error message and print it */
		if( Result != IFF_OKAY )
		{
			puts( GetIFFPMsg( Result ) );
		}
		else	/* If it was an ANIM we inspected, show the # of frames */
		{
			if( handled_anim )
				printf("Number of Frames = %d", numFrames+1);
		}

		/* Close the IFF File */
		Close(fp);

	}
	else printf( "Couldn't open %s. \n", argv[1]);

	printf("\n");

 /* Close ILBM lib */
	if( ILBMBase )		CloseLibrary( ILBMBase );
	exit();
}			  /* end of main() */


/*========= LoadILBM() or LoadIFF() calls me when it finds a FORM ==========*/

IFFP  myForm( chunkID, context, vectors, frame, proplist )
PROPList *proplist;
ILBMFrame *frame;
Vectors *vectors;
GroupContext *context;
ID		 chunkID;
{
register	IFFP   code = IFF_OKAY;
register	ULONG  propf;
BitMapHeader bmhdr;

	/* Print the FORM ID */
	printf("========= FORM %c%c%c%c =========\n",
				(char) ((chunkID>>24L) & 0x7F),
				(char) ((chunkID>>16L) & 0x7F),
				(char) ((chunkID>>8) & 0x7F),
				(char) (chunkID & 0x7F) );

/* --------------------- ANIM HEADER ------------------------- */

	/* If an ANIM, reset our frame count and ANIM flags of iUserFlags.
		Then parse the "frames" (imbedded ILBMs) of the ANIM. Note that
		GetF1ChunkHdr actually calls our myForm() routine for each imbedded
		ILBM "frame of animation" (i.e. this function is recursive). */

	if (chunkID == ID_ANIM)
	{
		/* set ANIMFLAG of iUserFlags to indicate that we're in an ANIM */
		frame->iUserFlags |= ANIMFLAG;

		/* clear HANDLED_ANIM to indicate that we aren't successful yet */
		handled_anim = 0;

		numFrames = -1;	/* first frame number will be 0 */

		/* Parse each frame of the ANIM */
		while (code >= 0)
		{
			code = GetF1ChunkHdr(context);
		}

		/* If we didn't get END_MARK back, then an error */
		if (code == END_MARK)
		{
			/* set HANDLED_ANIM to indicate that we successfully handled the ANIM */
			handled_anim = 1;

			code = IFF_OKAY;	/* So that we continue parsing any more FORMs in
										the file */
		}
	}

/* ---------------------- ILBM FORMS (ANIM "frames") -------------------- */

	if (chunkID == ID_ILBM )
	{
		/* Parse an ILBM or an ANIM "frame" (which is also an ILBM) */

		/* If there was an ILBM PROP for this group, copy the PROPFrame to our
			ILBMFrame.
		 	Note: SearchPROP() can return any kind of frame. Here, it is an
			ILBMFrame since chunkID == ID_ILBM. If we needed to reference
			anything in the returned pointer, we would have to recast it as
			an (ILBMFrame *).
		 */
		if( !(propf = SearchPROP( chunkID, proplist )) )
			CopyILBMProp( propf, frame );

		if( frame->iUserFlags & ANIMFLAG )
			{
			++numFrames;		/* 1 more "frame" */
			printf( "--- Frame #%d ---\n", numFrames+1 );
			}

		while( code >= 0 )	/* Negative numbers are errors, 0 means "ok, continue"
										and positive numbers are IFF chunk IDs
									 */

		{
			/* Move to the start of the next chunk */
			code = GetFChunkHdr( context );

			if (code > 0 )
			/* Here's a chunk ID. If this were a REAL program, we'd probably
				check to see if this is a chunk that we want to parse. If so, we
				could get the chunksize from the context structure, read the chunk
				into memory, and do something with the data. For example, like
				what I did with the BMHD chunk. Or maybe like this:
						bytes = ChunkMoreBytes( context );
						if( !( memptr = AllocMem( bytes, 0L ) ))
						{
							code = IFFReadBytes( bytes, context, memptr );
						}

			*/
			{
				printf("     %c%c%c%c\n",
						(char) ( (code>>24L) & 0x7F),
						(char) ( (code>>16L) & 0x7F),
						(char) ( (code>>8) & 0x7F),
						(char) ( code & 0x7F) );

				if( code == ID_BMHD )
				{
					/* Print out info about the image */
					code = GetBMHD(context, &bmhdr);
					if( code == IFF_OKAY )
					{
						printf("      width=%d, height=%d, depth=%d\n",
									 bmhdr.w, bmhdr.h, bmhdr.nPlanes);
					}
				}

			}
		}
	}

/* --------------------- Non-ILBM FORMS --------------------- */
	else
	{
		/* A REAL program would normally search the PROPList for any frames
			of the same type, and if found extract needed info from it.
			See notes in myProp() */

		while( code >= 0 )
		{
			code = GetF1ChunkHdr(context);

			if (code > 0)
			/* Here's a chunk ID. If this were a REAL program, we'd probably
				do something useful with it.
			*/
			{
				printf("     %c%c%c%c\n",
						(char) ( (code>>24L) & 0x7F),
						(char) ( (code>>16L) & 0x7F),
						(char) ( (code>>8) & 0x7F),
						(char) ( code & 0x7F) );
			}
		}
	}

	/*
	As long as we rtn IFF_OKAY, the library will continue parsing the IFF 
	file (until it gets to the end). If we rtn any other value, the library will
	return us back to main() from LoadILBM() with that value. For example, if
	we return IFF_DONE, we would return from LoadILBM with IFF_DONE.

	Note: the library routines GetFChunkHdr and GetF1ChunkHdr will return
	END_MARK when there's no more chunks in this FORM. In that case, we should
	know whether we found what we wanted in this FORM. If we did, we return
	IFF_OKAY to continue parsing the next FORM (we may be inside of a LIST or
	CAT), or IFF_DONE if we want to get out with what we've got. If we didn't
	find what we want, we would return IFF_OKAY to continue parsing.

	In this example, we're looking for everything, so we always return IFF_OKAY
	when GetFChunkHdr or GetF1ChunkHdr returns END_MARK.
	*/

	if (code == END_MARK)
	{
		/* Print a double-spaced line inbetween forms */
		puts("=============================\n");
		code = IFF_OKAY;
	}

	return( code );
}


/*========= LoadILBM() or LoadIFF() calls me when it finds a PROP ==========*/

IFFP myProp( chunkID, propID, context, vectors, frame, proplist )
PROPList *proplist;
ILBMFrame *frame;
Vectors *vectors;
GroupContext *context;
ID   propID;
ID   chunkID;
{
	/* We only hear about non-ILBM PROPS. If this were a REAL program, we
		would probably allocate some PROPFrame that we defined for the passed
		propID (an SMUSPropFrame for an ID_SMUS maybe?).  Then we would
		parse the PROP's chunks just like in the FORMhandler, storing info in
		this new PROPFrame. Later, in our FORMHandler, we search the PROPList
		for the appropriate ID and retrieve the info. The library automatically
		updates the PROPList when it gets to the end of an IFF group so that you
		don't have to keep track of which level you're at and which PROPS affect
		which chunks. Also, the library frees all PROPFrames that you allocate
		with GetPROPStruct() by the time we return to main. */

	/* Print the PROP ID */
	printf("  PROP %c%c%c%c\n",
				(char) ((chunkID>>24L) & 0x7F),
				(char) ((chunkID>>16L) & 0x7F),
				(char) ((chunkID>>8) & 0x7F),
				(char) (chunkID & 0x7F) );

	return( IFF_OKAY );
}
