
/*
 * xanim_fli.c
 *
 * Copyright (C) 1990,1991,1992 by Mark Podlipec. 
 * All rights reserved.
 *
 * This software may be freely copied, modified and redistributed
 * without fee provided that this copyright notice is preserved 
 * intact on all copies and modified copies.
 * 
 * There is no warranty or other guarantee of fitness of this software.
 * It is provided solely "as is". The author(s) disclaim(s) all
 * responsibility and liability with respect to this software's usage
 * or its effect upon hardware or computer systems.
 *
 */
#include <stdio.h>
#include "mytypes.h"
#include "xanim.h"
#include "xanim_fli.h" 

void Read_Fli_Header();
void Read_Fli_Frame_Header();
void Read_Fli_File();
void Decode_Fli_BRUN();
void Decode_Fli_LC();
void Read_Fli_COLOR();
void Fli_Buffer_Action();

ULONG Fli_Get_Word();
ULONG Fli_Get_HalfWord();

static Fli_Header fli_hdr;
static Fli_Frame_Header frame_hdr;

void Read_Fli_Header(fp,fli_hdr)
FILE *fp;
Fli_Header *fli_hdr;
{
 int i;

 fli_hdr->size  = Fli_Get_Word(fp);
 fli_hdr->magic = Fli_Get_HalfWord(fp);
 fli_hdr->frames = Fli_Get_HalfWord(fp);
 fli_hdr->width = Fli_Get_HalfWord(fp);
 fli_hdr->height = Fli_Get_HalfWord(fp);
 fli_hdr->res1 = Fli_Get_HalfWord(fp);
 fli_hdr->flags = Fli_Get_HalfWord(fp);
 fli_hdr->speed = Fli_Get_HalfWord(fp);
 fli_hdr->next  = Fli_Get_Word(fp);
 fli_hdr->frit  = Fli_Get_Word(fp);
 for(i=0;i<102;i++) fgetc(fp);   /* ignore unused part of Fli_Header */

 imagex=fli_hdr->width;
 imagey=fli_hdr->height;
 if ( (fli_hdr->magic != 0xaf11) && (fli_hdr->magic != 0xaf12) )
 {
  fprintf(stderr,"imagex=%lx imagey=%lx\n",imagex,imagey);
  fprintf(stderr,"Fli Header Error magic %lx not = 0xaf11\n",fli_hdr->magic);
  TheEnd();
 }
}

void Read_Fli_Frame_Header(fp,frame_hdr)
FILE *fp;
Fli_Frame_Header *frame_hdr;
{
 int i;

 frame_hdr->size = Fli_Get_Word(fp);
 frame_hdr->magic = Fli_Get_HalfWord(fp);
 frame_hdr->chunks = Fli_Get_HalfWord(fp);
 for(i=0;i<8;i++) fgetc(fp);	/* ignore unused part of Fli_Frame_Header */

 if (frame_hdr->magic != 0xf1fa)
 {
  fprintf(stderr,"Frame_Header Error magic %lx not = 0xf1fa\n",frame_hdr->magic);
  TheEnd();
 }
}

