/* Programmheader

	Name:		dispatch.c
	Main:		reko
	Versionstring:	$VER: dispatch.c 1.10 (18.12.2001)
	Author:		SDI
	Distribution:	Freeware
	Description:	the real stuff

 1.0   19.09.99 : first version
 1.1   02.10.99 : added PC stuff
 1.2   03.10.99 : some optimizations
 1.3   10.10.99 : added HAM conversion
 1.4   14.10.99 : added ENV variable support
 1.5   17.10.99 : now passes ModeID in HighColor system
 1.6   20.10.99 : added HAM6 mode and card clear
 1.7   11.11.99 : Now the data is told to be 24 bit!
 1.8   10.12.99 : added 16BIT/24BIT keywords
 1.9   04.11.01 : added support for layout of older reko datatypes
 1.10  18.12.01 : fixed Preview for RKP8 and RKP16
*/

#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/datatypes.h>
#include <proto/utility.h>
#include <clib/alib_protos.h>
#include <datatypes/pictureclass.h>
#include <libraries/iffparse.h>
#include <exec/memory.h>
#include <datatypes/pictureclass.h>

#include "SDI_compiler.h"
#include "reko.h"

#define DOSBase		cb->cb_DOSBase
#define SysBase		cb->cb_SysBase
#define DataTypesBase	cb->cb_DataTypesBase
#define UtilityBase	cb->cb_UtilityBase
#define GfxBase		cb->cb_GfxBase
#define IntuitionBase	cb->cb_IntuitionBase

#define REKOFLAG_SOLITON	(1<<0)
#define REKOFLAG_PREVIEW	(1<<1)
#define REKOFLAG_HAMDIRECT	(1<<2)
#define REKOFLAG_16BIT		(1<<3)
#define REKOFLAG_REKODT39	(1<<4)
#define REKOFLAG_MREKO		(1<<5)

#define REKOFLAG_PCMODE		(1<<6)

struct RekoHeader /* REKO file header */
{
  ULONG rh_ID;
  ULONG rh_BodySize;
  ULONG rh_CardSize;
  UWORD rh_Height;
  UWORD rh_Width;
  ULONG rh_ModeID;
  UBYTE rh_Depth;
  UBYTE rh_CardsCnt;
};

struct PCRekoHeader /* PCREKO file header */
{ /* all in INTEL format */
  UWORD rh_PCID;
  ULONG rh_ID;
  UWORD rh_ID2;
  ULONG rh_BodySize; /* FileSize-22 */
  ULONG rh_CardSize;
  UWORD rh_Width;
  UWORD rh_Height;
  UBYTE rh_Depth;
  UBYTE rh_CardsCnt;
};

#define ID_REKO			MAKE_ID('R','E','K','O')
#define ID_PC			0x5043
#define HEADER_SIZE		sizeof(struct RekoHeader)

#define FULLHEIGHT		4  /* Number of cards vertically */
#define REKO_I			55 /* Cards in REKO I cardset */
#define REKO_II			59 /* Cards in REKO II cardset */
#define REKO_III		68 /* Cards in REKO III cardset */

#define BORDERCOL	0xF0
#define BACKCOL		0

static UBYTE Mapping[REKO_III][2] = {
  {13,2}, {13,1}, {13,0},
  { 0,0}, { 0,1}, { 0,2}, { 0,3},
  { 1,0}, { 1,1}, { 1,2}, { 1,3},
  { 2,0}, { 2,1}, { 2,2}, { 2,3},
  { 3,0}, { 3,1}, { 3,2}, { 3,3},
  { 4,0}, { 4,1}, { 4,2}, { 4,3},
  { 5,0}, { 5,1}, { 5,2}, { 5,3},
  { 6,0}, { 6,1}, { 6,2}, { 6,3},
  { 7,0}, { 7,1}, { 7,2}, { 7,3},
  { 8,0}, { 8,1}, { 8,2}, { 8,3},
  { 9,0}, { 9,1}, { 9,2}, { 9,3},
  {10,0}, {10,1}, {10,2}, {10,3},
  {11,0}, {11,1}, {11,2}, {11,3},
  {12,0}, {12,1}, {12,2}, {12,3},
  {13,3}, {14,3}, {15,3}, {16,3},
  {14,0}, {15,0}, {16,0},
  {14,1}, {15,1}, {16,1},
  {14,2}, {15,2}, {16,2}
};

