/* Routinen für IFF ILBM-Dateien
** File: spiff.c
*/


/* Unterroutine zum Test auf FORM ILBM, RGBN und RGB8 */


BYTE IsIFF(struct IFFHandle *iff)
{
 LONG              error = NULL;
 struct ContextNode *top = NULL;

 if(!(error = ParseIFF(iff,IFFPARSE_RAWSTEP)))
 {

  if(top=CurrentChunk(iff))
  {

   if((top->cn_Type) == ID_ILBM) return(1L);
   if((top->cn_Type) == MAKE_ID('R','G','B','N')) return(2L);
   if((top->cn_Type) == MAKE_ID('R','G','B','8')) return(3L);
  }
 }

 return(NULL);
}


/* Einlesen des BMHD Chunk in die Struktur */


BOOL GetBMHD(struct IFFHandle *iff,BitMapHeader *bmap)
{
 if(ReadChunkBytes(iff,bmap,20L) == 20L) return(TRUE);
 return(FALSE);
}


/* Einlesen des CAMG Chunk */


BOOL GetCAMG(struct IFFHandle *iff,ULONG *mode)
{
 if(ReadChunkBytes(iff,mode,4L) == 4L) return(TRUE);
 return(FALSE);
}


/* Einlesen des CMAP Chunk in ColorRegister */


BOOL GetCMAP(struct IFFHandle *iff,UBYTE *creg,LONG cnum)
{ 
 if(ReadChunkBytes(iff,creg,3*cnum) == 3*cnum) return(TRUE);
 return(FALSE);
}


/* Einlesen des BODY Chunk aus einer IFF ILBM Datei */


LONG GetBODY_ILBM(struct IFFHandle *iff,BitMapHeader *bmap,ULONG mode,struct Screen *scr)
{
 UWORD        width = scr->Width; /* screen width */
 UWORD      height = scr->Height; /* screen height */

 LONG               error = NULL;
 LONG              actual = NULL;
 LONG                     bcount;
 UWORD                    bpline; /* Anzahl der Bytes in einer Zeile */
 LONG           pixoffset = NULL;
 UWORD                    wpline; /* Anzahl der Words in einer Zeile */
 WORD             gfxmode = NULL;
 register WORD       min128=-128;
 register LONG srcbytes,dstbytes;
 register BYTE           *source;
 register BYTE             *dest;
 register BYTE          *scrbase;
 register UWORD     *sbuf = NULL; /* Buffer für eine Bildzeile */
 register UBYTE   *hambuf = NULL; /* Buffer für eine HAM Bildzeile */
 register WORD           c,h,p,n;

 gfxmode = 8;

 if(mode & HAM_KEY)
 {
  if(bmap->nPlanes == 6) gfxmode = 12;
  if(bmap->nPlanes == 8) gfxmode = 23;
 }

 if(USERFLGS & PFLG_TRUECLR)              
 {
  gfxmode = 24;
  if(USERFLGS & UFLG_TC15BIT) gfxmode = 15;
  if(USERFLGS & UFLG_TC16BIT) gfxmode = 16;
 }

 bpline = (ULONG)(bmap->w / 8);
 if(((LONG)bmap->w) % 8) bpline++;
 if(((LONG)bpline) % 2) bpline++;

 bcount = (bmap->nPlanes) * bpline;
 wpline = bpline / 2;

 scrbase = LockVillageScreen(scr);

 if(!(USERFLGS & UFLG_NOCENTER)) /* Zentrierung des Bildes auf dem Screen */
 {
  switch(gfxmode)
  {
   case 8:

   scrbase += (((height) - (bmap->h)) / 2 * (width)) + (((width) - (bmap->w)) / 2);
   break;
 
   case 12:
   case 15:
   case 16:

   scrbase += (((height) - (bmap->h)) / 2 * (width) * 2) + (((width) - (bmap->w)) / 2 * 2);
   break;

   case 23:
   case 24:

   scrbase += (((height) - (bmap->h)) / 2 * (width) * 3) + (((width) - (bmap->w)) / 2 * 3);
   break;
  }
 }

 if(sbuf = AllocVec((ULONG)(bpline*(UWORD)bmap->nPlanes),MEMF_PUBLIC|MEMF_CLEAR))
 {

  if(mode & HAM_KEY) 
  {
   if(!(hambuf = AllocVec((ULONG)bmap->w,MEMF_PUBLIC|MEMF_CLEAR)))
   {
    error = MSG_EALLOCMEM;
    goto ByteError;
   }
  }

  if(bmap->compression)
  {
   srcbytes = ReadChunkBytes(iff,cbuf,cbufsize); /* Lesen der Daten nach cbuf */
   source = cbuf;
  }

  for(h=0; h < (bmap->h); h++)
  {

   if(bmap->compression) 
   {
    dest = (BYTE *)sbuf;

    for(p=0; p < (bmap->nPlanes); p++)
    {
     dstbytes = bpline;

     while(dstbytes > 0)
     {

      if((srcbytes -= 1) < 0)
      {

       if((srcbytes = ReadChunkBytes(iff,cbuf,cbufsize) - 1) < 0)
       {
        error = MSG_MIFFILBM;
        goto ByteError;
       }
       source = cbuf;
      }

      n = *source++;
     
      if(n >= 0)
      {
       n += 1;

       if((srcbytes -= n) < 0) 
       {
        srcbytes += n;

        for(c=0; c < srcbytes; c++) cbuf[c] = *source++;
 
        if(ReadChunkBytes(iff,&cbuf[srcbytes],cbufsize - srcbytes) < 0) 
        {
         error = MSG_MIFFILBM;
         goto ByteError;
        }
        srcbytes = cbufsize-n;
        source = cbuf;
       }

       if((dstbytes -= n) < 0) break;
       do (*dest++ = *source++); while(--n > 0);
      }

      else if(n != min128)
      {
       n = -n + 1;

       if((srcbytes -= 1) < 0)
       {

        if((srcbytes = ReadChunkBytes(iff,cbuf,cbufsize) - 1) < 0)
        {
         error = MSG_MIFFILBM;
         goto ByteError;
        }
        source = cbuf;
       }

       if((dstbytes -= n) < 0) break;
       c = *source++;
       do *dest++ = c; while(--n > 0);
      }
     }
    }
   }
   else actual = ReadChunkBytes(iff,sbuf,bcount);

   switch(gfxmode)
   {
    case 8:

    Convert8(&scrbase[pixoffset],sbuf,bmap->nPlanes,bpline);
    pixoffset += width;
    break;

    case 12:

    ConvertHAM6(&scrbase[pixoffset],sbuf,hambuf,color,bpline);
    pixoffset += width*2;
    break;


    case 15:

    Convert15(&scrbase[pixoffset],sbuf,bpline);
    pixoffset += width*2;
    break;

    case 16:

    Convert16(&scrbase[pixoffset],sbuf,bpline);
    pixoffset += width*2;
    break;

    case 23:

    ConvertHAM8(&scrbase[pixoffset],sbuf,hambuf,color,bpline);
    pixoffset += width*3;
    break;
    
    case 24:

    Convert24(&scrbase[pixoffset],sbuf,bpline);
    pixoffset += width*3;
    break;

   }
  }

ByteError:

  if(hambuf) FreeVec(hambuf);
  FreeVec(sbuf);
 }

 UnLockVillageScreen(scr); 

 return(error);
}


