/*  Rendition.c           by Martin Boyd 25-Apr-93.
 *
 *  An example file loader using the OpalVision LSI interface.
 *
 *  The rendition file format simply consists of a 1024 byte
 * header (Described below) followed by 32 bit (RGBA) image data
 * compressed using run-length encoding. Each run is encoded as
 * 6 bytes:
 *
 *	Byte 0 - Red
 *	Byte 1 - Green
 *	Byte 2 - Blue
 *	Byte 3 - Alpha
 *	Byte 4 - Run length low byte
 *	Byte 5 - Run length high byte
 *
 * NOTE: This loader only loads the 6RN format as described
 *      above.
 */


#include <exec/types.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <opal/loadsave.h>
#include <opal/opalpaint.h>
#include <opal/opallib.h>
#include <stdlib.h>
#include <string.h>


char Version[] = "\0$VER: Rendition.Loader 1.0 (25.04.93)";

	/* Rendition format information */

struct Tile
	{ long Ptr;
	  long Length;
	};

struct RendHeader		/* 1024 byte header		  */
	{ long	Magic;		/* H_MAGIC			  */ 
	  SHORT	Version;	/* 2				  */
	  char	Label[128];	/* ASCII Desciption		  */
	  char	Date[26];	/* Date of image creation	  */
	  SHORT	Height;		/* Pixel height of image	  */
	  SHORT	Width;		/* Pixel width of image		  */
	  SHORT	TileHeight;	/* Pixel height of tile		  */
	  SHORT	TileWidth;	/* Pixel width of tile		  */
	  SHORT	Format;		/* See below			  */
	  SHORT	Encoding;	/* See below			  */
	  long	NumRuns;	/* Number of runs		  */
	  SHORT	BlockSize;	/* 1024				  */
	  SHORT XOffset;	/* Horizontal offset		  */
	  SHORT YOffset;	/* Vertical offset		  */
	  long CMapLen;		/* Number of colour table entries */
	  long CMapPtr;		/* Pointer to ColourMap		  */
	  char CMapName[64];	/* Colour Map filename		  */
	  struct Tile Tile[64];	/* Tile pointer table		  */
	  char UnSpec[258];	/* Unspecified (reserved)	  */
	};


#define H_MAGIC		0xE880
#define H_BLOCKSIZE	1024
#define H_HEADERSIZE	1024

	/* Format types */
#define H_CRGBAPIX	2	/* 4 bytes per pixel	*/
#define H_6RUNLH	11	/* 6 bytes per run	*/
#define H_2RUN		12	/* 2 bytes per run; colour map in seperate file */
#define H_2RUNMAP	13	/* 2 bytes per run; colour map in image file */
#define H_COLOURMAP	20	/* 4 bytes per entry	*/
#define H_MIPMAP	22	/* Mip map: header, map table and 4-byte pixels */
#define H_ONEDRGBA8	23	/* 1D table of 4-byte pixels */
#define H_ONEDBYTE	24	/* 1D table of bytes */


char LoaderName[] = "Rendition";
char PortName[] = "Rendition_Loader";


int Width,Height;		/* Dimension of the image being loaded	*/
BPTR File;			/* File handle of image being loaded	*/
ULONG LoadFlags;		/* Flags from the OVCMD_LOADIMAGE command */
struct MsgPort *LoadPort;	/* This modules message port		*/
struct OpalBase *OpalBase;

BOOL Do_Load (void);


