/*	ILBM picture reader etc. for PUZZ	M.J.Round	January 1990
	Largely copied from 'view.c' by D. John Hodgson.
*/

#define SR(a,b,c) if (Read(a,(char *)b,c)==-1L) return(NULL);

#define MAXWIDTH  376 /*	max non-HIRES width					*/
#define MAXHEIGHT 242 /*	max non-interlaced height (NTSC)	*/
#define MAXCOLORS 32  /*	max # colors supported  			*/

#define MakeID(a,b,c,d) ((a)<<24L | (b)<<16L | (c)<<8 | (d))

#define ID_FORM MakeID('F','O','R','M')
#define ID_ILBM MakeID('I','L','B','M')
#define ID_BMHD MakeID('B','M','H','D')
#define ID_CAMG MakeID('C','A','M','G')
#define ID_CMAP MakeID('C','M','A','P')
#define ID_BODY MakeID('B','O','D','Y')

typedef struct
	{
	long ckID,ckSize;
	} Chunk;

Chunk header;
char *bufstart;
struct BitMap b;
struct RastPort rp;
struct RastPort dbuffrastport;
struct BitMap dbuffbitmap;

extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase *GfxBase;
extern BitMapHeader bmhd;
extern struct TextAttr MyFont;

struct Screen *ReadILBM(BPTR fp)
	{
	struct NewScreen NewScreen;
	struct Screen *screen;
	char colormap[MAXCOLORS][3],*sourcebuf;
	short colorcount;
	long id,ViewModes=0;

	SR(fp,&header,(long)sizeof(header));
	if (header.ckID!=ID_FORM) return(NULL);

	SR(fp,&id,(long)sizeof(id));
	if (id!=ID_ILBM) return(NULL);

	for (;;)
		{
		SR(fp,&header,(long)sizeof(header));

		if (header.ckID==ID_BODY) break;

		switch(header.ckID)
			{
			case ID_BMHD:
				SR(fp,&bmhd,(long)sizeof(bmhd));
				break;

			case ID_CMAP:
				SR(fp,&colormap[0][0],(long)header.ckSize);
				colorcount=header.ckSize/3;
				break;

			case ID_CAMG:
				SR(fp,&ViewModes,(long)header.ckSize);
				break;

			default:
				Seek(fp,(((header.ckSize)+1)&(~1L)),OFFSET_CURRENT);
	  
			}
		}

	/*	Read planes into RAM for ease of decompression	*/
	 
	sourcebuf=bufstart=AllocMem((long)header.ckSize,MEMF_PUBLIC);
	if (sourcebuf==0L) return (NULL);

	SR(fp,sourcebuf,(long)header.ckSize); 

	NewScreen.LeftEdge=0; NewScreen.TopEdge=0;
	NewScreen.Width=bmhd.w; NewScreen.Height=bmhd.h;
	NewScreen.Depth=bmhd.nPlanes;

	/*	make some forced assumptions if CAMG chunk unavailable	*/

	if (!(NewScreen.ViewModes=ViewModes))
		{
		if (bmhd.w>MAXWIDTH) NewScreen.ViewModes|=HIRES;
		if (bmhd.h>MAXHEIGHT) NewScreen.ViewModes|=LACE;
		}

	NewScreen.Type=CUSTOMSCREEN;
	NewScreen.Font=&MyFont;
	NewScreen.Gadgets=0L;

	screen=OpenScreen(&NewScreen);

	while (colorcount--)
		SetRGB4
			(
			&screen->ViewPort,
			(long)colorcount,
			colormap[colorcount][0]>>4L,colormap[colorcount][1]>>4L,
			colormap[colorcount][2]>>4L
			);
  
	return(screen);   
	}	   

void Expand(screen,bmhd) /* Fast line decompress/deinterleave	*/
struct Screen *screen;
BitMapHeader *bmhd;
	{
	register char *sourcebuf;
	register char n,*destbuf; /*	in order of preferred allocation	*/
	register short plane,linelen,rowbytes,i;

	sourcebuf = bufstart;
	linelen=bmhd->w/8;

	for (i=0;i<bmhd->h;i++) /*	process n lines/screen	*/
		{
		for (plane=0;plane<bmhd->nPlanes;plane++)
			{	/*	process n planes/line	*/
			destbuf=(char *)(screen->BitMap.Planes[plane])+linelen*i;

			if (bmhd->compression== 1)
				{	/*	compressed screen?	*/
				rowbytes=linelen;

				while (rowbytes)
					{	/*	unpack until 1 scan-line complete		*/
					n=*sourcebuf++; 	/*	fetch block run marker	*/

					/*	uncompressed block? copy n bytes verbatim	*/
					if (n>=0)
						{
						movmem
							(
							sourcebuf,
							destbuf,
							(unsigned int)++n
							);
						rowbytes-=n;
						destbuf+=n;
						sourcebuf+=n;
						}
					else
						{	/*	compr. block? expand n duplicate bytes	*/
						n=-n+1;
						rowbytes-=n;
						setmem
							(
							destbuf,
							(unsigned int)n,
							(unsigned int)*sourcebuf++
							);
						destbuf+=n;
						}

					}	/*	finish unpacking line	*/
				}
			else
				{	/*	uncompressed? just copy */
				movmem(sourcebuf,destbuf,(unsigned int)linelen);
				sourcebuf+=linelen;
				}
			}	/*	finish interleaved planes, lines	*/
		}
	}

struct RastPort *getbuffs(int width,int height)
	{
	int i;
	
	InitBitMap(&b,bmhd.nPlanes,bmhd.w,11);
	
	for (i=0; i<bmhd.nPlanes; i++)
		{
		b.Planes[i] = (PLANEPTR)AllocRaster(bmhd.w,11);
		if(b.Planes[i] == NULL)
			{
			while (--i >= 0)
				FreeRaster(b.Planes[i],bmhd.w,11);

			return (NULL);
			}
		}

	InitRastPort(&rp);
	rp.BitMap = &b;

	InitBitMap(&dbuffbitmap,bmhd.nPlanes,width,height);
	
	for (i=0; i<bmhd.nPlanes; i++)
		{
		dbuffbitmap.Planes[i] = (PLANEPTR)AllocRaster(width,height);
		if(dbuffbitmap.Planes[i] == NULL)
			{
			while (--i >= 0)
				FreeRaster(dbuffbitmap.Planes[i],width,height);

			return (NULL);
			}
		}

	InitRastPort(&dbuffrastport);
	dbuffrastport.BitMap = &dbuffbitmap;
	
	return (&dbuffrastport);
	}
	
void saveundermenu(struct RastPort *rast)
	{
	ClipBlit
		(
		rast,0,0		/*	source posn	*/
		,&rp,0,0		/*	dest posn	*/
		,bmhd.w,11  	/*	size		*/
		,0xc0			/*	direct copy	*/
		);
	}
	
void drawovermenu(struct RastPort *rast)
	{
	ClipBlit
		(
		&rp,0,0 		/*	source posn */
		,rast,0,0		/*	dest posn	*/
		,bmhd.w,11  	/*	size		*/
		,0xc0			/*	direct copy	*/
		);
	}

void freebuffs(int width,int height)
	{
	int i;
	
	for (i=0; i<bmhd.nPlanes; i++)
		FreeRaster(dbuffbitmap.Planes[i],width,height);
		
	for (i=0; i<bmhd.nPlanes; i++)
		FreeRaster(b.Planes[i],bmhd.w,11);

	FreeMem(bufstart,(long)header.ckSize); /*	free compressed buffer	*/
	}
