/*
 * ifficon.c   V1.4
 *
 * IFF brush handling
 *
 * (c) 1991 by Stefan Becker
 *
 */
#include "ToolManager.h"

/* Our own DiskObject definition */
struct IFFDObj {
                struct DiskObject id_dobj;
                struct Image      id_img;
                ULONG             id_size; /* size of image */
               };

/* Bitmap header (BMHD) structure */
struct BitMapHeader {
                     UWORD w,h;                   /* Width, height in pixels */
                     WORD  x,y;
                     UBYTE nplanes;               /* Number of planes */
                     UBYTE Masking;               /* Masking */
                     UBYTE Compression;           /* Compression algorithm */
                     UBYTE pad1;
                     UWORD TransparentColor;
                     UBYTE XAspect,YAspect;
                     WORD  PageWidth, PageHeight;
};
#define MSK_HASMASK   1
#define COMP_NO       0
#define COMP_BYTERUN1 1
#define BPR(w)        ((((w)+15)>>4)<<1) /* Bytes per row formula */

/* IFF ID's */
#define ID_ILBM MAKE_ID('I','L','B','M')
#define ID_BMHD MAKE_ID('B','M','H','D')
#define ID_BODY MAKE_ID('B','O','D','Y')

/* miscellaneous */
struct Library *IFFParseBase;

/* Read BODY chunk into memory and convert it to an image */
static BOOL ReadBODY(struct IFFHandle *iff, struct BitMapHeader *bmhd,
                     UBYTE *dest, ULONG bpr, ULONG planeoff)
{
 register UBYTE *sp,*dp;
 register ULONG row;
 UBYTE *src;
 ULONG planes=bmhd->nplanes;
 ULONG size=CurrentChunk(iff)->cn_Size;
 BOOL ncomp=bmhd->Compression==COMP_NO;
 BOOL mask=bmhd->Masking==MSK_HASMASK;
 BOOL rc=TRUE;

 /* Get memory for BODY chunk contents */
 if (!(sp=src=malloc(size))) goto rbe1;

 /* Read in the complete BODY chunk */
 if (ReadChunkBytes(iff,src,size)!=size) goto rbe2;

 for (row=0; row<bmhd->h; row++)        /* Read data row by row */
  {
   register ULONG pl,rem;
   register char k;

   for (pl=0; pl<planes; pl++)          /* Read data plane by plane */
    {
     dp=dest+pl*planeoff+row*bpr;       /* Calculate destination pointer */

     if (ncomp)
      for (k=bpr; k--;) *dp++=*sp++;    /* No compression */
     else
      {                                 /* Byte Run compression */
       rem=bpr;

       while (rem)                      /* Row not completed */
        {
         k=*sp++;                       /* Read compression code */

         if (k>=0)
          {                             /* Literal copy */
           if ((rem-=++k)<0) goto rbe2; /* Error in de-compression? */

           while (k--) *dp++=*sp++;     /* Copy the following k+1 bytes */
          }
         else if (k!=-128)              /* Code -128 == No Operation */
          {                             /* Byte run encoded */
           register UBYTE byte;

           k=-k;
           if ((rem-=++k)<0) goto rbe2; /* Error in de-compression? */

           byte=*sp++;                  /* Get byte */
           while (k--) *dp++=byte;      /* Copy this byte -k+1 times */
          }
        }
      }
    }

   if (mask)                            /* Skip mask plane */
    if (ncomp)
     sp+=bpr;                           /* No compression */
    else
     {                                  /* Byte Run compression */
      rem=bpr;

      while (rem)                       /* Row not completed */
       {
        k=*sp++;                        /* Read compression code */

        if (k>=0)
         {                              /* Literal copy */
          if ((rem-=++k)<0) goto rbe2;  /* Error in de-compression? */
          sp+=k;                        /* Skip the following k+1 bytes */
         }
        else if (k!=-128)               /* Code -128 == No Operation */
         {                              /* Byte run encoded */
          k=-k;
          if ((rem-=++k)<0) goto rbe2;  /* Error in de-compression? */
          sp++;                         /* Skip this byte */
         }
       }
     }
  }

 /* All OK */
 rc=FALSE;

rbe2: free(src);
rbe1: return(rc);
}