ASM(Object *) Dispatch(REG(a0, Class *cl), REG(a2, Object *o), REG(a1, Msg msg));

Class *initClass(struct ClassBase *cb)
{
  Class *CL;

  /* Create our class (no instance) */
  if((CL=MakeClass(CLASSNAME,PICTUREDTCLASS,NULL,NULL,0)))
  {
    CL->cl_Dispatcher.h_Entry=(HOOKFUNC)Dispatch;
    CL->cl_UserData=(ULONG) cb;
    AddClass(CL);
  }
  return CL;
}

/* Calculate number of cards horizontally */
static ULONG GetFullWidth(ULONG cards)
{
  ULONG HCnt;

  if(cards <= REKO_I) /* REKO-I cardset */
    HCnt=14;
  else if(cards <= REKO_III) /* REKO-II or REKO-III cardset */
    HCnt=17;
  else /* Unknown cardset */
    HCnt=cards/FULLHEIGHT+(cards%FULLHEIGHT>0);
  return HCnt;
}

static void MakeBackCard(STRPTR buf, ULONG width, ULONG height)
{
  ULONG j;
  STRPTR b;

  b = buf;
  for(j = width*height*3; j; --j) /* clear background */
    *(b++) = BACKCOL;

  b = buf+3;
  for(j = (width-2)*3; j; --j) /* upper border */
    *(b++) = BORDERCOL;

  b = buf+((width*(height-1))+1)*3;
  for(j = (width-2)*3; j; --j) /* bottom border */
    *(b++) = BORDERCOL;

  b = buf + width*3;
  for(j = height-2; j ; --j) /* side borders */
  {
    b[0] = b[1] = b[2] = BORDERCOL;
    b += width*3;
    *(b-3) = *(b-2) = *(b-1) = BORDERCOL;
  }
  
  b = buf + width*3;
  b[3] = b[4] = b[5] = BORDERCOL;
  b += width*3;
  *(b-6) = *(b-5) = *(b-4) = BORDERCOL;
  b = buf + width*(height-2)*3;
  b[3] = b[4] = b[5] = BORDERCOL;
  b += width*3;
  *(b-6) = *(b-5) = *(b-4) = BORDERCOL;
}

static void GetXY(ULONG num, ULONG *x, ULONG *y, ULONG flags)
{
  if(flags & REKOFLAG_PREVIEW)
  {
    *x = *y = 0;
  }
  else if(num < REKO_III)
  {
    *x = Mapping[num][0];
    *y = Mapping[num][1];
    if(num < 3)
    {
      if(flags & REKOFLAG_MREKO)
      {
        switch(num)
        {
        case 0: *y = 1; break;
        case 1: *y = 2; break;
        case 2: *y = 3; break;
        };
      }
      else if(flags & REKOFLAG_REKODT39)
      {
        *y = 1;
        switch(num)
        {
        case 0: *x = 13; break;
        case 1: *x = 14; break;
        case 2: *x = 15; break;
        }
      }
    }
    else if(num >= REKO_I && (flags & (REKOFLAG_MREKO|REKOFLAG_REKODT39)))
    {
      if(num < REKO_II) /* stack cards */
        *y = 0;
      else if((flags & REKOFLAG_REKODT39) && num < REKO_II+2)
      {
        *x = 13; *y = num-REKO_II+2;
      }
      else
        (*y)++;
    }
  }
  else
  {
    *x = num/FULLHEIGHT;
    *y = num%FULLHEIGHT;
  }
}

static void ClearCards(ULONG cnt, struct ClassBase *cb, Class *cl, Object *o, APTR buf, ULONG w, ULONG h, ULONG flags)
{
  ULONG maxcnt, x, y;
  STRPTR b;

  if(flags & REKOFLAG_PREVIEW)
    return;

  b = (STRPTR) buf;
  for(maxcnt = w*h*3; maxcnt; --maxcnt) /* clear card */
    *(b++) = BACKCOL;

  maxcnt = GetFullWidth(cnt)*FULLHEIGHT;

  if(flags & REKOFLAG_PCMODE)
  {
    GetXY(0, &x, &y, flags);
    DoSuperMethod(cl, o, PDTM_WRITEPIXELARRAY, (ULONG) buf, PBPAFMT_RGB, w*3, x*w, y*h, w, h);
  }

  while(cnt < maxcnt)
  {
    GetXY(cnt++, &x, &y, flags);
    DoSuperMethod(cl, o, PDTM_WRITEPIXELARRAY, (ULONG) buf, PBPAFMT_RGB, w*3, x*w, y*h, w, h);
  }
}

