/**************
* TURBOMANDEL *
**************/

/* IFF read/write */

#include <stdio.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>

struct iff_chunk { long iff_type, iff_length; };
struct form_chunk { long fc_type, fc_length, fc_subtype; };

struct BitMapHeader {
   UWORD w, h, x, y;
   UBYTE nPlanes, masking, compression, pad1;
   UWORD transparentColor;
   UBYTE xAspect, yAspect;
   WORD pageWidth, pageHeight;
   };
struct ILBM_info {
   struct BitMapHeader header;
   struct BitMap bitmap;
   UBYTE cmap[96];
   };
extern struct MandelChunk {
   ULONG XCoo[2], YCoo[2], XSide[2], YSide[2];
   UWORD Iteration;
   UBYTE Calculation, Flags;
   SHORT TopPos[3], DecrStep[3], ColInt[3];
   } MandChunk;

read_iff (info, name, just_header)
struct ILBM_info *info;
char *name;
int just_header;
{
   UBYTE bool;
   FILE *file;
   struct form_chunk chunk;

   if ((file = fopen (name, "r") ) == 0) return (FALSE);
   if (fread (&chunk, sizeof (struct form_chunk), 1, file) != 1) {
      fclose (file); return (FALSE);
      }
   if (chunk.fc_type != 'FORM' || chunk.fc_subtype != 'ILBM') {
      fclose (file); return (FALSE);
      }
   bool = read_ilbm (file, info, chunk.fc_length - sizeof (chunk), just_header);
   fclose (file);
   return ((int)bool);
}

read_ilbm (file, info, length, just_header)
FILE *file;
struct ILBM_info *info;
long length;
int just_header;
{
   struct iff_chunk chunk;
   long read_in = 0;
   int got_header = FALSE, got_cmap = FALSE;

   while (read_in < length) {
      if (fread (&chunk, sizeof (chunk), 1, file) != 1) return (FALSE);
      switch (chunk.iff_type) {
         case 'BMHD':
            if (fread (&info->header, sizeof(info->header), 1, file) != 1)
               return (FALSE);
            got_header = TRUE;
            break;
         case 'MANC':      /* read mandel chunk */
            if (fread (&MandChunk, sizeof(MandChunk), 1, file) != 1)
               return (FALSE);
            break;
         case 'CMAP':
            if (!got_header) return (FALSE);
            if (chunk.iff_length <= 96) {
               if (fread (info->cmap, (int)chunk.iff_length, 1, file) != 1)
                  return (FALSE);
               }
            else {
               if (fread(info->cmap, 96, 1, file) != 1) return (FALSE);
               fseek (file, (long)chunk.iff_length - sizeof (3 * 32), 1);
               }
            got_cmap = TRUE;
            break;
         case 'BODY':
            if (!got_cmap || !got_header) return (FALSE);
            if (just_header) return (TRUE);
            return (read_body (file, info, chunk.iff_length));
         default:
            fseek (file, (long)chunk.iff_length, 1);
            break;
         }
      read_in += chunk.iff_length + sizeof(chunk);
      }
   return (FALSE); 
}

read_body (file, info, length)
FILE *file;
register struct ILBM_info *info;
long length;
{
   long i, j, plane_offset;

   if (info->header.compression != 0 && info->header.compression != 1)
      return (FALSE);

   plane_offset = 0;
   for (i = 0; i < info->bitmap.Rows; i++) {
      if (info->header.compression == 0) {
         for (j = 0; j < info->bitmap.Depth; j++)
            if (fread(info->bitmap.Planes[j] + plane_offset,
               info->bitmap.BytesPerRow, 1, file) != 1) return(FALSE);
         }
      else {
         register char *dest, value;
         register int so_far, count;

         for (j = 0; j < info->bitmap.Depth; j++) {
            so_far = info->bitmap.BytesPerRow;
            dest = (char *)info->bitmap.Planes[j] + plane_offset;
            while (so_far > 0) {
               value = getc (file);
               if (value > 0 && value != 128) {
                  count = (int)value + 1;
                  so_far -= count;
                  if (fread (dest, count, 1, file) != 1) return (FALSE);
                  dest += count;
                  }
               else {
                  count = (int)-value + 1;
                  so_far -= count;
                  value = getc (file);
                  while (--count >= 0) *dest++ = value;
                  }
               }
            if (so_far != 0) return (FALSE);
            }
         }
      plane_offset += info->bitmap.BytesPerRow;
      }
   return (TRUE);
}

#define DUMP   0
#define RUN    1

ULONG pack_row (source, dest, size)
char *source, *dest;
int size;
{
   char c, lastc = 0, *olddest;
   short mode = DUMP, nbuf = 0, rstart = 0, i;
   char buf[128*3/2];

   olddest = dest;
   buf[0] = lastc = *source++;
   nbuf = 1; size--;
   for (; size; --size) {
      buf[nbuf++] = c = *source++;
      if (mode == DUMP) {
         if (nbuf > 128) {
            *dest++ = nbuf - 2;
            for (i = 0; i < nbuf-1; i++) *dest++ = buf[i];
            buf[0] = c; nbuf = 1; rstart = 0;
            }
         else if (c == lastc) {
            if (nbuf - rstart >= 3) {
               if (rstart > 0) {
                  *dest++ = rstart - 1;
                  for (i = 0; i < rstart; i++) *dest++ = buf[i];
                  }
               mode = RUN;
               }
            else if (rstart == 0) mode = RUN;
            }
         else rstart = nbuf - 1;
         }
      else {
         if ((c != lastc) || (nbuf - rstart > 128)) {
            *dest++ = - (nbuf - rstart - 2); *dest++ = lastc;
            buf[0] = c; nbuf = 1; rstart = 0; mode = DUMP;
            }
         }
      lastc = c;
      }
   if (mode == DUMP) {
      *dest++ = nbuf - 1;
      for (i = 0; i < nbuf; i++) *dest++ = buf[i];
      }
   else {
      *dest++ = - nbuf + rstart + 1; *dest++ = lastc;
      }
   return ((ULONG)(dest - olddest));
}

