#include "iff.h"

extern void *IntuitionBase,*GfxBase,*DiskfontBase;

/* READILBM.C : NewZAP support routines - (c) 1986 John Hodgson */

#define DISK_ERR   1  /* error processing */
#define IFF_ERR    2
#define OUT_OF_MEM 3
#define EXPAND_ERR 4
#define BAD_MODE   5

#define MakeID(a,b,c,d) ((a)<<24L | (b)<<16L | (c)<<8 | (d))

#define ID_FORM MakeID('F','O','R','M')
#define ID_ILBM MakeID('I','L','B','M')
#define ID_BMHD MakeID('B','M','H','D')
#define ID_CAMG MakeID('C','A','M','G')
#define ID_CMAP MakeID('C','M','A','P')
#define ID_BODY MakeID('B','O','D','Y')

#define cmpByteRun1 1

#define ROUNDODDUP(a) (((a)+1)&(~1L))

typedef struct {
  long ckID,ckSize;
} Chunk;

typedef struct {
  short w,h,x,y;
  char  nPlanes,masking,compression,pad1;
  short transparentColor;
  char  xAspect, yAspect;
  short pageWidth,pageHeight;
} BitMapHeader;

short Mode;
unsigned char *pdiskptr;

#define SafeRead(a,b,c) if (ReadNEW(a,b,c)==-1L) \
  { CloseNEW(a); return(DISK_ERR); }

void *AllocMem(),*AllocRaster(); 

/************************************************************************
*                                                                       *
*    Routine name(s) : ReadILBM()                                       *
*    Author          : D. John Hodgson                                  *
*    Environment     : Aztec "C", default                               *
*                                                                       *
*    ReadILBM attempts to read an IFF file into an user-supplied        *
*    bitmap. Returns TRUE if successful. Brushes supported.             *
*                                                                       *
*    OPTIONS : Filespec reflects user-supplied pointer to IFF file in   *
*              RAM if mode argument is non-zero. Otherwise, disk.       *
*                                                                       *
*    LIMITATIONS : Bare-bones ILBM; no masking, CATS/LISTS/PROPS.       *
*                  All other graphics (compressed or not) supported.    *
************************************************************************/

void *OpenNEW(name,accessMode)
unsigned char *name;
long accessMode;
{
  struct FileHandle *Open();

  if (!Mode) return(Open(name,accessMode));

  pdiskptr=name; /* initialize pseudo-disk RAM ptr */ 
  return(pdiskptr);
}

long ReadNEW(file,buffer,length)
struct FileHandle *file;
char *buffer;
long length;
{
  long Read();

  if (!Mode) return(Read(file,buffer,length));

  movmem(pdiskptr,buffer,(unsigned int)length);
  pdiskptr+=length;

  return(length);
}

long SeekNEW(file,position,tmode)
struct FileHandle *file;
long position,tmode;
{
  long Seek();

  if (!Mode) return(Seek(file,position,tmode));

  /* Note : RAM seek only supports one mode : OFFSET_CURRENT! */
  if (tmode!=OFFSET_CURRENT) return(BAD_MODE);
	
  pdiskptr+=position;

  return((long)pdiskptr-position); /* Seek() returns previous position! */
} 

void CloseNEW(file)
struct FileHandle *file;
{
  if (!Mode) Close(file);
}

/* tag : core routines live here! */

ReadILBM(fspec,mode,picmap)
char *fspec; /* AmigaDOS filename */
short mode;
struct PicMap *picmap;
{
  struct FileHandle *fp;
  Chunk header;
  BitMapHeader bmhd;
  unsigned char colormap[MAXCOLORS][3],*bufstart,*sourcebuf;
  short i;
  long id;

  Mode=mode; 
  setmem(picmap,sizeof(*picmap),0L);

  if (!(fp=OpenNEW(fspec,MODE_OLDFILE))) return(DISK_ERR);

  SafeRead(fp,&header,(long)sizeof(header));
  if (header.ckID!=ID_FORM) { CloseNEW(fp); return(IFF_ERR); }

  SafeRead(fp,&id,(long)sizeof(id));
  if (id!=ID_ILBM) { CloseNEW(fp); return(IFF_ERR); }