#define EndConvI32(a)	(((a)>>24)|(((a)>>8)&0xFF00)|(((a)<<8)&0xFF0000)|((a)<<24))
#define EndConvI16(a)	((UWORD)(((a)>>8)|((a)<<8)))

static LONG GetPCREKO(struct ClassBase *cb, Class *cl, Object *o, BPTR fh,
struct BitMapHeader *bmhd, struct PCRekoHeader *h, STRPTR Title, ULONG flags)
{
  register ULONG CSize;
  LONG result = ERROR_OBJECT_WRONG_TYPE;

  flags |= REKOFLAG_PCMODE;
  h->rh_CardSize = EndConvI32(h->rh_CardSize);
  h->rh_Width = EndConvI16(h->rh_Width);
  h->rh_Height = EndConvI16(h->rh_Height);
  if(flags & REKOFLAG_SOLITON)
  {
    if(h->rh_CardsCnt > REKO_I-2)
      h->rh_CardsCnt = REKO_I-2;
  }
  else if(flags & REKOFLAG_PREVIEW)
    h->rh_CardsCnt = 5;
  /* Calculate sizes */
  CSize = h->rh_Width*h->rh_Height*(h->rh_Depth>>3);
  if(h->rh_CardsCnt && CSize && (h->rh_Depth == 8 || h->rh_Depth == 16) && h->rh_CardSize == CSize)
  {
    LONG ErrorLevel = 0, ErrorNumber = 0;
    ULONG ModeID;

     /* Fill in BitMapHeader information */
    bmhd->bmh_Width = bmhd->bmh_PageWidth = h->rh_Width*(flags & REKOFLAG_PREVIEW ? 1 : GetFullWidth(h->rh_CardsCnt));
    bmhd->bmh_Height = bmhd->bmh_PageHeight = h->rh_Height*(flags & REKOFLAG_PREVIEW ? 1 : FULLHEIGHT);
    bmhd->bmh_Depth = flags & REKOFLAG_16BIT ? 16 : 24;

    ModeID=BestModeID(BIDTAG_DesiredWidth, bmhd->bmh_Width, BIDTAG_DesiredHeight, bmhd->bmh_Height,
    BIDTAG_Depth, bmhd->bmh_Depth, TAG_DONE);

    SetDTAttrs(o, 0, 0, DTA_ObjName, Title, DTA_NominalHoriz, bmhd->bmh_Width, DTA_NominalVert, bmhd->bmh_Height,
    PDTA_SourceMode, PMODE_V43, PDTA_ModeID, ModeID, DTA_ErrorLevel, &ErrorLevel, DTA_ErrorNumber, &ErrorNumber, TAG_DONE);

    if(ErrorLevel)
      result = ErrorNumber;
    else if(h->rh_Depth == 16)
    {
      register UBYTE *d, *e;

      CSize = h->rh_Width*h->rh_Height*2+4;

      if((d = (UBYTE *) AllocVec(CSize+h->rh_Width*h->rh_Height*3, MEMF_PUBLIC)))
      {
	ULONG x, y;
        register ULONG i, j, card = 0;

	result = 0;
        e = d + CSize;
	  
	while(!result && card < h->rh_CardsCnt)
	{
	  if(Read(fh, d, CSize) != CSize)
	    result = ERROR_OBJECT_WRONG_TYPE;
	  else
	  {
	    j = 0;
	    for(i = 4; i < CSize; i += 2)
	    {
	      e[j++] = (d[i+1]<<1)&0xF8;		/* red */
	      e[j++] = (d[i+1]<<6 | d[i]>>2)&0xF8;	/* green */
	      e[j++] = (d[i]<<3)&0xF8;			/* blue */
	    }

	    GetXY(card+2, &x, &y, flags);
            DoSuperMethod(cl, o, PDTM_WRITEPIXELARRAY, (ULONG) e, PBPAFMT_RGB,
            h->rh_Width*3, x*h->rh_Width, y*h->rh_Height, h->rh_Width, h->rh_Height);
          }
          ++card;
        }
        if(!result && !(flags & REKOFLAG_PREVIEW))
        {
	  MakeBackCard(e, h->rh_Width, h->rh_Height);
	  GetXY(1, &x, &y, flags);
          DoSuperMethod(cl, o, PDTM_WRITEPIXELARRAY, (ULONG) e, PBPAFMT_RGB,
          h->rh_Width*3, x*h->rh_Width, y*h->rh_Height, h->rh_Width, h->rh_Height);
	  ClearCards(h->rh_CardsCnt+2, cb, cl, o, e, h->rh_Width, h->rh_Height, flags);
	}
	FreeVec(d);
      } /* AllocVec */
      else
	result = ERROR_NO_FREE_STORE;
    }
    else if(h->rh_Depth == 8)
    {
      register UBYTE *d, *e, *f, *g;

      CSize = h->rh_Width*h->rh_Height;

      if((d = (UBYTE *) AllocVec(CSize+256*3+h->rh_Width*h->rh_Height*3, MEMF_PUBLIC)))
      {
	ULONG x, y;
        register ULONG i, j, card = 0;

	result = 0;
        e = d + 256*3 + CSize;
        f = d + CSize;
	  
	while(!result && card < h->rh_CardsCnt)
	{
	  if(Read(fh, d, 256*2+4) != 256*2+4)
	    result = ERROR_OBJECT_WRONG_TYPE;
	  else
	  {
	    j = 0;
	    for(i = 4; i < (256)*2+4; i += 2)
	    {
	      f[j++] = (d[i+1]<<1)&0xF8;		/* red */
	      f[j++] = (d[i+1]<<6 | d[i]>>2)&0xF8;	/* green */
	      f[j++] = (d[i]<<3)&0xF8;			/* blue */
	    }
	    if(Read(fh, d, CSize) != CSize)
	      result = ERROR_OBJECT_WRONG_TYPE;
	    else
	    {
	      for(i = j = 0; i < CSize; ++i)
	      {
	        g = f + 3*d[i];
	        e[j++] = *(g++);
	        e[j++] = *(g++);
	        e[j++] = *g;
	      }

	      GetXY(card+2, &x, &y, flags);
              DoSuperMethod(cl, o, PDTM_WRITEPIXELARRAY, (ULONG) e, PBPAFMT_RGB,
              h->rh_Width*3, x*h->rh_Width, y*h->rh_Height, h->rh_Width, h->rh_Height);
            }
          }
          ++card;
        } /* while */
        if(!result && !(flags & REKOFLAG_PREVIEW))
	{
	  MakeBackCard(e, h->rh_Width, h->rh_Height);
	  GetXY(1, &x, &y, flags);
          DoSuperMethod(cl, o, PDTM_WRITEPIXELARRAY, (ULONG) e, PBPAFMT_RGB,
          h->rh_Width*3, x*h->rh_Width, y*h->rh_Height, h->rh_Width, h->rh_Height);
	  ClearCards(h->rh_CardsCnt+2, cb, cl, o, e, h->rh_Width, h->rh_Height, flags);
	}
	FreeVec(d);
      } /* AllocVec */
      else
        result = ERROR_NO_FREE_STORE;
    } /* Error ? */
  } /* data checks */
  return result;
}