ULONG pack_bitmap (file, bm)
FILE *file;
struct BitMap *bm;
{
   char packbuffer[256];
   int i, j, row_length;
   ULONG compressed_length = 0, plane_offset = 0;

   for (i = 0; i < bm->Rows; i++) {
      for (j = 0; j < bm->Depth; j++) {
         row_length = pack_row
            (bm->Planes[j] + plane_offset, packbuffer, bm->BytesPerRow);
         if (file) if (fwrite(packbuffer, row_length, 1, file) != 1) return (0);
         compressed_length += row_length;
         }
      plane_offset += bm->BytesPerRow;
      }
   if (compressed_length & 1) {
      if (file) if (putc (0, file) == EOF) return(0);
      compressed_length++;
      }
   return (compressed_length);
}

int write_iff (name, colors, scr, compressed)
char *name;
UWORD *colors;
struct Screen *scr;
int compressed;
{
   struct BitMap *bits;
   struct form_chunk chunk;
   struct iff_chunk ichunk;
   struct BitMapHeader header;
   UBYTE rgbcolors[96];
   SHORT i, numcols, j, row_offset;
   ULONG bits_size, viewmodes;
   FILE *file;

   if ((file = fopen (name, "w") ) == 0) return(0);
   if (scr) {
      bits = &scr->BitMap;
      if ((numcols = (1 << bits->Depth)) > 32) numcols = 32;
      }
   else numcols = 32;
   chunk.fc_type = 'FORM'; chunk.fc_subtype = 'ILBM';
   chunk.fc_length = 4 + 3 * sizeof (struct iff_chunk) + 12 + 62 +
      numcols * 3 + sizeof(struct BitMapHeader);
   if (scr) {
      if (compressed) {
         if ((bits_size = pack_bitmap (NULL, bits)) == 0) return(0);
         }
      else bits_size = bits->BytesPerRow * bits->Rows * bits->Depth;
      chunk.fc_length += bits_size;
      }
   if (fwrite (&chunk, sizeof(chunk), 1, file) != 1) return(0);

   ichunk.iff_type = 'BMHD';
   ichunk.iff_length = sizeof(header);
   if (fwrite (&ichunk, sizeof(ichunk), 1, file) != 1) return(0);

   header.masking = header.pad1 = header.transparentColor = 0;
   header.compression = compressed;
   header.pageWidth = bits->BytesPerRow * 8;
   header.pageHeight = bits->Rows;
   header.xAspect = header.yAspect = 10;

   /* write bitmapheader */
   if (scr) {
      header.w = bits->BytesPerRow * 8;
      header.h = bits->Rows;
      header.nPlanes = bits->Depth;
      header.x = header.y = 0;
      }
   if (fwrite (&header, sizeof(header), 1, file) != 1) return(0);

   /* write camg */
   ichunk.iff_type = 'CAMG'; ichunk.iff_length = 4;
   if (fwrite (&ichunk, sizeof(ichunk), 1, file) != 1) return(0);
   viewmodes = (ULONG)scr->ViewPort.Modes;
   if (fwrite (&viewmodes, 4, 1, file) != 1) return(0);

   /* write cmap */
   ichunk.iff_type = 'CMAP'; ichunk.iff_length = numcols * 3;
   if (fwrite (&ichunk, sizeof(ichunk), 1, file) != 1) return(0);
   for (i = 0; i < numcols; i++) {
      rgbcolors[3 * i] = (colors[i] & 0xf00) >> 4;
      rgbcolors[3 * i + 1] = (colors[i] & 0x0f0);
      rgbcolors[3 * i + 2] = (colors[i] & 0x00f) << 4;
      }
   if (fwrite (&rgbcolors[0], numcols * 3, 1, file) != 1) return(0);

   /* write mandel chunk */
   ichunk.iff_type = 'MANC'; ichunk.iff_length = sizeof(MandChunk);
   if (fwrite (&ichunk, sizeof(ichunk), 1, file) != 1) return(0);
   if (fwrite (&MandChunk, sizeof(MandChunk), 1, file) != 1) return (0);

   if (scr) {
      ichunk.iff_type = 'BODY'; ichunk.iff_length = bits_size;
      if (fwrite(&ichunk, sizeof(ichunk), 1, file) != 1) return(0);
      if (compressed) {
         if (pack_bitmap(file, bits) == 0) return(0);
         }
      else {
         i = bits->Rows;
         row_offset = 0;
         while (--i >= 0) {
            for (j = 0; j < bits->Depth; j++)
               if (fwrite( bits->Planes[j]+row_offset, bits->BytesPerRow,
                  1, file) != 1) return(0);
            row_offset += bits->BytesPerRow;
            }
         }
      }
   fclose (file);
   return (1);
}