  for (;;) {
    SafeRead(fp,&header,(long)sizeof(header));

    if (header.ckID==ID_BODY) break;

    switch(header.ckID) {
      case ID_BMHD: SafeRead(fp,&bmhd,(long)sizeof(bmhd));

                    /* force WORD boundary for all pics */
                    bmhd.w=(bmhd.w + 0xf) & 0xfff0;

                    break;

      case ID_CMAP: SafeRead(fp,&colormap[0][0],(long)header.ckSize);

                    for (i=0;i<header.ckSize/3;i++) 
                      picmap->colormap[i]=colormap[i][2]>>4 |
                                          colormap[i][1] & 0xf0 |
                                          (colormap[i][0] & 0xf0)<<4;
                    break;	

      case ID_CAMG: SafeRead(fp,&picmap->ViewModes,(long)header.ckSize);
                    break;

      default:      SeekNEW(fp,ROUNDODDUP(header.ckSize),OFFSET_CURRENT);
    }
  }

  /* make some forced assumptions if CAMG chunk unavailable */

  if (!picmap->ViewModes) {
    if (bmhd.w>LOWIDTH) picmap->ViewModes=HIRES;
    if (bmhd.h>LOHEIGHT) picmap->ViewModes|=LACE;
  }

  /* Read planes into RAM for ease if decompression */

  if (Mode) sourcebuf=bufstart=pdiskptr;  /* memory-resident IFF? */
  else { /* disk-resident IFF? */
    if (!(sourcebuf=bufstart=AllocMem((long)header.ckSize,MEMF_PUBLIC)))
       return(OUT_OF_MEM);
    SafeRead(fp,sourcebuf,(long)header.ckSize); CloseNEW(fp);  
  }

  InitBitMap(&picmap->BitMap,(long)bmhd.nPlanes,(long)bmhd.w,(long)bmhd.h);

  /* if we -REALLY- need a blank page, zero after allocating! */

  for (i=0;i<bmhd.nPlanes;i++)
    if (!(picmap->BitMap.Planes[i]=AllocRaster((long)bmhd.w,(long)bmhd.h))) {
      FreeBitMap(&picmap->BitMap);
      return(OUT_OF_MEM);
    }

  i=Expand(&bmhd,bufstart,&picmap->BitMap);

  if (!Mode) FreeMem(bufstart,(long)header.ckSize);

  return(i); /* error if unpacking prob */
}      

FreeBitMap(bitmap)
struct BitMap *bitmap;
{
  short i;

  /* take into acct partially allocated maps (due to error) */

  for (i=0;i<bitmap->Depth;i++) {
    if (bitmap->Planes[i]) FreeRaster(
       bitmap->Planes[i],(long)bitmap->BytesPerRow*8,(long)bitmap->Rows);
  }

  setmem(bitmap,sizeof(bitmap),0L); /* prevent dbl-dealloc errs */
}

Expand(bmhd,sourcebuf,bitmap) /* Fast line decompress/deinterleave */
BitMapHeader *bmhd;
register char *sourcebuf;
struct BitMap *bitmap;
{
  register char n,*destbuf; /* in order of preferred allocation */
  register short plane,rowlen,rowbytes,i;

  rowlen=(bmhd->w)>>3;

  for (i=0;i<bmhd->h;i++) /* process n lines/screen */
    for (plane=0;plane<bmhd->nPlanes;plane++) { /* process n planes/line */
      destbuf=(char *)(bitmap->Planes[plane])+rowlen*i;

      if (bmhd->compression==cmpByteRun1) { /* compressed screen? */
        rowbytes=rowlen;

        while (rowbytes>0) { /* unpack until 1 scan-line complete */
          n=*sourcebuf++; /* fetch block run marker */

          /* uncompressed block? copy n bytes verbatim */
          if (n>=0) {
            ++n;
            movmem(sourcebuf,destbuf,(unsigned int)n);
            rowbytes-=n; destbuf+=n; sourcebuf+=n;
          }
          else { /* compressed block? expand n duplicate bytes */
            n=-n+1;
            setmem(destbuf,(unsigned int)n,(unsigned int)*sourcebuf);
            rowbytes -=n; sourcebuf++; destbuf+=n;
          }

        } /* finish unpacking line */
        if (rowbytes) return(EXPAND_ERR); /* shouldn't be any left over! */
      }
      else { /* uncompressed? just copy */
        movmem(sourcebuf,destbuf,(unsigned int)rowlen);
        sourcebuf+=rowlen; destbuf+=rowlen;
      }
    } /* finish interleaved planes, lines */

  return(0); /* good result! */
}
