/* example Window BMP loader - (C) Almathera 1994 21/11/94

BUGS: does NOT support .DIB/RLE compressed or OS/2 BMP files yet

REQUIRES: SAS C V6.50 or higher.

*/

	#include <exec/types.h>
	#include <clib/exec_protos.h>
	#include <clib/dos_protos.h>
	#include <dos/dos.h>
	#include <pragmas/exec_pragmas.h>
	#include <pragmas/dos_pragmas.h>
   #include "pgs_protos.h"
   #include "pgs_pragmas.h"

	#include "gio.h"
	#include <stdio.h>
	#include <stdlib.h>

struct BMPHeader 
{
	UWORD		id;			// char id[2]
	PCLONG	filesize;
	PCWORD	reserved[2];
	PCLONG	headersize;
	PCLONG	infosize;
	PCLONG	width;
	PCLONG	depth;
	PCWORD	biplanes;
	PCWORD	bits;
	PCLONG	bicompression;
	PCLONG	bisizeimage;
	PCLONG	bixpelspermeter;
	PCLONG	biyperlpermeter;
	PCLONG	biclrused;
	PCLONG	biclrimportant;
};

struct BMPPalEntry 
{
	UBYTE blu;
 	UBYTE grn;
	UBYTE	red;
	UBYTE	pad;

};

#define PCLong2Long(val) ((((val&0xff)<<24)|((val&0xff00)<<8)|((val&0xff0000)>>8)|((val&0xff000000)>>24)))
#define PCWord2Word(val) ((((val&0xff)<<8)|((val&0xff00)>>8)))

/* prototypes */
__asm ULONG __saveds GioInfo(void);
__asm ULONG __saveds GioExamine(register __a0 struct GIOData *giodata);
__asm ULONG __saveds GioRead(register __a0 struct GIOData *giodata);
__asm ULONG __saveds GioWrite(register __a0 struct GIOData *giodata);
/* end of prototypes */


__asm ULONG __saveds GioInfo(void)
{
//	return(GIOF_LOADER8|GIOF_LOADER24|GIOF_SAVER8|GIOF_SAVER24|GIOF_LOADFILE|GIOF_SAVEFILE)
	return(GIOF_LOADER8|GIOF_LOADER24|GIOF_LOADFILE|GIOF_SAVER24|GIOF_SAVEFILE);

}

__asm ULONG __saveds GioExamine(register __a0 struct GIOData *giodata)
{
	struct BMPHeader *bmphead;
	giodata->Flags = GioInfo();
	// examine file and return information on it.
	bmphead = (struct BMPHeader *) giodata->Data;
	
	if(bmphead->id!=(0x424d))
	{
		giodata->Error = LOAD_WRONGTYPE;
		goto err;
	}
	giodata->Width 			= PCLong2Long(bmphead->width);
	giodata->Height 			= PCLong2Long(bmphead->depth);
	giodata->Depth 			= PCWord2Word(bmphead->bits);
	giodata->Headerlength 	= PCLong2Long(bmphead->headersize);

	giodata->Error = LOAD_OK;
err:
	return(giodata->Error);

}

#define DOSBase giodata->DOSBase
#define PgsBase giodata->PgsBase