/* Einlesen des BODY Chunk aus einer IFF RGBN bzw. RGB8 Datei */

LONG GetBODY_RGBx(struct IFFHandle *iff,BitMapHeader *bmap,struct Screen *scr)
{
 UWORD        width = scr->Width;
 UWORD      height = scr->Height;

 LONG               error = NULL;
 LONG             pixoffset=NULL;
 WORD               gfxmode=NULL;
 register LONG srcbytes,dstbytes;
 register UBYTE          *source;
 register UBYTE            *dest;
 register UBYTE         *scrbase;
 register WORD               c,h;

 UBYTE            red,green,blue;
 register UWORD    repeat = NULL;

 gfxmode = 12;

 if(USERFLGS & PFLG_TRUECLR)              
 {
  gfxmode = 24;
  if(USERFLGS & UFLG_TC15BIT) gfxmode = 15;
  if(USERFLGS & UFLG_TC16BIT) gfxmode = 16;
 }

 scrbase = LockVillageScreen(s);

 if(!(USERFLGS & UFLG_NOCENTER)) 
 {
  switch(gfxmode)
  {
   case 12:
   case 15:
   case 16:

   scrbase += (((height) - (bmap->h)) / 2 * (width) * 2) + (((width) - (bmap->w)) / 2 * 2);
   break;

   case 24:

   scrbase += (((height) - (bmap->h)) / 2 * (width) * 3) + (((width) - (bmap->w)) / 2 * 3);
   break;
  }
 }

 srcbytes = ReadChunkBytes(iff,cbuf,cbufsize); /* Lesen der Daten nach cbuf */
 source = cbuf;

 if(gfxmode != 12)
 {

  for(h=0; h < (bmap->h); h++)
  {
   dest = &scrbase[pixoffset];
 
   dstbytes = bmap->w;

   while(dstbytes > 0)
   {

    if(!repeat) /* 1) Wdh.zähler kann über eine Zeile hinaus reichen */
    {

     if((srcbytes -= 4) < 0) 
     {
      srcbytes += 4;

      for(c=0; c < srcbytes; c++) cbuf[c] = *source++;
 
      if(ReadChunkBytes(iff,&cbuf[srcbytes],cbufsize - srcbytes) < 0) 
      {
       error = MSG_MIFFILBM;
       goto RGBxError;
      }
      srcbytes = cbufsize-4;
      source = cbuf;
     }

     red    = *source++;
     green  = *source++;
     blue   = *source++;
     repeat = (UWORD)(*source++ & 0x007f);

     if(!repeat)
     {

      if((srcbytes -= 1) < 0)
      {

       if(ReadChunkBytes(iff,cbuf,cbufsize) < 0)
       {
        error = MSG_MIFFILBM;
        goto RGBxError;
       }
       srcbytes = cbufsize - 1;
       source = cbuf;
      }

      if(!(repeat = (UWORD)(*source++)))
      {

       if((srcbytes -= 2) < 0) 
       {
        srcbytes += 2;

        for(c=0; c < srcbytes; c++) cbuf[c] = *source++;

        if(ReadChunkBytes(iff,&cbuf[srcbytes],cbufsize - srcbytes) < 0) 
        {
         error = MSG_MIFFILBM;
         goto RGBxError;
        }
        srcbytes = cbufsize-2;
        source = cbuf;
       }

       repeat = (UWORD)((*source++)<<8);
       repeat += (UWORD)*source++;
      }
     } 
    }

    if((dstbytes -= repeat) < 0) repeat += dstbytes; /* siehe 1) */
    
    do
    {
     switch(gfxmode)
     {
      case 15:

      *dest++ = ((green<<2)&0xE0) | ((blue>>3)&0x1F);
      *dest++ = ((green>>6)&0x03) | ((red>>1)&0x7C);
      break;

      case 16:

      *dest++ = ((green<<3)&0xE0) | ((blue>>3)&0x1F);
      *dest++ = ((green>>5)&0x07) | (red&0xF8);
      break;

      case 24:

      *dest++ = blue;
      *dest++ = green;
      *dest++ = red; 
      break;
     }
    } while(--repeat > 0);

    if(dstbytes < 0) /* siehe 1) */
    {
     repeat = -dstbytes;
     break;
    }

   }
   switch(gfxmode)
   {
    case 15:
    case 16:
    pixoffset += width * 2;
    break;

    case 24:
    pixoffset += width * 3;
    break;
   }
  }
 }
 else
 {

  for(h=0; h < (bmap->h); h++)
  {
   dest = &scrbase[pixoffset];
 
   dstbytes = (LONG)bmap->w;

   while(dstbytes > 0)
   {

    if(!repeat) /* 1) Wdh.zähler kann über eine Zeile hinaus reichen */
    {

     if((srcbytes -= 2) < 0) 
     {
      srcbytes += 2;

      for(c=0; c < srcbytes; c++) cbuf[c] = *source++;
 
      if(ReadChunkBytes(iff,&cbuf[srcbytes],cbufsize - srcbytes) < 0) 
      {
       error = MSG_MIFFILBM;
       goto RGBxError;
      }
      srcbytes = cbufsize - 2;
      source = cbuf;
     }

     red    = *source & 0xf0;
     green  = (*source++<<4) & 0xf0;
     blue   = *source & 0xf0;
     repeat = (UWORD)(*source++ & 0x07);

     if(!repeat)
     {

      if((srcbytes -= 1) < 0)
      {

       if(ReadChunkBytes(iff,cbuf,cbufsize) < 0)
       {
        error = MSG_MIFFILBM;
        goto RGBxError;
       }
       srcbytes = cbufsize - 1;
       source = cbuf;
      }

      if(!(repeat = (UWORD)(*source++)))
      {

       if((srcbytes -= 2) < 0) 
       {
        srcbytes += 2;

        for(c=0; c < srcbytes; c++) cbuf[c] = *source++;

        if(ReadChunkBytes(iff,&cbuf[srcbytes],cbufsize - srcbytes) < 0) 
        {
         error = MSG_MIFFILBM;
         goto RGBxError;
        }
        srcbytes = cbufsize - 2;
        source = cbuf;
       }

       repeat = (UWORD)((*source++)<<8);
       repeat += (UWORD)*source++;
      }
     } 
    }

    if((dstbytes -= repeat) < 0) repeat += dstbytes; /* siehe 1) */
    
    do
    {
     *dest++ = ((green<<2)&0xE0) | ((blue>>3)&0x1F);
     *dest++ = ((green>>6)&0x03) | ((red>>1)&0x7C);
    } while(--repeat > 0);

    if(dstbytes < 0) /* siehe 1) */
    {
     repeat = -dstbytes;
     break;
    }

   }
   pixoffset += width * 2;
  }
 }

RGBxError:

 UnLockVillageScreen(s); 

 return(error);
}
