
/******************************************************************************
 *
 * Flowerpower's DeBox Datatype
 *
 * Written by Christian Buchner and David N. Junod
 *
 ******************************************************************************
 * dispatch.c
 *
 */

#include "classbase.h"

/*****************************************************************************/

#include <stdarg.h>

/* Message request routine */

void __stdargs Message(struct ClassBase *cb, UBYTE *Msg,...)
{
	va_list Arg;
	struct EasyStruct Req={sizeof(struct EasyStruct),0,"DeBox Request",0,"Okay"};
	va_start(Arg,Msg);
	Req.es_TextFormat=Msg;
	EasyRequestArgs(NULL,&Req,0,Arg);
	va_end(Arg);
}

/*****************************************************************************/

ULONG setdtattrs (struct ClassBase * cb, Object * o, ULONG data,...)
{
	return (SetDTAttrsA (o, NULL, NULL, (struct TagItem *) & data));
}

/*****************************************************************************/

ULONG getdtattrs (struct ClassBase * cb, Object * o, ULONG data,...)
{
	return (GetDTAttrsA (o, (struct TagItem *) & data));
}

/*****************************************************************************/

Class *initClass (struct ClassBase * cb)
{
	Class *cl;
	
	/* Create our class.  Note that this particular class doesn't have any instance data */
	if (cl = MakeClass (LibName, PICTUREDTCLASS, NULL, NULL, 0L))
	{
		cl->cl_Dispatcher.h_Entry = (ULONG (*)())Dispatch;
		cl->cl_UserData = (ULONG) cb;
		AddClass (cl);
	}
	
	return (cl);
}

/*****************************************************************************/

ULONG __asm Dispatch (register __a0 Class * cl, register __a2 Object * o, register __a1 Msg msg)
{
	struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
	ULONG retval;
	
	switch (msg->MethodID)
	{
		case OM_NEW:
		if (retval = DoSuperMethodA (cl, o, msg))
		{
			if (!GetPicture (cb, cl, (Object *) retval, ((struct opSet *) msg)->ops_AttrList))
			{
				CoerceMethod (cl, (Object *) retval, OM_DISPOSE);
				return NULL;
			}
		}
		break;
		
		/* Let the superclass handle everything else */
		default:
		retval = (ULONG) DoSuperMethodA (cl, o, msg);
		break;
	}
	
	return (retval);
}

/*****************************************************************************/