static LONG MakeHAM(struct ClassBase *cb, Class *cl, Object *o, BPTR fh,
struct BitMapHeader *bmhd, struct RekoHeader *h, STRPTR Title, ULONG flags)
{
  ULONG CSize;
  LONG result = ERROR_OBJECT_WRONG_TYPE;

  /* Calculate sizes */
  CSize = (h->rh_Width*h->rh_Height*h->rh_Depth)>>3;
  if(h->rh_CardsCnt && CSize && h->rh_CardSize == CSize)
  {
    LONG ErrorLevel = 0, ErrorNumber = 0;
    ULONG ModeID;

     /* Fill in BitMapHeader information */
    bmhd->bmh_Width = bmhd->bmh_PageWidth = h->rh_Width*(flags & REKOFLAG_PREVIEW ? 1 : GetFullWidth(h->rh_CardsCnt));
    bmhd->bmh_Height = bmhd->bmh_PageHeight = h->rh_Height*(flags & REKOFLAG_PREVIEW ? 1 : FULLHEIGHT);
    bmhd->bmh_Depth = flags & REKOFLAG_16BIT ? 16 : 24;

    ModeID=BestModeID(BIDTAG_DesiredWidth, bmhd->bmh_Width, BIDTAG_DesiredHeight, bmhd->bmh_Height,
    BIDTAG_Depth, bmhd->bmh_Depth, TAG_DONE);

    SetDTAttrs(o, 0, 0, DTA_ObjName, Title, DTA_NominalHoriz, bmhd->bmh_Width, DTA_NominalVert, bmhd->bmh_Height,
    PDTA_SourceMode, PMODE_V43, PDTA_ModeID, ModeID, DTA_ErrorLevel, &ErrorLevel, DTA_ErrorNumber, &ErrorNumber, TAG_DONE);

    if(ErrorLevel)
      result = ErrorNumber;
    else if(h->rh_Depth == 8 || h->rh_Depth == 6)
    {
      UBYTE *Cr;
      ULONG i, dep;

      dep = h->rh_Depth;
      i = (1<<(dep-2))*3;
      if((Cr = (UBYTE *) AllocVec(i, MEMF_PUBLIC)))
      {
        if(Read(fh, Cr, i) == i)
        {
          register UBYTE *d, *e;

          if((d = (UBYTE *) AllocVec(CSize+h->rh_Width*h->rh_Height*3, MEMF_PUBLIC)))
          {
            ULONG j, k, l, m, card = 0, u;

	    result = 0;
	  
            l = h->rh_Width >> 3;
            u = dep*l;
	    while(!result && card < h->rh_CardsCnt)
	    {
	      if(Read(fh, d, CSize) != CSize)
	        result = ERROR_OBJECT_WRONG_TYPE;
	      else
	      {
	        ULONG x, y, v;
	        UBYTE *planar[8], image, last[3];

                e = d + CSize;

	        for(j = v = 0; j < h->rh_Height; ++j, v += u)
	        {
	          for(k = m = 0; k < dep; ++k, m += l)
	            planar[k] = d + m + v;

		  last[0] = Cr[0];
		  last[1] = Cr[1];
		  last[2] = Cr[2];
		  for(m = l; m; --m)
		  {
		    for(i = (1<<7); i; i >>= 1)
		    {
		      image = 0;
		      for(k = 0; k < dep; ++k)
		      {
		        image >>= 1;
		        image |= (*planar[k] & i) ? (1<<7) : 0;
		      }
		      k = image >> 6;
		      image <<= 2;
		      switch(k)
		      {
		      case 0: image >>= (8-dep+2); last[0] = Cr[image*3]; last[1] = Cr[image*3+1]; last[2] = Cr[image*3+2]; break;
		      case 1: last[2] = image; break;
		      case 2: last[0] = image; break;
		      case 3: last[1] = image; break;
		      }
		      *(e++) = last[0];
		      *(e++) = last[1];
		      *(e++) = last[2];
		    }
		    for(k = 0; k < dep; k++)
		      planar[k]++;
	          }
                }

	        GetXY(card, &x, &y, flags);
                DoSuperMethod(cl, o, PDTM_WRITEPIXELARRAY, (ULONG) (d+CSize), PBPAFMT_RGB,
                h->rh_Width*3, x*h->rh_Width, y*h->rh_Height, h->rh_Width, h->rh_Height);
              }
              ++card;
            }
            if(!result)
	      ClearCards(h->rh_CardsCnt, cb, cl, o, d+CSize, h->rh_Width, h->rh_Height, flags);
            
	    FreeVec(d);
          } /* AllocVec */
          else
	    result = ERROR_NO_FREE_STORE;
	} /* Read */
      } /* AllocVec */
      else
        result = ERROR_NO_FREE_STORE;
    }
  } /* data checks */
  return result;
}

