#include <proto/intuition.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <exec/types.h>
#include <intuition/intuition.h>
#include <graphics/gfx.h>
#include <libraries/dos.h>
#include <string.h>
#include <stdio.h>

extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase *GfxBase;

#define MakeID(a,b,c,d)  ( (ULONG)(a)<<24L | (ULONG)(b)<<16L | (c)<<8 | (d) )
#define FORM    MakeID('F','O','R','M')
#define ID_ILBM MakeID('I','L','B','M')
#define ID_BMHD MakeID('B','M','H','D')
#define ID_CMAP MakeID('C','M','A','P')
#define ID_CAMG MakeID('C','A','M','G')
#define ID_BODY MakeID('B','O','D','Y')

typedef UBYTE Masking;
#define mskHasMask	1

typedef UBYTE Compression;
#define cmpByteRun1	1

typedef struct {
	UWORD w, h;
	WORD  x, y;
	UBYTE nPlanes;
	Masking masking;
	Compression compression;
	UBYTE pad1;
	UWORD transparentColor;
	UBYTE xAspect, yAspect;
	WORD  pageWidth, pageHeight;
	} BitMapHeader;

struct NewScreen NewScreen;
BitMapHeader BitMapHeader;

main(int argc,char **argv) {

	ULONG t;
	long int h, l, ncol, i, x, y, n, f = 0;
	char ColorMap[96], *p;
   unsigned long int ComAmg;
	struct Screen *Screen;

	IntuitionBase = (void *)OpenLibrary("intuition.library", 0);
	GfxBase = (void *)OpenLibrary("graphics.library", 0);


	h = Open(argv[1], MODE_OLDFILE);
	if (h == NULL) {
		puts("File non trovato");
		exit(FALSE);
	}

	Read(h, (void *)&t, 4);

/* Controlla che i primi 4 caratteri siano "FORM" */
	if (t != FORM) exit(FALSE);

/* Qui c'e' la lunghezza totale, che non ci interessa */
	Seek(h, 4, OFFSET_CURRENT);

	Read(h, (void *)&t, 4);
/* Controlla che sia un FORM ILBM */
	if (t != ID_ILBM) exit(FALSE);

	do {

/* Legge l'identificatore e la lunghezza del chunk */
		Read(h, (void *)&t, 4);
		Read(h, (void *)&l, 4);

/* Analizza il chunk; se e' sconosciuto si salta */
		switch(t) {
			case ID_BMHD:
				Read(h, (void *)&BitMapHeader, l);
				break;
			case ID_CMAP:
				Read(h, ColorMap, l);
				if (l%2) Seek(h, 1, OFFSET_CURRENT);
				ncol = l/3;    /* Numero colori = numero componenti/3 */
				break;
			case ID_CAMG:
				Read(h, (void *)&ComAmg, 4);
				break;
			case ID_BODY:
				p = (void *)calloc(l, 1);

		/* leggiamo tutto il chunk in una volta perche' leggerlo byte per byte
			sarebbe lentissimo */
				Read(h, p, l);
		/* ora usiamo i dati in BitMapHeader per inizializzare lo schermo
			di Intuition */
				NewScreen.Type = CUSTOMSCREEN | SCREENQUIET;
				NewScreen.Width = BitMapHeader.w;
				NewScreen.Height = BitMapHeader.h;
				NewScreen.Depth = BitMapHeader.nPlanes;
				NewScreen.ViewModes = (ComAmg & ~(SPRITES | VP_HIDE | GENLOCK_AUDIO | GENLOCK_VIDEO));
				Screen = OpenScreen(&NewScreen);
				for(i=0; i<ncol; i++) SetRGB4(&Screen->ViewPort, i,
														ColorMap[i*3] >> 4, ColorMap[i*3+1] >> 4, ColorMap[i*3+2] >> 4);

		/* a parte qualche piccola ottimizzazione, questo ciclo segue fedelmente
			l'algoritmo base; si noti che non viene controllata un'eventuale compressione
			erronea attraverso le righe e che in caso l'immagine non sia compressa il programma
			non fa nulla; puo' essere un utile esercizio provare a scrivere il codice per
			questo caso, anche se non esistono in pratica immagini IFF non compresse. */

				if (BitMapHeader.compression == cmpByteRun1) {
					for(y=0; y<NewScreen.Height; y++)
		/* sommiamo uno alla profondita' se il masking e' presente */
						for(i=0; i<NewScreen.Depth+(BitMapHeader.masking & mskHasMask != 0); i++)
							for(x=0; x<NewScreen.Width; )
								if ( (n = *(p++)) >= 0 ) {
		/* il test controlla se non stiamo passando sul masking
			(nel qual caso non bisogna scrivere) */
									if (i<NewScreen.Depth)
										memcpy((char *)Screen->BitMap.Planes[i]+y*(NewScreen.Width/8)+x/8, p, n+1);
									p += n+1;
									x += (n+1)*8;
								}
								else if (n > -128) {
									if (i<NewScreen.Depth)
										memset((char *)Screen->BitMap.Planes[i]+y*(NewScreen.Width/8)+x/8, *p, -n+1);
									x += (-n+1)*8;
									p++;
								}
		/* aspettiamo 10 sec */
					Delay(500);
				}
				f = 1;
				break;
			default:			Seek(h, l, OFFSET_CURRENT);
								if (l%2) Seek(h, 1, OFFSET_CURRENT);
								break;
		}
	} while(!f);

	CloseScreen(Screen);
	CloseLibrary((void *)IntuitionBase);
	CloseLibrary((void *)GfxBase);
	Close(h);
	exit();
}