/* Load an IFF brush and create a DiskObject */
struct DiskObject *LoadIFFIcon(char *name)
{
 register struct IFFHandle *iff;
 register struct IFFDObj *id=NULL;
 struct ContextNode *cn;
 struct StoredProperty *sp;
 struct BitMapHeader *bmhd;
 ULONG bpr,planeoff;
 BOOL error=TRUE;

 /* Open IFF parsing library */
 if (!(IFFParseBase=OpenLibrary("iffparse.library",0))) goto lie1;

 /* Get memory for DiskObject and clear it */
 if (!(id=calloc(sizeof(struct IFFDObj),1))) goto lie2;
 id->id_dobj.do_Version=WB_DISKVERSION;
 id->id_dobj.do_Gadget.GadgetRender=&id->id_img;
 id->id_dobj.do_Gadget.UserData=(APTR) WB_DISKREVISION;

 /* Alloc IFF handle */
 if (!(iff=AllocIFF())) goto lie3;

 /* Open IFF file and init IFF handle */
 if (!(iff->iff_Stream=Open(name,MODE_OLDFILE))) goto lie4;
 InitIFFasDOS(iff);

 /* Declare ILBM property & stop chunks. Open IFF context */
 if (PropChunk(iff,ID_ILBM,ID_BMHD)) goto lie5;
 if (StopChunk(iff,ID_ILBM,ID_BODY)) goto lie5;
 if (StopOnExit(iff,ID_ILBM,ID_FORM)) goto lie5;
 if (OpenIFF(iff,IFFF_READ)) goto lie5;

 /* Start parsing */
 if (ParseIFF(iff,IFFPARSE_STEP)) goto lie6;

 /* Check for FORM ILBM chunk */
 if (!(cn=CurrentChunk(iff))) goto lie6;
 if ((cn->cn_ID!=ID_FORM) || (cn->cn_Type!=ID_ILBM)) goto lie6;

 /* Continue parsing until BODY chunk is found */
 if (ParseIFF(iff,IFFPARSE_SCAN)) goto lie6;

 /* BMHD chunk found? */
 if (!(sp=FindProp(iff,ID_ILBM,ID_BMHD))) goto lie6;
 bmhd=(struct BitMapHeader *) sp->sp_Data;

 /* Check compression type */
 if ((bmhd->Compression!=COMP_NO) && (bmhd->Compression!=COMP_BYTERUN1))
  goto lie6;

 /* Retrieve BMHD chunk values */
 id->id_dobj.do_Gadget.Width=bmhd->w;
 id->id_dobj.do_Gadget.Height=bmhd->h+1;
 id->id_img.Width=bmhd->w;
 id->id_img.Height=bmhd->h;
 id->id_img.Depth=bmhd->nplanes;
 id->id_img.PlanePick=(1<<bmhd->nplanes)-1;

 /* Allocate chip memory for image data */
 bpr=BPR(bmhd->w);                   /* Bytes per row */
 planeoff=bpr*bmhd->h;               /* Bytes per plane */
 id->id_size=planeoff*bmhd->nplanes; /* Bytes for image */
 if (!(id->id_img.ImageData=AllocMem(id->id_size,
                                     MEMF_PUBLIC|MEMF_CHIP|MEMF_CLEAR)))
  goto lie6;

 /* Read ImageData */
 error=ReadBODY(iff,bmhd,(UBYTE *) id->id_img.ImageData,bpr,planeoff);

      if (error) FreeMem(id->id_img.ImageData,id->id_size);
lie6: CloseIFF(iff);
lie5: Close(iff->iff_Stream);
lie4: FreeIFF(iff);
lie3: if (error)
       {
        free(id);
        id=NULL;
       }
lie2: CloseLibrary(IFFParseBase);
lie1: return(id);
}

/* Free an IFF icon */
void FreeIFFIcon(struct DiskObject *dobj)
{
 register struct IFFDObj *id=dobj;

 FreeMem(id->id_img.ImageData,id->id_size);
 free(id);
}
