/** routine for saveing an IFF picture to the clipboard...
    By Stephen Vermeulen 1989.
 **/

struct IOClipReq clipboardIO = 0;
struct MsgPort clipboardMsgPort = 0;
struct MsgPort satisfyMsgPort = 0;

#ifndef MakeID
#define MakeID(a, b, c, d)\
  ( ((long) (a)<<24) + ((long) (b)<<16) + ((long)(c)<<8) + (long)(d) )
#endif

short CBisOpen;      /** this will be set to 1 when the clipboard has
                         been successfully opened.
                      **/

/** the following two routines are from a CBM example somewhere on the
    Fish Disks...
 **/

int CBOpen(unit)
int unit;
{
    int error;

    /* open the clipboard device */
    if ((error = OpenDevice("clipboard.device", (long) unit, &clipboardIO, 0L)) != 0)
        CBisOpen = 0;

    /* Set up the message port in the I/O request */
    clipboardMsgPort.mp_Node.ln_Type = NT_MSGPORT;
    clipboardMsgPort.mp_Flags = 0;
    clipboardMsgPort.mp_SigBit = AllocSignal(-1L);
    clipboardMsgPort.mp_SigTask = (struct Task *) FindTask((char *) NULL);
    AddPort(&clipboardMsgPort);
    clipboardIO.io_Message.mn_ReplyPort = &clipboardMsgPort;

    satisfyMsgPort.mp_Node.ln_Type = NT_MSGPORT;
    satisfyMsgPort.mp_Flags = 0;
    satisfyMsgPort.mp_SigBit = AllocSignal(-1L);
    satisfyMsgPort.mp_SigTask = (struct Task *) FindTask((char *) NULL);
    AddPort(&satisfyMsgPort);
    CBisOpen = 1;
}

CBClose()
{
    if (CBisOpen)
    {
      if (clipboardMsgPort.mp_SigBit > 0)
         FreeSignal(clipboardMsgPort.mp_SigBit);
      if (satisfyMsgPort.mp_SigBit > 0)
         FreeSignal(satisfyMsgPort.mp_SigBit);

      RemPort(&satisfyMsgPort);
      RemPort(&clipboardMsgPort);
      CloseDevice(&clipboardIO);
      CBisOpen = 0;
    }
}

/** the rest of the clipboard routines are my own work **/

/** this is used to set the mode of the clipboard, to either read
    or write (0 or 1)
 **/

CBSetMode(mode)
int mode;
{
  if (mode) /** write **/
  {
    clipboardIO.io_Command = CMD_WRITE;
    clipboardIO.io_Offset = 0;
    clipboardIO.io_ClipID = 0;
  }
  else
  {
    clipboardIO.io_Command = CMD_READ;
    clipboardIO.io_Offset = 0;
    clipboardIO.io_ClipID = 0;
  }
}

/** the following is the function that replaces fread(), calling
    the clipboard instead.
 **/

gfread(data, length, n)
UBYTE *data;
int length, n;
{
    if (CBisOpen)
    {
      while (n)
      {
        clipboardIO.io_Command = CMD_READ;
        clipboardIO.io_Data = (STRPTR) data;
        clipboardIO.io_Length = length;
        DoIO(&clipboardIO);
        data += length;
        --n;
      }
    }
}

gputc(c)
char c;
{
    if (CBisOpen)
    {
      clipboardIO.io_Command = CMD_WRITE;
      clipboardIO.io_Data = (STRPTR) &c;
      clipboardIO.io_Length = 1;
      DoIO(&clipboardIO);
    }
}

gfwrite(data, length, n)
UBYTE *data;
int length, n;
{
    if (CBisOpen)
    {
      while (n)
      {
        clipboardIO.io_Command = CMD_WRITE;
        clipboardIO.io_Data = (STRPTR) data;
        clipboardIO.io_Length = length;
        DoIO(&clipboardIO);
        data += length;
        --n;
      }
    }
}

CBEndSession(mode)
int mode;
{
  if (!CBisOpen) return;
  if (mode)  /** end of writing **/
  {
    clipboardIO.io_Command = CMD_UPDATE;
    DoIO(&clipboardIO);
  }
  else   /** end of reading **/
  {
    clipboardIO.io_Command = CMD_READ;
    clipboardIO.io_Offset = 2000000000; /** don't use -1L here!
                                            the clipboard treats this
                                            as a SIGNED quantity! **/
    clipboardIO.io_Length = 1;
    clipboardIO.io_Data = (STRPTR) 0;
    DoIO(&clipboardIO);
  }
}

/** note the following does not support seeking relative to the end
    of the clip...
 **/

gfseek(offset, origin)
long offset;
int origin;
{
    if (CBisOpen)
    {
      switch (origin)
      {
        case 0: /** rel to begining **/
          clipboardIO.io_Offset = offset;
          break;
        case 1: /** rel to current pos **/
          clipboardIO.io_Offset += offset;
          break;
      }
    }
}

long gftell()
{
    if (CBisOpen)
    {
      return(clipboardIO.io_Offset);
    }
    else
      return(NULL);
}

gfopen(mode)
char *mode;
{
    if (mode[0] == 'r') CBSetMode(0);
    else CBSetMode(1);
}

gfclose(mode)
char *mode;
{
    if (mode[0] == 'r')
    {
      /** we must force a read beyond the end of the file to tell
          clipboard we are done with it!
       **/
      CBEndSession(0);
    }
    else CBEndSession(1);
}

#define MaxPackedSize(rowSize) ( (rowSize) + ( ((rowSize) + 127) >> 7) )
#define RowBytes(w)  ((((w) + 15) >> 4) << 1)