void Read_Fli_File(fname)
char *fname;
{
 FILE *fin;
 int i,j,ret;
 ACTION *act;


 if ( (fin=fopen(fname,"r")) == 0)
 { 
  fprintf(stderr,"can't open Fli File %s for reading\n",fname); 
  TheEnd();
 }
 
 Read_Fli_Header(fin,&fli_hdr);

fprintf(stderr,"  x=%ld y=%ld Frames = %ld\n",fli_hdr.width,
            fli_hdr.height,fli_hdr.frames);

 for(i=0;i<fli_hdr.frames;i++)
 {
  Read_Fli_Frame_Header(fin,&frame_hdr);

  if (frame_hdr.chunks==0)  /* this frame is for timing purposes */
  {
   if (verbose) fprintf(stderr," ACT_DELAY Chunk\n");
   act = &action[action_cnt];
   action_cnt++;
   act->type = ACT_DELAY;
   if (jiffy_flag) act->time = jiffy_flag;
   else act->time = fli_hdr.speed * MS_PER_60HZ;
   act->data = 0;
  }
  else /* this frame has real data in it */
  {
   /* Loop through chunks in the frame
    */
   for(j=0;j<frame_hdr.chunks;j++)
   {
    ULONG chunk_size,chunk_type;
   
    act = &action[action_cnt];
    act->data = 0;
    action_cnt++;
 
    chunk_size = Fli_Get_Word(fin);
    chunk_type = Fli_Get_HalfWord(fin);
    switch(chunk_type)
    {
     case FLI_COLOR:
                 if (verbose) fprintf(stderr," FLI_COLOR Chunk\n");
		 Read_Fli_COLOR(fin,act);
		 break;
 
     case FLI_LC:
		 {
		  ACT_FLI_LC_HDR *fli_lc_hdr;

		  if (verbose) fprintf(stderr," FLI_LC\n");
		  act->type = ACT_FLI_LC;
 		  if (jiffy_flag) act->time = jiffy_flag;
 		  else act->time = fli_hdr.speed * MS_PER_60HZ;
 		  fli_lc_hdr = (ACT_FLI_LC_HDR *) 
		        malloc( chunk_size-6 + sizeof(ACT_FLI_LC_HDR));
 		  if (fli_lc_hdr == 0) TheEnd1("Read_Fli_LC: malloc failed");
		  act->data = (char *)(fli_lc_hdr);
 		  ret = fread( fli_lc_hdr->data, (chunk_size-6), 1, fin);
 		  if (ret != 1) TheEnd1("Read_Fli_LC: read failed");
		 }
		 break;
 
     case FLI_BLACK:
                 if (verbose) fprintf(stderr," FLI_BLACK\n");
		 if (chunk_size > 6) 
                       TheEnd1("Read_Fli_BLACK different than expected");
		 act->type = ACT_FLI_BLACK;
 		 if (jiffy_flag) act->time = jiffy_flag;
 		 else act->time = fli_hdr.speed * MS_PER_60HZ;
 		 act->data = 0;
		 break;
 
     case FLI_BRUN:
                 if (verbose) fprintf(stderr," FLI_BRUN\n");
		 act->type = ACT_FLI_BRUN;
 		 if (jiffy_flag) act->time = jiffy_flag;
 		 else act->time = fli_hdr.speed * MS_PER_60HZ;
 		 act->data = (char *) malloc( chunk_size-6 );
 		 if (act->data == 0) TheEnd1("Read_Fli_Brun: malloc failed");
 		 ret = fread( act->data, (chunk_size-6), 1, fin);
 		 if (ret != 1) TheEnd1("Read_Fli_Brun: read failed");
		 break;
 
     case FLI_COPY:
                 if (verbose) fprintf(stderr," FLI_COPY\n");
		 act->type = ACT_FLI_COPY;
 		 if (jiffy_flag) act->time = jiffy_flag;
 		 else act->time = fli_hdr.speed * MS_PER_60HZ;
 		 act->data = (char *) malloc( chunk_size-4 );
 		 if (act->data == 0) TheEnd1("Read_Fli_COPY: malloc failed");
 		 ret = fread( act->data, (chunk_size-4), 1, fin);
 		 if (ret != 1) TheEnd1("Read_Fli_COPY: read failed");
		 break;
      default: 
 fprintf(stderr,"default size=%lx type=%lx\n",chunk_size,chunk_type);
 		 TheEnd();
    } /* end of switch */
    if (chunk_size & 0x01)
    {
 fprintf(stderr,"chunk_size=%lx ODD\n",chunk_size);
    }
   } /* end of chunks is frame */
  } /* end of not Timing Frame */
 } /* end of frames in file */
          
 fclose(fin);
}


/*
 * Routine to Decode a Fli BRUN chunk
 */