void main (void)
{
   struct LSIMessage *Mesg;
   struct RendHeader *Header;

	LoadPort = CreatePort (PortName,0);
	if (LoadPort==NULL)
		exit (10);
	OpalBase = (struct OpalBase *)OpenLibrary ("opal.library",0);
	if (OpalBase==NULL)
		{ DeletePort (LoadPort);
		  exit (10);
		}

	if (!AddOVLoader (LoaderName,LoadPort,
			OVLF_ALLOWIMAGE|OVLF_ALLOWALPHA|OVLF_NEEDFILENAME))
		{ DeletePort (LoadPort);
		  CloseLibrary ((struct Library *)OpalBase);
		  exit (10);
		}
	CloseLibrary ((struct Library *)OpalBase);	/* library must be closed */
	OpalBase = NULL;

	while (1)
		{ WaitPort (LoadPort);
		  while (Mesg = (struct LSIMessage *)GetMsg (LoadPort))
			{ switch (Mesg->lsi_Type)
				{ case OVCMD_LOADIMAGE:
					File = Mesg->lsi_File;
					LoadFlags = Mesg->lsi_Flags;
					ReplyMsg ((struct Message *)Mesg);
					Do_Load();
					break;
				  case OVCMD_EXPUNGE:
					if (!OpalBase)
						OpalBase = (struct OpalBase *)
							OpenLibrary ("opal.library",0);
					RemOVLoader (LoaderName);
					if (OpalBase)
						CloseLibrary ((struct Library *)OpalBase);
					DeletePort (LoadPort);
					ReplyMsg ((struct Message *)Mesg);
					exit (0);
					break;
				  case OVCMD_FORMATCHECK:
					Header = (struct RendHeader *)Mesg->lsi_Address;
					if ((Header->Magic!=H_MAGIC) ||
						(Header->Format!=H_6RUNLH))
						Mesg->lsi_Result = FALSE;
					else
						{ Width = Header->Width;
						  Height = Header->Height;
						  Mesg->lsi_Result = TRUE;
						  Mesg->lsi_Width = Width;
						  Mesg->lsi_Height = Height;
						  Mesg->lsi_Depth = 24;
						  Mesg->lsi_Flags = LSIF_HASIMAGE|LSIF_HASALPHA;
						}
					ReplyMsg ((struct Message *)Mesg);
					break;
				}
			}
		}
}