/************************************************************
  The following are the IFF read and write routines
************************************************************/

#define FORM MakeID('F', 'O', 'R', 'M')
#define ILBM MakeID('I', 'L', 'B', 'M')
#define BMHD MakeID('B', 'M', 'H', 'D')
#define BODY MakeID('B', 'O', 'D', 'Y')
#define VDFL MakeID('V', 'D', 'F', 'L')
#define CMAP MakeID('C', 'M', 'A', 'P')
#define GRAB MakeID('G', 'R', 'A', 'B')
#define CAMG MakeID('C', 'A', 'M', 'G')
#define CRNG MakeID('C', 'R', 'N', 'G')

struct BMHdr
{
  USHORT w, h;
  SHORT  x, y;
  UBYTE  nPlanes, masking, compression, pad1;
  USHORT transparentColor;
  UBYTE  xAspect, yAspect;
  SHORT  pageWidth, pageHeight;
};

struct PictHdr
{
  LONG IFFid, filelen, ILBMid, BMHDid, BMHDsize;
  struct BMHdr bmhdr;
};

char comp_buf[1036];  /* buffer to hold the compressed input data */
short comp_buf_len;   /* number of bytes in the compressed buffer */

/***************************************************************
  Quicky little routine that just writes a long word to the file
****************************************************************/

void write_WORD(i)
LONG i;
{
  (void) gfwrite( (char *) &i, 4, 1);
}

void write_CMAP(s, d)
struct Screen *s;
short d;
{
  short i, colour;

  if (d == 6) d == 5; /* cause of ehb */
  write_WORD(CMAP);
  write_WORD(3L * (1L << d));
  for (i = 0; i < (1 << d); ++i)
  {
    colour = GetRGB4(s->ViewPort.ColorMap, (long) i);
    gputc((colour & 0x0f00) >> 4);  /****** red ******/
    gputc((colour & 0x00f0));       /***** green *****/
    gputc((colour & 0x000f) << 4);  /****** blue *****/
  }
}

/************************************************************
  The following routine is used by the IFF saving routines
  to write the body section of the image.
  Returns true if sucessful, else it returns FALSE.
************************************************************/

int save_BODY(bm, comp)
struct BitMap *bm;
short comp;
{
  short y, nbytes, bwidth, depth;
  short p;
  struct BitMap mybm;
  char *source, *dest, *bp;

  nbytes = bwidth = bm->BytesPerRow;
  mybm = *bm;
  depth = bm->Depth;

  /*******************
    scan down the rows
  ********************/

  for (y = 0; y < bm->Rows; ++y)
  {
    /**************************
      Sweep down the bit planes
    ***************************/

    for (p = 0; p < depth; ++p)
    {
      bp = (char *) mybm.Planes[p];
      if (comp)
      {
        /****************
          pack this row
        *****************/

        source = bp;
        dest = &comp_buf[0];
        nbytes = PackRow(&source, &dest, bwidth);
        bp = &comp_buf[0];
      }

      /************************
        Now write out the data
      *************************/

      gfwrite((char *) bp, nbytes, 1);
      mybm.Planes[p] += bwidth;
    } /* end for bitplanes */
  }  /*  end for rows */
  return(1);
}

/************************************************************
  The routine to save designated area of a raster port in
  IFF format.
************************************************************/

short save_bm(bm, s)
struct BitMap *bm;
struct Screen *s;
{
  LONG vmode, body_start, body_len, file_len;
  struct PictHdr Pict;
  short i;
  FILE *out;
  char *name;

  CBOpen(0);
  if (!CBisOpen) return(0);
  gfopen("w");
  Pict.bmhdr.w = bm->BytesPerRow << 3;
  Pict.bmhdr.h = bm->Rows;
  Pict.bmhdr.x = 0;
  Pict.bmhdr.y = 0;
  Pict.bmhdr.nPlanes = bm->Depth;
  Pict.bmhdr.masking = 0;
  Pict.bmhdr.compression = 1;
  Pict.bmhdr.pad1 = 0;
  Pict.bmhdr.transparentColor = 0;
  Pict.bmhdr.xAspect = (s->Width > 400) ? 10 : 20;
  Pict.bmhdr.yAspect = (s->Height > 280) ? 11 : 22;
  Pict.bmhdr.pageWidth = s->Width;
  Pict.bmhdr.pageHeight = s->Height;
  Pict.IFFid = FORM;
  Pict.filelen = 0;  /* fill in later */
  Pict.ILBMid = ILBM;
  Pict.BMHDid = BMHD;
  Pict.BMHDsize = sizeof(struct BMHdr);
  gfwrite( (char *) &Pict, (short) sizeof(struct PictHdr), 1);

  write_CMAP(s, bm->Depth);
  vmode = s->ViewPort.Modes;
  vmode &= HIRES | LACE | EXTRA_HALFBRITE | HAM;
  write_WORD(CAMG, out);
  write_WORD(4L, out);
  write_WORD(vmode, out);

  /*********************
  Write the BODY chunk
  **********************/

  write_WORD(BODY);
  write_WORD(0L);     /* fill in later */
  body_start = gftell();
  save_BODY(bm, 1);
  body_len = gftell() - body_start; /* length of body chunk */
  if (gftell() & 0x01L) gputc(0);  /* pad byte if needed */
  file_len = gftell();

  /**************************
  Reposition in the file
  and write the actual body
  and file lengths.
  ***************************/

  gfseek(body_start - 4L, 0);  /* body length */
  write_WORD(body_len);
  gfseek(4L, 0);               /* file length */
  write_WORD(file_len - 8L);
  gfclose("w");
  CBClose();
  return(1);
}