void Decode_Fli_BRUN(data,image_out)
unsigned char *data,*image_out;
{
 ULONG i,j,k,packets,size,x,offset;

 for(i=0; i<imagey; i++)
 {
  offset = i * imagex;
  packets = *data++;

  x=0;
  for(j= 0; j < packets; j++)
  {
   size = *data++;
   if (size & 0x80)             /* size < 0 so there is -size unique bytes */
   {
    size = 256-size; 
    for(k= x; k < (x+size); k++) image_out[k+offset] = *data++;
    x += size;   
   }
   else                         /* size is pos repeat next byte size times */
   {
    ULONG d;
    d = *data++;
    for(k= x; k < (x+size); k++) image_out[k+offset] = d;
    x+=size;   
   }
  } /* end of packets per line */
 } /* end of line */
}

/*
 * Routine to Decode an Fli LC chunk
 */
void Decode_Fli_LC(data,image_out,r_hdr)
unsigned char *data,*image_out;
REGION_HDR *r_hdr;
{
 ULONG i,j,k,packets,size,x,offset;
 ULONG start,lines,skip,minx,maxx;

 start = *data++; start |= *data++ << 8;  /* lines to skip */
 lines = *data++; lines |= *data++ << 8;  /* number of lines */

 r_hdr->ypos  = start;
 r_hdr->ysize = lines;
 minx = imagex; 
 maxx = 0;
 
 for(i=start;i<(start+lines);i++)
 {
  offset = i * imagex;
  packets = *data++;

  x=0;
  for(j=0;j<packets;j++)
  {
   skip = *data++;   /* this is the skip count */
   size = *data++;

   if (j==0) if (skip < minx) minx = skip;
   x+=skip;
   if (size==0) 
   {
    size = *data++;
fprintf(stderr,"size==0 what to we do? next is %ld\n",size);
    TheEnd();
   }
   if (size&0x80) /* next byte repeated -size times */
   {
    ULONG d;
    size = 256-size; 
    d = *data++;
    for(k=x;k<(x+size);k++) image_out[k+offset] = d;
    x+=size;   
   }
   else /* size is pos */
   {
    for(k=x;k<(x+size);k++) image_out[k+offset] = *data++;
    x+=size;   
   }
  } /* end of packets per line */
  if (x > maxx) maxx = x;
 } /* end of line */
 r_hdr->xpos = minx;

 if (maxx > imagex) maxx=imagex;
 if (maxx > minx) r_hdr->xsize = maxx - minx;
 else r_hdr->xsize = imagex - minx;

 if (debug_flag) fprintf(stderr,"xypos=<%ld %ld> xysize=<%ld %ld>\n",
                         r_hdr->xpos,r_hdr->ypos,r_hdr->xsize,r_hdr->ysize);
}

/*
 * Routine to read an Fli COLOR chunk
 */
void Read_Fli_COLOR(fin,act)
FILE *fin;
ACTION *act;
{
 ULONG i,k,l,packets,skip,colors,cnt;
 ColorReg *act_cmap;
 CMAP_HDR *cmap_hdr;

 act->type = ACT_CMAP;
 act->time = 0;
 cmap_hdr = (CMAP_HDR *)
                malloc( CMAP_SIZE * sizeof(ColorReg) + sizeof(CMAP_HDR));
 if (cmap_hdr == 0) TheEnd1("Fli_Read_COLOR: malloc failed\n");
 act->data = (char *)cmap_hdr;
 act_cmap = (ColorReg *)cmap_hdr->data;
 cmap_hdr->cmap_size = imagec;

 for(i=0;i<CMAP_SIZE;i++) 
 {
  act_cmap[i].red   = cmap[i].red;
  act_cmap[i].green = cmap[i].green;
  act_cmap[i].blue  = cmap[i].blue;
 }

 packets = Fli_Get_HalfWord(fin);
 cnt=2;
 for(k=0;k<packets;k++)
 {
  skip = fgetc(fin);
  colors=fgetc(fin);
  cnt+=2;
  if (verbose) fprintf(stderr,"       skip=%ld colors=%ld\n",skip,colors);
  if (colors==0) colors=256;
  imagec=256;
  for(l=skip;l<(skip+colors);l++)
  {
   act_cmap[l].red   = cmap[l].red   = fgetc(fin)<<2;
   act_cmap[l].green = cmap[l].green = fgetc(fin)<<2;
   act_cmap[l].blue  = cmap[l].blue  = fgetc(fin)<<2;
  } /* end of colors */
  cnt+= 3 * colors;
 } /* end of packets */
 if (cnt&0x01) fgetc(fin);  /* read pad byte if needed */
}