__asm ULONG __saveds GioRead(register __a0 struct GIOData *giodata)
{
int x,y,r;
UBYTE *bytes,*pal;
ULONG rdsize;
struct BMPPalEntry palentry;

	SetProgress("Loading Windows BMP file",0);

	if(giodata->Depth == 24)
	{
		// load in 24-bit data here

		Seek(giodata->Filehandle,giodata->Headerlength+giodata->SkipBytes,
				OFFSET_BEGINNING);
		for(y=giodata->Height-1;y>=0;y--)
		{
			if(!(y%16)) // don't slow down by calling SetProgress too often
			if(SetProgress(0,100-(y*100/giodata->Height))!=1)
			{
				giodata->Error = LOAD_ABORTED;
				goto err;
         }

			bytes=GetLine(giodata,y);
// here we obtain a pointer to the line bitmap

			Read(giodata->Filehandle,bytes,giodata->Width*3);
// we read the data in

			for(x=0;x<giodata->Width;x++)
			{ // BGR to RGB - swap byte order after load
					r=*bytes;
					*bytes=*(bytes+2);
					*(bytes+2)=r;
					bytes+=3;
			}
// BMP byte order is different to ours, so we swap them!

			ReleaseLine(giodata,y);
// and release the line when we've finished.

		}

		giodata->Error = LOAD_OK;
		goto err;

	} else
	{		// load in 1/4/8-bit data here
		// first decode palette

		Seek(giodata->Filehandle,0x36+giodata->SkipBytes,OFFSET_BEGINNING);		
		if(!(pal = giodata->Palette)) 
		{
			giodata->Error=LOAD_WRONGTYPE;
			goto err;
		}
		for(x=0;x<(1<<(giodata->Depth));x++)
		{	
			Read(giodata->Filehandle,&palentry,4);			
			*pal++=palentry.red;
			*pal++=palentry.grn;
			*pal++=palentry.blu;
		}
		Seek(giodata->Filehandle,giodata->Headerlength+giodata->SkipBytes,
           OFFSET_BEGINNING);
		switch(giodata->Depth) 
		{

      // decide how many bytes to read in at a time

			case 1:
			rdsize=giodata->Width/8;
			break;

			case 4:
			rdsize=giodata->Width/2;
			break;

			case 8:
			rdsize=giodata->Width;
			break;

			default:
			giodata->Error = LOAD_WRONGTYPE;
			goto err;
		}
		if(rdsize & 0x0003) {rdsize |= 0x0003;rdsize++;}

		for(y=giodata->Height-1;y>=0;y--)
		{
			if(!(y%16))
			if(SetProgress(0,100-(y*100/giodata->Height))!=1)
			{
				giodata->Error = LOAD_ABORTED;
				goto err;
			}
			bytes = GetLine(giodata,y);
			Read(giodata->Filehandle,bytes,rdsize);
			ReleaseLine(giodata,y);
		}

		giodata->Error = LOAD_OK;
		goto err;

	}
	giodata->Error=LOAD_WRONGTYPE;

err:
	return(giodata->Error);
}

__asm ULONG __saveds GioWrite(register __a0 struct GIOData *giodata)
{
struct BMPHeader header = {0};
UBYTE *peeker;
int x,y,bytewid;
UBYTE r,g,b;


	if(giodata->Depth == 24)
	{ 		// save 24-bit data here

		bytewid = giodata->Width*3;
		if(bytewid & 0x3) 
		{	bytewid|=0x3;	++bytewid;	
		}
		header.id			= 0x424d;
		header.filesize		= PCLong2Long(54 + (bytewid * giodata->Height));
		header.headersize	= PCLong2Long(54);
		header.infosize		= PCLong2Long(0x28L);
		header.width		= PCLong2Long(giodata->Width);
		header.depth		= PCLong2Long(giodata->Height);	
		header.biplanes		= PCWord2Word(1);
		header.bits			= PCWord2Word(24);
		header.bicompression= 0;
		header.bisizeimage	= PCLong2Long(bytewid*giodata->Height);

		Write(giodata->Filehandle,&header,sizeof(struct BMPHeader));


		for(y=0;y<giodata->Height;y++)
		{
			if(!(y%16))
			if(SetProgress(0,y*100/giodata->Height)!=1)
			{
				giodata->Error = LOAD_ABORTED;
				goto err;
			}

			peeker=GetLine(giodata,y);
			for(x=0;x<giodata->Width;x++)
			{
				r=*peeker++;
				g=*peeker++;	
				b=*peeker++;
				FPutC(giodata->Filehandle,b);
				FPutC(giodata->Filehandle,g);
				FPutC(giodata->Filehandle,r);
			}
			ReleaseLine(giodata,y);
		}
		giodata->Error = LOAD_OK;

	} else
	{		// save 8-bit data here
		giodata->Error = LOAD_WRONGTYPE;
	}

err:
	return(giodata->Error);
}