static LONG GetREKO(struct ClassBase *cb, Class *cl, Object *o, struct TagItem *attrs, ULONG flags)
{
  struct RekoHeader Header;
  LONG result = ERROR_OBJECT_WRONG_TYPE;
  struct BitMapHeader *bmhd = 0;
  BPTR fh = 0;
  STRPTR Title; /* Picture name */
  ULONG LSize, CSize, NumCol, ModeID;
  ULONG *MethodArray;

  Title = (STRPTR)GetTagData(DTA_Name, 0, attrs);
  GetDTAttrs(o, DTA_Handle, &fh, PDTA_BitMapHeader, &bmhd, DTA_Methods, &MethodArray, TAG_DONE);

  /* Now we search for the new PDTM_WRITEPIXELARRAY method to determine
     if it's the new V43 picture.datatype */
  while(*MethodArray != ~0 && *MethodArray != PDTM_WRITEPIXELARRAY)
    ++MethodArray;

  if(fh && bmhd)
  {
    if(Seek(fh, 0, OFFSET_BEGINNING)>=0)
    {
      if(Read(fh, &Header, HEADER_SIZE) == HEADER_SIZE)
      {
        if((((struct PCRekoHeader *) &Header)->rh_PCID == ID_PC) && (((struct PCRekoHeader *) &Header)->rh_ID == ID_REKO)
        && (*MethodArray == PDTM_WRITEPIXELARRAY))
	  result = GetPCREKO(cb, cl, o, fh, bmhd, (struct PCRekoHeader *) &Header, Title, flags);
	else if(Header.rh_ID == ID_REKO)
	{
	  if(flags & REKOFLAG_SOLITON)
	  {
            if(Header.rh_CardsCnt > REKO_I)
              Header.rh_CardsCnt = REKO_I;
          }
          else if(flags & REKOFLAG_REKODT39)
          {
            if(Header.rh_CardsCnt > REKO_II)
              Header.rh_CardsCnt = REKO_II;
          }
          else if(flags & REKOFLAG_PREVIEW)
            Header.rh_CardsCnt = 7;
	  if(Header.rh_ModeID & HAM_KEY)
	  {
	    if((*MethodArray == PDTM_WRITEPIXELARRAY) && !(flags & REKOFLAG_HAMDIRECT))
	      return MakeHAM(cb, cl, o, fh, bmhd, &Header, Title, flags);
	    else if(flags & REKOFLAG_SOLITON)
	      return 0;
	  }
	  /* Calculate sizes */
	  NumCol = 1<<((Header.rh_ModeID & HAM_KEY) ? Header.rh_Depth-2 : Header.rh_Depth);
	  LSize = Header.rh_Width>>3; /* Width in bytes */
	  CSize = LSize*Header.rh_Height*Header.rh_Depth; /* Card size in bytes */
	  if(Header.rh_CardsCnt && CSize && Header.rh_Depth>=3 && Header.rh_CardSize == CSize)
	  {
	    /* Fill in BitMapHeader information */
	    bmhd->bmh_Width = bmhd->bmh_PageWidth = Header.rh_Width*(flags & REKOFLAG_PREVIEW ? 1 :
	    GetFullWidth(Header.rh_CardsCnt));
	    bmhd->bmh_Height = bmhd->bmh_PageHeight = Header.rh_Height*(flags & REKOFLAG_PREVIEW ? 1 : FULLHEIGHT);
	    bmhd->bmh_Depth = Header.rh_Depth;

	    /* Get display mode id */
	    if((ModeID=BestModeID(Header.rh_ModeID & HAM_KEY ? BIDTAG_DIPFMustHave : TAG_IGNORE, DIPF_IS_HAM,
	    BIDTAG_DesiredWidth, bmhd->bmh_Width, BIDTAG_DesiredHeight, bmhd->bmh_Height,
	    BIDTAG_Depth, Header.rh_Depth, TAG_DONE)) != INVALID_ID)
	    {
  	      UBYTE *CMap = 0;		/* in real "struct ColorRegister *" */
	      ULONG *CRegs = 0;

	      /* Set colors */
	      SetDTAttrs(o,NULL,NULL,PDTA_NumColors,NumCol,TAG_DONE);
	      GetDTAttrs(o,PDTA_ColorRegisters,&CMap,PDTA_CRegs,&CRegs,TAG_DONE);
	      if(CMap && CRegs)
	      {
	        NumCol *= 3;
	        if(Read(fh,CMap,NumCol)==NumCol)
	        {
	          register ULONG i;
		  struct BitMap *bm;
		  register ULONG *a;
		  register UBYTE *b;
		  
		  a = CRegs;
		  b = CMap;

		  while(NumCol--)
  		  {
		    i = *(b++);
		    i = (i<<8)+i;
		    *(a++) = (i<<16)+i;
		  }

	          /* Prepare bitmap */
	          if((bm=AllocBitMap(bmhd->bmh_Width, bmhd->bmh_Height, Header.rh_Depth,
	          (BMF_CLEAR | BMF_INTERLEAVED | BMF_DISPLAYABLE), 0)))
	          {
		    STRPTR Buffer;

	            /* Allocate temporary buffer */
		    if((Buffer = (STRPTR) AllocVec(CSize, MEMF_PUBLIC)))
		    {
		      result = 0;
		      /* Read data in 1-card chunks */
		      for(i=0; !result && i < Header.rh_CardsCnt; ++i)
		      {
			if(Read(fh,Buffer,CSize) != CSize)
			  result = ERROR_OBJECT_WRONG_TYPE;
			else
			{
			  ULONG x,y;
			  register ULONG k, j, DstOffs, bpr, h;

  			  bpr = bm->BytesPerRow;
  			  b = Buffer;
  			  h = Header.rh_Height;

  			  GetXY(i,&x,&y, flags);
  			  DstOffs=y*h*bpr+x*LSize;
			  while(h--)
			  {
			    for(k=0; k < Header.rh_Depth; k++)
    			    {
      			      for(j = 0; j < LSize; ++j)
        			bm->Planes[k][DstOffs+j] = *(b++);
    			    }
    			    DstOffs += bpr;
			  }
			}
		      }
	              FreeVec(Buffer);

		      /* Set attributes of destination picture */
		      if(!result)
		        SetDTAttrs(o, 0, 0, DTA_ObjName, Title, DTA_NominalHoriz, bmhd->bmh_Width,
			DTA_NominalVert, bmhd->bmh_Height, PDTA_BitMap, bm, PDTA_ModeID, ModeID, TAG_DONE);
		    } /* AllocVec */
		    else
		      result = ERROR_NO_FREE_STORE;
		    if(result)
	              FreeBitMap(bm);
	          } /* AllocBitmap */
	        } /* Read colortable */
	      } /* CMap && CRegs */
	    } /* BestModeID */
	  } /* data checks */
	} /* ID_REKO ? */
      } /* read header */
    } /* seek to begin */
  } /* fh && bmhd */
  return result;
}