BOOL Do_Load (void)
{
   int x,y;
   struct MsgPort *HostPort;
   struct LSIMessage Mesg;
   UBYTE *RBuff,*GBuff,*BBuff,*ABuff,*DestBuff,*SrcBuff;
   register UBYTE *Src,*R,*G,*B,*A,RData,GData,BData,AData;
   long SBuffSize,DBuffSize,FileLength,RunLength,AmountLeft;
   long AmountRead,FilePos;
   int DBuffRows,Row;


	HostPort = FindPort (OVLOADERPORT);	/* Find the host port */
	if (HostPort==NULL) return (FALSE);

	memset (&Mesg,0,sizeof(struct LSIMessage));
	Mesg.lsi_Node.mn_Node.ln_Type = NT_MESSAGE;
	Mesg.lsi_Node.mn_ReplyPort = LoadPort;
	Mesg.lsi_Node.mn_Length = sizeof (struct LSIMessage);

		/* This buffer is used to hold the decompressed data */
	DBuffRows = 20;
	do
		{ DBuffSize = DBuffRows * Width * 4;
		  DestBuff = AllocMem (DBuffSize,MEMF_CLEAR);
		  if (DestBuff==NULL)
			DBuffRows--;
		} while ((DestBuff==NULL) && (DBuffRows>0));

	if (DBuffRows==0)
		{ Mesg.lsi_Type = OVCMD_ERROR;
		  Mesg.lsi_Result = LSI_ERR_OUTOFMEM;
		  LSICmd (HostPort,LoadPort,&Mesg);
		  return (FALSE);
		}
	RBuff = DestBuff;
	GBuff = DestBuff+(1*DBuffRows*Width);
	BBuff = DestBuff+(2*DBuffRows*Width);
	ABuff = DestBuff+(3*DBuffRows*Width);

		/* Allocate a buffer for raw file data.
		 * Allocate the biggest buffer we can, start
		 * with the size of the file and work our way down.
		 */
	Seek (File,0,OFFSET_END);
	FileLength = Seek (File,sizeof(struct RendHeader),OFFSET_BEGINNING);
	SBuffSize = FileLength - sizeof (struct RendHeader);

	do
		{ SrcBuff = AllocMem (SBuffSize,MEMF_CLEAR);
		  if (SrcBuff==NULL)
			SBuffSize = SBuffSize/2;
		}
	while ((SrcBuff==NULL) && (SBuffSize>(DBuffSize+1024)));

	if (SrcBuff==NULL)
		{ FreeMem (DestBuff,DBuffSize);
		  Mesg.lsi_Type = OVCMD_ERROR;
		  Mesg.lsi_Result = LSI_ERR_OUTOFMEM;
		  LSICmd (HostPort,LoadPort,&Mesg);
		  return (FALSE);
		}

		/* Read in Image data and get the first run */
	FilePos = Seek (File,0,OFFSET_CURRENT);
	AmountRead = Read (File,SrcBuff,SBuffSize);
	AmountLeft = AmountRead;
	R = RBuff;
	G = GBuff;
	B = BBuff;
	A = ABuff;
	Src = SrcBuff;
	RData = *Src++;
	GData = *Src++;
	BData = *Src++;
	AData = *Src++;
	RunLength = *Src++;
	RunLength += (*Src++)<<8;
	Row = 0;
	AmountLeft -=6;

	for (y=0; y<Height; y++)
		{ for (x=0; x<Width;x++)
			{ if (RunLength==0)
				{ if ((AmountLeft<6) && (FilePos+AmountRead<FileLength))
					{ FilePos += AmountRead-AmountLeft;
					  Seek (File,FilePos,OFFSET_BEGINNING);
					  AmountRead = Read (File,SrcBuff,SBuffSize);
					  AmountLeft = AmountRead;
					  Src = SrcBuff;
					}
				  RData = *Src++;
				  GData = *Src++;
				  BData = *Src++;
				  AData = *Src++;
				  RunLength = *Src++;
				  RunLength += (*Src++)<<8;
				  AmountLeft -= 6;
				}
			  *R++ = RData;
			  *G++ = GData;
			  *B++ = BData;
			  *A++ = AData;
			  RunLength--;
			}
		  Row++;

			/* Once decompress buffer is full, send
			 * image data to host.
			 */

		  if ((Row==DBuffRows) || (y==Height-1))
			{ Mesg.lsi_Planes[0] = RBuff;
			  Mesg.lsi_Planes[1] = GBuff;
			  Mesg.lsi_Planes[2] = BBuff;
			  Mesg.lsi_Width = Width;
			  Mesg.lsi_Height = Row;
			  Mesg.lsi_X = 0;
			  Mesg.lsi_Y = y-Row+1;
			  Mesg.lsi_Type = OVCMD_SENDDATA;
			  Mesg.lsi_SubType = OVDF_RGB;
			  LSICmd (HostPort,LoadPort,&Mesg);

				/* Send Alpha channel data if requested */

			  if (LoadFlags & LSIF_ALPHA)
				{ Mesg.lsi_Planes[0] = ABuff;
				  Mesg.lsi_Width = Width;
				  Mesg.lsi_Height = Row;
				  Mesg.lsi_X = 0;
				  Mesg.lsi_Y = y-Row+1;
				  Mesg.lsi_Type = OVCMD_SENDDATA;
				  Mesg.lsi_SubType = OVDF_ALPHA;
				  LSICmd (HostPort,LoadPort,&Mesg);
				}
			  Row = 0;
			  R = RBuff;
			  G = GBuff;
			  B = BBuff;
			  A = ABuff;
			}

		  if ((y & 0xF)==0)
			{ Mesg.lsi_Type = OVCMD_PERCENTAGE;
			  Mesg.lsi_Address = "Rend ";
			  Mesg.lsi_Result = (y*100)/Height;
			  LSICmd (HostPort,LoadPort,&Mesg);
			}
		}

	Mesg.lsi_Type = OVCMD_DONE;
	LSICmd (HostPort,LoadPort,&Mesg);
	FreeMem (DestBuff,DBuffSize);
	FreeMem (SrcBuff,SBuffSize);
	return (TRUE);
}