/*
 * Routine to read buffer Fli Actions from action_start up to
 * action_cnt.
 */
void Fli_Buffer_Action(action_start)
int action_start;
{
 int i,pic_size;
 char *pic,*t_pic;
 ACTION *act;

 /* Allocate image buffer so deltas may be applied
  */
 pic_size = imagex * imagey;
 pic = (char *) malloc( pic_size );
 if (pic == 0) TheEnd1("BufferAction: malloc failed");

 for(i=action_start;i<action_cnt;i++)
 {
  act = &action[i];
  switch(act->type)
  {
   case ACT_DELAY:	
   case ACT_CMAP:	
   case ACT_IMAGE:	
   case ACT_FLI_COLOR:	
   case ACT_FLI_COPY:	
   case ACT_FLI_BLACK:	
			/* Clear to color 0 
			 */
			memset(pic,0x00,pic_size);

 			t_pic = (char *) malloc( pic_size );
			if (t_pic == 0) TheEnd1("BufferAction: malloc failed");
			memset(t_pic,0x00,pic_size);

			act->type = ACT_IMAGE;  /* change to image */
			free(act->data);        /* free up encoded */
			act->data = t_pic;      /* point to image */
			break;
   case ACT_FLI_BRUN:	

			Decode_Fli_BRUN(act->data,pic);

 			t_pic = (char *) malloc( pic_size );
			if (t_pic == 0) TheEnd1("BufferAction: malloc failed");

			memcpy(t_pic,pic,pic_size);
			act->type = ACT_IMAGE;  /* change to image */
			free(act->data);        /* free up encoded */
			act->data = t_pic;      /* point to image */

			break;
   case ACT_FLI_LC:	
			{
			 ACT_FLI_LC_HDR *fli_lc_hdr;
			 ACT_REGION_HDR *act_reg_hdr;

			 fli_lc_hdr = (ACT_FLI_LC_HDR *)(act->data);
			 Decode_Fli_LC(fli_lc_hdr->data, pic, fli_lc_hdr);

			 /* Since an FLI LC chunk typically changes less
			  * than the full image size, use a REGION to
			  * store the changes.
			  */
 			 act_reg_hdr = (ACT_REGION_HDR *) 
			    malloc(sizeof(ACT_REGION_HDR) + pic_size );
			 if (act_reg_hdr == 0) 
				TheEnd1("FLI BufferAction: malloc failed");
			 memcpy((void *)act_reg_hdr->data,
				(void *)pic, pic_size);
			 act->type = ACT_REGION;  /* change to region */
			 if (optimize_flag == TRUE)
			 {
			   act_reg_hdr->xpos  = fli_lc_hdr->xpos; 
			   act_reg_hdr->ypos  = fli_lc_hdr->ypos;
			   act_reg_hdr->xsize = fli_lc_hdr->xsize;
			   act_reg_hdr->ysize = fli_lc_hdr->ysize;
			 }
			 else
                         {
                           act_reg_hdr->xpos = 0;
                           act_reg_hdr->ypos = 0;
                           act_reg_hdr->xsize = imagex;
                           act_reg_hdr->ysize = imagey;
                         }

			 free(act->data);         /* free up encoded */
			 act->data = (char *)act_reg_hdr; /* point to region */
			}

			break;
   default:		
			fprintf(stderr,"Unknown not supported\n");
  }
 }
 free(pic);
}
 
 
/* Routine to read a little endian long word.
 * Note
 */
ULONG Fli_Get_Word(fp)
FILE *fp;
{
 ULONG ret;

 ret =  fgetc(fp);
 ret |= fgetc(fp) << 8;
 ret |= fgetc(fp) << 16;
 ret |= fgetc(fp) << 24;
 return ret;
}

/* Routine to read a little endian half word.
 */
ULONG Fli_Get_HalfWord(fp)
FILE *fp;
{
 ULONG ret;

 ret =  fgetc(fp);
 ret |= fgetc(fp) << 8;
 return ret;
}