ASM(Object *) Dispatch(REG(a0, Class *cl), REG(a2, Object *o), REG(a1, Msg msg))
{
  struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
  Object *Obj;

  switch(msg->MethodID)
  {
    case OM_NEW: /* We know this method */
      if((Obj = (Object *)DoSuperMethodA(cl,o,msg)))
      {
        UBYTE dtmode[50] = "NORMAL", *ptr;
        ULONG flags = 0;

        GetVar("REKODTMODE", (STRPTR) &dtmode, 50, GVF_GLOBAL_ONLY);
        ptr = dtmode;
        while(*ptr)
        {
          switch(*ptr)
          {
          case 'm': case 'M': flags |= REKOFLAG_MREKO; break;           /* MREKOMODE */
          case 'r': case 'R': flags |= REKOFLAG_REKODT39; break;        /* REKODT39MODE */
          case 'p': case 'P': flags |= REKOFLAG_PREVIEW; break;		/* PREVIEW */
          case 's': case 'S': flags |= REKOFLAG_SOLITON; break;		/* SOLITON */
          case 'h': case 'H': flags |= REKOFLAG_HAMDIRECT; break;	/* HAMDIRECT */
          case '1':	      flags |= REKOFLAG_16BIT; break;		/* 16BIT */
       /* case '2': */							/* 24BIT */
       /* case 'n': case 'N': */					/* NORMAL */
          }
          while(*ptr && *(ptr++) != ',')
            ;
        }
        if(flags & REKOFLAG_SOLITON)
          flags &= ~(REKOFLAG_HAMDIRECT|REKOFLAG_MREKO|REKOFLAG_REKODT39|REKOFLAG_PREVIEW);
        else if(flags & REKOFLAG_PREVIEW)
          flags &= ~(REKOFLAG_REKODT39|REKOFLAG_MREKO);
        else if(flags & REKOFLAG_MREKO)
          flags &= ~(REKOFLAG_REKODT39);

        if((flags = GetREKO(cb, cl, Obj, ((struct opSet *)msg)->ops_AttrList, flags)))
        {
          SetIoErr(flags);
          CoerceMethod(cl, Obj, OM_DISPOSE);
          return 0;
        }
        else
          SetIoErr(0);
      }
      break;
    default: /* Let the superclass handle it */
      Obj = (Object *)DoSuperMethodA(cl, o, msg);
      break;
  }
  return Obj;
}