BOOL __asm GetPicture (register __a6 struct ClassBase * cb, register __a0 Class * cl, register __a2 Object * o, register __a1 struct TagItem * attrs)
{
	BOOL Success = TRUE;
	STRPTR title;
	struct BitMapHeader *bmhd;
	BPTR fh;
	struct DeBoxChunk chunk;
	UBYTE *data=NULL;
	UWORD n;
	UWORD *colors;
	struct BitMap *bm=NULL;
	ULONG modeid;
	struct ColorRegister *cmap;
	LONG *cregs;
	
	/* Get the default title */
	title = (STRPTR) GetTagData (DTA_Name, NULL, attrs);
	
	/* Get the file handle to read from and the BitMapHeader to write to */
	if (!((getdtattrs (cb, o, DTA_Handle, &fh, PDTA_BitMapHeader, &bmhd, TAG_DONE) == 2) && fh))
	{
		SetIoErr(ERROR_OBJECT_WRONG_TYPE);
		Success=FALSE;
	}
	else
	{
		/* Skip the first chunk header (similar to FORM header) */
		Seek (fh, sizeof(struct DeBoxChunk), OFFSET_BEGINNING);
		
		/* Read in a chunk header */
		while (Success && (Read(fh, &chunk, sizeof(chunk))==sizeof(chunk)))
		{
			/* Check header CRC */
			if (!DeBoxCRC(&chunk))
			{
				SetIoErr(ERROR_BAD_HUNK);
				Success=FALSE;
				break;
			}
			else
			{
				/* Allocate memory for chunk data */
				if (!(data=AllocVec(chunk.chunk_len,MEMF_PUBLIC)))
				{
					SetIoErr(ERROR_NO_FREE_STORE);
					Success=FALSE;
					break;
				}
				else
				{
					/* Read in chunk body */
					if (Read(fh, data, chunk.chunk_len)!=chunk.chunk_len)
					{
						SetIoErr(ERROR_BAD_HUNK);
						Success=FALSE;
						break;
					}
					else
					{
//						Message(cb,	"Read chunk: %ld (%ld)\n"
//									"Checksum: $%02lx\n"
//									"HeaderMark: $%02lx\n"
//									"DontKnow_02: $%04lx\n"
//									"Flags: $%02lx\n"
//									"CrunchMethod: $%02lx\n"
//									"Destination Plane: $%02lx\n"
//									"Reference Plane: $%02lx",
//									
//									chunk.chunk_len,
//									chunk.decrunched_len,
//									(ULONG)chunk.checksum,
//									(ULONG)chunk.headermark,
//									(ULONG)chunk.dontknow02,
//									(ULONG)chunk.flags,
//									(ULONG)chunk.crunchmethod,
//									(ULONG)chunk.destinplane,
//									(ULONG)chunk.referenceplane);
						
						/* The first chunk is assumed to be the header */
						if (bm==NULL)
						{
							struct DeBoxInfo *info=(struct DeBoxInfo*)data;
							
							/* Fill in the size information */
							bmhd->bmh_Width  = bmhd->bmh_PageWidth  = info->width;
							bmhd->bmh_Height = bmhd->bmh_PageHeight = info->height;
							bmhd->bmh_Depth  = info->depth;
							
							/* Compute the mode ID */
							modeid = info->modeid & (HIRES|HAM|EXTRA_HALFBRITE|SUPERHIRES|DOUBLESCAN|LACE);
							
							/* Set the number of colors */
							setdtattrs (cb, o, PDTA_NumColors, info->numcolors, TAG_DONE);
							
							/* Get the destination for the color information */
							getdtattrs (cb, o,
								PDTA_ColorRegisters, (ULONG)&cmap,
								PDTA_CRegs, &cregs,
								TAG_DONE);
							
							/* Get pointer to palette array */
							colors=(UWORD*)(data+info->palette_offset);
							
							/* Set the color information */
							for (n=0; n < info->numcolors; n++)
							{
								/* Set the master color table */
								cmap->red = ((*colors&0xF00)>>4)|((*colors&0xF00)>>8);
								cmap->green=((*colors&0x0F0)   )|((*colors&0x0F0)>>4);
								cmap->blue =((*colors&0x00F)<<4)|((*colors&0x00F)   );
								
								/* Set the color table used for remapping */
								cregs[n * 3 + 0] = cmap->red   << 24;
								cregs[n * 3 + 1] = cmap->green << 24;
								cregs[n * 3 + 2] = cmap->blue  << 24;
								
								colors++;
								cmap++;
							}
							
							/* Allocate the bitmap */
							if (!(bm = AllocBitMap (bmhd->bmh_Width, bmhd->bmh_Height, bmhd->bmh_Depth, BMF_CLEAR, NULL)))
							{
								SetIoErr(ERROR_NO_FREE_STORE);
								Success=FALSE;
								break;
							}
						}
						else
						{
							UBYTE *sourcedata=data;
							ULONG sourcesize;
							
							UBYTE *destinplane=NULL;
							ULONG destinsize;
//							ULONG extrasize=0;
							
							UBYTE *referenceplane=NULL;
							
							/* Get (corrected) size of chunk */
							sourcesize=chunk.chunk_len;
							if (chunk.flags&FLGF_SUBTRACT_ONE_BYTE) sourcesize--;
							
							/* Get size of destination buffer */
							destinsize=chunk.decrunched_len;
							
							/* Check and get reference plane */
							if (chunk.referenceplane>=bmhd->bmh_Depth)
							{
								SetIoErr(ERROR_BAD_HUNK);
								Success=FALSE;
								break;
							}
							referenceplane=bm->Planes[chunk.referenceplane];
							
							if (chunk.flags&FLGF_USE_EXTRA_BUFFER)
							{
								Message(cb,	"This image file uses an unimplemented feature.\n"
											"Please contact the author of this datatype\n"
											"(C.Buchner) and send him *this* image file.");
								SetIoErr(ERROR_BAD_HUNK);
								Success=FALSE;
								break;
								
//								/* Allocate an extra buffer */
//								destinsize=*((ULONG*)sourcedata);
//								sourcesize-=sizeof(ULONG);
//								sourcedata+=sizeof(ULONG);
//								Message(cb,"Allocating extra buffer (%ld)...",destinsize);
//								if (!(destinplane=AllocVec(destinsize,MEMF_CLEAR)))
//								{
//									Message(cb,"No memory for extra buffer!");
//									SetIoErr(ERROR_NO_FREE_STORE);
//									Success=FALSE;
//									break;
//								}
//								extrasize=destinsize;
							}
							else
							{
								/* No extra buffer, check and get destination plane */
								if (chunk.destinplane>=bmhd->bmh_Depth)
								{
									SetIoErr(ERROR_BAD_HUNK);
									Success=FALSE;
									break;
								}
								destinplane=bm->Planes[chunk.destinplane];
							}
							
							/* Handle the different crunch methods */
							switch(chunk.crunchmethod)
							{
								case CRUNCHMETHOD_PLAIN:
								{
									CopyMem(sourcedata,destinplane,destinsize);
									break;
								}
								case CRUNCHMETHOD_1:
								{
									Method_1(sourcedata,sourcesize,referenceplane,destinplane,destinsize);
									break;
								}
								case CRUNCHMETHOD_2:
								{
									Method_2(sourcedata,sourcesize,referenceplane,destinplane,destinsize);
									break;
								}
								case CRUNCHMETHOD_FILL:
								{
									if (sourcesize!=1)
									{
										SetIoErr(ERROR_BAD_HUNK);
										Success=FALSE;
										break;
									}
									else
									{
										memset(destinplane,destinsize,*sourcedata);
									}
									break;
								}
								case CRUNCHMETHOD_4:
								{
									Method_4(sourcedata,sourcesize,referenceplane,destinplane,destinsize);
									break;
								}
								case CRUNCHMETHOD_5:
								{
									Method_5(sourcedata,sourcesize,referenceplane,destinplane,destinsize);
									break;
								}
								default:
								{
									SetIoErr(ERROR_BAD_HUNK);
									Success=FALSE;
									break;
								}
							}
							
							if (Success)
							{
								if (chunk.flags&FLGF_XOR_REFERENCE_INTO_DEST)
								{
									XOR_Memory(referenceplane,destinplane,destinsize);
								}
							}
							
							if (!(chunk.flags&FLGF_ANOTHER_CHUNK_FOLLOWS))
							{
								break;
							}
						}
					}
					FreeVec(data);
					data=NULL;
				}
			}
		}
		/* just in case we broke out of the while loop */
		if (data)
		{
			FreeVec(data);
			data=NULL;
		}
	}
	if (Success)
	{
		if (bm)
		{
			/* Set the attributes */
			setdtattrs (cb, o,
				DTA_ObjName,		title,
				DTA_NominalHoriz,	bmhd->bmh_Width,
				DTA_NominalVert,	bmhd->bmh_Height,
				PDTA_BitMap,		bm,
				PDTA_ModeID,		modeid,
				TAG_DONE);
		}
	}
	else
	{
		if (bm) FreeBitMap(bm);
	}
	return(Success);
}
