
/*
 * xa_movi.c
 *
 * Copyright (C) 1996,1997 by Mark Podlipec. 
 * All rights reserved.
 *
 * This software may be freely copied, modified and redistributed without
 * fee for non-commerical purposes 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.
 *
 */

/*******************************
 * Revision
 *
 *
 ********************************/


#include "xa_movi.h" 

xaULONG MOVI_Read_File();
MOVI_FRAME *MOVI_Add_Frame();
void MOVI_Free_Frame_List();
void ACT_Setup_Delta();

float MOVI_Read_Float();
xaULONG MOVI_Read_Int();
xaULONG MOVI_Read_I_Compression();
xaULONG MOVI_Read_A_Compression();
xaULONG MOVI_Read_Header();
xaULONG MOVI_Read_Header2();
xaULONG MOVI_Read_Header3();
xaULONG MOVI_Parse_MOVI_HDR();
xaULONG MOVI_Parse_MOVI_I_HDR();
xaULONG MOVI_Parse_MOVI_A_HDR();
xaULONG MOVI_Read_Index2();
xaULONG MOVI_Read_Index3();
void MOVI_Add_Video_Frames();
void MOVI_Add_Audio_Frames();
xaULONG MOVI_Get_Color();
extern void CMAP_Cache_Init();
extern void CMAP_Cache_Clear();
extern void JPG_Setup_Samp_Limit_Table();


/* CODEC ROUTINES */
xaULONG MOVI_Decode_MVC1();
extern xaULONG JFIF_Decode_JPEG();
extern xaULONG FLI_Decode_BLACK();
extern void XA_Gen_YUV_Tabs();
extern JPG_Alloc_MCU_Bufs();
extern jpg_search_marker();

XA_ACTION *ACT_Get_Action();
XA_CHDR *ACT_Get_CMAP();
XA_CHDR *CMAP_Create_332();
XA_CHDR *CMAP_Create_422();
XA_CHDR *CMAP_Create_Gray();
void ACT_Add_CHDR_To_Action();
void ACT_Setup_Mapped();
void ACT_Get_CCMAP();
XA_CHDR *CMAP_Create_CHDR_From_True();
xaULONG CMAP_Find_Closest();
xaUBYTE *UTIL_RGB_To_FS_Map();
xaUBYTE *UTIL_RGB_To_Map();

void  UTIL_FPS_2_Time();
extern XA_ANIM_SETUP *XA_Get_Anim_Setup();
void XA_Free_Anim_Setup();

extern xaLONG xa_dither_flag;
extern xaUBYTE  *xa_byte_limit;

static xaULONG movi_audio_attempt;
static xaULONG movi_audio_type;
static xaULONG movi_audio_freq;
static xaULONG movi_audio_chans;
static xaULONG movi_audio_bps;
xaULONG XA_Add_Sound();

#define MOVI_ICOMP_UNK	0
#define MOVI_ICOMP_MVC1	1
#define MOVI_ICOMP_MVC2	2
#define MOVI_ICOMP_JPEG	10

#define MOVI_ACOMP_UNK	0
#define MOVI_ACOMP_100	100
#define MOVI_ACOMP_401 	401
#define MOVI_ACOMP_402 	402

static xaULONG movi_frame_cnt;
static MOVI_FRAME *movi_frame_start,*movi_frame_cur;

MOVI_FRAME *MOVI_Add_Frame(time,timelo,act)
xaULONG time,timelo;
XA_ACTION *act;
{
  MOVI_FRAME *fframe;
 
  fframe = (MOVI_FRAME *) malloc(sizeof(MOVI_FRAME));
  if (fframe == 0) TheEnd1("MOVI_Add_Frame: malloc err");
 
  fframe->time   = time;
  fframe->timelo = timelo;
  fframe->act = act;
  fframe->next = 0;
 
  if (movi_frame_start == 0) movi_frame_start = fframe;
  else movi_frame_cur->next = fframe;
 
  movi_frame_cur = fframe;
  movi_frame_cnt++;
  return(fframe);
}

void MOVI_Free_Frame_List(fframes)
MOVI_FRAME *fframes;
{
  MOVI_FRAME *ftmp;
  while(fframes != 0)
  {
    ftmp = fframes;
    fframes = fframes->next;
    FREE(ftmp,0xA000);
  }
}


xaULONG MOVI_Read_File(fname,anim_hdr,audio_attempt)
char *fname;
XA_ANIM_HDR *anim_hdr;
xaULONG audio_attempt;	/* xaTRUE if audio is to be attempted */
{ XA_INPUT *xin = anim_hdr->xin;
  xaLONG i,t_time;
  xaULONG t_timelo;
  XA_ANIM_SETUP *movi;
  MOVI_HDR   movi_hdr;
  
  movi = XA_Get_Anim_Setup();
  movi->vid_time = XA_GET_TIME( 100 ); /* default */
  movi->compression	= MOVI_ICOMP_UNK;

  movi_frame_cnt	= 0;
  movi_frame_start	= 0;
  movi_frame_cur	= 0;
  movi_audio_attempt	= audio_attempt;

  movi_hdr.version	= 0;
  movi_hdr.a_hdr	= 0;
  movi_hdr.i_hdr	= 0;

  if (MOVI_Read_Header(xin,anim_hdr,movi,&movi_hdr) == xaFALSE)
  { xin->Close_File(xin);
    return(xaFALSE);
  }

  if (movi_hdr.version == 3)
  {  if (MOVI_Read_Index3(xin,anim_hdr,movi,&movi_hdr)==xaFALSE) 
							return(xaFALSE);
  }
  else if (movi_hdr.version == 2)
  {  if (MOVI_Read_Index2(xin,anim_hdr,movi,&movi_hdr)==xaFALSE) 
							return(xaFALSE);
  }
  else 
  { xin->Close_File(xin);
    return(xaFALSE);
  }

  movi->cmap_frame_num = movi_hdr.i_hdr->frames / cmap_sample_cnt;

  MOVI_Add_Video_Frames(xin,anim_hdr,movi,&movi_hdr);
  MOVI_Add_Audio_Frames(xin,anim_hdr,movi,&movi_hdr);

  xin->Close_File(xin);
  if (movi_hdr.a_hdr) 
  { if (movi_hdr.a_hdr->off)
    { free(movi_hdr.a_hdr->off);
      free(movi_hdr.a_hdr->size);
    }
    free(movi_hdr.a_hdr); movi_hdr.a_hdr = 0; 
  } 
  if (movi_hdr.i_hdr) 
  { if (movi_hdr.i_hdr->off)
    { free(movi_hdr.i_hdr->off);
      free(movi_hdr.i_hdr->size);
    }
    free(movi_hdr.i_hdr); movi_hdr.i_hdr = 0;
  } 

  if (movi_frame_cnt == 0)
  { 
    fprintf(stderr,"MOVI: No supported video frames exist in this file.\n");
    return(xaFALSE);
  }

  anim_hdr->frame_lst = (XA_FRAME *)
				malloc( sizeof(XA_FRAME) * (movi_frame_cnt+1));
  if (anim_hdr->frame_lst == NULL) TheEnd1("MOVI_Read_File: frame malloc err");

  movi_frame_cur = movi_frame_start;
  i = 0;
  t_time = 0;
  t_timelo = 0;
  while(movi_frame_cur != 0)
  {
    if (i > movi_frame_cnt)
    {
      fprintf(stderr,"MOVI_Read_Anim: frame inconsistency %d %d\n",
                i,movi_frame_cnt);
      break;
    }
    anim_hdr->frame_lst[i].time_dur = movi_frame_cur->time;
    anim_hdr->frame_lst[i].zztime = t_time;
    t_time += movi_frame_cur->time;
    t_timelo += movi_frame_cur->timelo;
    while(t_timelo > (1<<24)) {t_time++; t_timelo -= (1<<24);}
    anim_hdr->frame_lst[i].act = movi_frame_cur->act;
    movi_frame_cur = movi_frame_cur->next;
    i++;
  }
  anim_hdr->imagex = movi->imagex;
  anim_hdr->imagey = movi->imagey;
  anim_hdr->imagec = movi->imagec;
  anim_hdr->imaged = 8; /* nop */
  anim_hdr->frame_lst[i].time_dur = 0;
  anim_hdr->frame_lst[i].zztime = -1;
  anim_hdr->frame_lst[i].act  = 0;
  anim_hdr->loop_frame = 0;
  if (!(xin->load_flag & XA_IN_LOAD_BUF)) anim_hdr->anim_flags |= ANIM_SNG_BUF;
  if (xin->load_flag & XA_IN_LOAD_FILE) anim_hdr->anim_flags |= ANIM_USE_FILE;
  anim_hdr->anim_flags |= ANIM_FULL_IM;
  anim_hdr->max_fvid_size = movi->max_fvid_size;
  anim_hdr->max_faud_size = movi->max_faud_size;
  anim_hdr->fname = anim_hdr->name;
  if (i > 0) 
  {
    anim_hdr->last_frame = i - 1;
    anim_hdr->total_time = anim_hdr->frame_lst[i-1].zztime
				+ anim_hdr->frame_lst[i-1].time_dur;
  }
  else
  {
    anim_hdr->last_frame = 0;
    anim_hdr->total_time = 0;
  }
  MOVI_Free_Frame_List(movi_frame_start);
  XA_Free_Anim_Setup(movi);
/* POD TBD  Free movi_hdr */
  return(xaTRUE);
} /* end of read file */

/********************
 *
 *********************/
float MOVI_Read_Float(xin,size)
XA_INPUT *xin;
xaULONG size;
{ float fret = 0.0;
  float scale = 0.1;

 /** integer part */
 while(size--)
 { xaULONG dat = xin->Read_U8(xin);
   DEBUG_LEVEL1 fprintf(stderr,"<%x>",dat);
   if ( (dat >= 0x30) && (dat <= 0x39)) 
			fret = (10.0 * fret) + (float)(dat - 0x30);
   else if (dat <= 0) return(fret);
   else break;
 }
 /** fractional part */
 DEBUG_LEVEL1 fprintf(stderr,"<.>");
 while(size--)
 { xaULONG dat = xin->Read_U8(xin);
   DEBUG_LEVEL1 fprintf(stderr,"<%x>",dat);
   if ( (dat >= 0x30) && (dat <= 0x39)) 
   {
     fret += scale * (float)(dat - 0x30);
     scale /= 10.0;
   }
   else break;
 }
 DEBUG_LEVEL1 fprintf(stderr,"size %x\n",size);
 return(fret);
}


/********************
 *
 *********************/
xaULONG MOVI_Read_Int(xin,size)
XA_INPUT *xin;
xaULONG size;
{ xaULONG ret = 0;
 while(size--)
 { xaULONG dat = xin->Read_U8(xin);
   if ( (dat >= 0x30) && (dat <= 0x39)) ret = (10 * ret) + (dat - 0x30);
   else break;
 }
 return(ret);
}

/********************
 *
 *********************/
xaULONG MOVI_Read_I_Compression(xin,size)
XA_INPUT *xin;
xaULONG size;
{ xaULONG ret = 0;
  if (size >= 256) 
  { fprintf(stderr,"MOVI: string to long %d\n",size); 
    while(size--) (void)(xin->Read_U8(xin));
  }
  else
  { char tbuf[256]; char *p = tbuf;
    while(size--) *p++ = (char)(xin->Read_U8(xin) & 0xff);
    if (strcmp(tbuf,"1") == 0)		ret = MOVI_ICOMP_MVC1;
    else if (strcmp(tbuf,"10") == 0)	ret = MOVI_ICOMP_JPEG;
    else if (strcmp(tbuf,"MVC2") == 0)	ret = MOVI_ICOMP_MVC2;
    else
    { fprintf(stderr,"  MOVI: unknown video codec (%s)\n",tbuf);
      ret = MOVI_ICOMP_UNK;
    }
  }
  return(ret);
}

/********************
 *
 *********************/
xaULONG MOVI_Read_A_Compression(xin,size)
XA_INPUT *xin;
xaULONG size;
{ xaULONG ret = 0;
  if (size >= 256) 
  { fprintf(stderr,"MOVI: string to long %d\n",size); 
    while(size--) (void)(xin->Read_U8(xin));
  }
  else
  { char tbuf[256]; char *p = tbuf;
    while(size--) *p++ = (char)(xin->Read_U8(xin) & 0xff);
    if (strcmp(tbuf,"100") == 0)	ret = MOVI_ACOMP_100;
    else if (strcmp(tbuf,"401") == 0)	ret = MOVI_ACOMP_401;
    else if (strcmp(tbuf,"402") == 0)	ret = MOVI_ACOMP_402;
    else
    { fprintf(stderr,"  MOVI: unknown audio codec (%s)\n",tbuf);
      ret = MOVI_ACOMP_UNK;
    }
  }
  return(ret);
}

/********************
 *
 *********************/
xaULONG MOVI_Parse_MOVI_HDR(xin,movi_hdr,buf,size)
XA_INPUT *xin;
MOVI_HDR *movi_hdr;
char *buf;
xaULONG size;
{
  if (strcmp(buf,"__NUM_I_TRACKS") == 0)
				movi_hdr->i_tracks = MOVI_Read_Int(xin,size); 
  else if (strcmp(buf,"__NUM_A_TRACKS") == 0)
				movi_hdr->a_tracks = MOVI_Read_Int(xin,size); 
  else if (strcmp(buf,"LOOP_MODE") == 0)
				movi_hdr->loop_mode = MOVI_Read_Int(xin,size); 
  else if (strcmp(buf,"NUM_LOOPS") == 0)
				movi_hdr->num_loops = MOVI_Read_Int(xin,size); 
  else if (strcmp(buf,"OPTIMIZED") == 0)
				movi_hdr->optimized = MOVI_Read_Int(xin,size); 
  else /* unknown */
  {
    fprintf(stderr,"MOVI: unknown HDR var %s with size %d\n",buf,size);
    while(size--) (void)xin->Read_U8(xin);
  }
  return(xaTRUE);
}

/********************
 *
 *********************/
xaULONG MOVI_Parse_MOVI_A_HDR(xin,a_hdr,buf,size)
XA_INPUT *xin;
MOVI_A_HDR *a_hdr;
char *buf;
xaULONG size;
{
  if (strcmp(buf,"__DIR_COUNT") == 0)
				a_hdr->frames = MOVI_Read_Int(xin,size); 
  else if (strcmp(buf,"SAMPLE_WIDTH") == 0)
				a_hdr->width = MOVI_Read_Int(xin,size); 
  else if (strcmp(buf,"NUM_CHANNELS") == 0)
				a_hdr->chans = MOVI_Read_Int(xin,size); 
  else if (strcmp(buf,"AUDIO_FORMAT") == 0)
			a_hdr->format = MOVI_Read_A_Compression(xin,size); 
  else if (strcmp(buf,"SAMPLE_RATE") == 0)
				a_hdr->rate = MOVI_Read_Float(xin,size); 
  else if (strcmp(buf,"COMPRESSION") == 0)
			a_hdr->compression = MOVI_Read_A_Compression(xin,size); 
  else /* unknown */
  {
    fprintf(stderr,"MOVI: unknown A_HDR var %s with size %d\n",buf,size);
    while(size--) (void)xin->Read_U8(xin);
  }
  return(xaTRUE);
}

/********************
 *
 *********************/
xaULONG MOVI_Parse_MOVI_I_HDR(xin,i_hdr,buf,size)
XA_INPUT *xin;
MOVI_I_HDR *i_hdr;
char *buf;
xaULONG size;
{
  if (strcmp(buf,"__DIR_COUNT") == 0)
			i_hdr->frames = MOVI_Read_Int(xin,size); 
  else if (strcmp(buf,"WIDTH") == 0)
			i_hdr->width = MOVI_Read_Int(xin,size); 
  else if (strcmp(buf,"HEIGHT") == 0)
			i_hdr->height = MOVI_Read_Int(xin,size); 
  else if (strcmp(buf,"COMPRESSION") == 0)
			i_hdr->compression = MOVI_Read_I_Compression(xin,size); 
  else if (strcmp(buf,"INTERLACING") == 0)
			i_hdr->interlacing = MOVI_Read_Int(xin,size); 
  else if (strcmp(buf,"PACKING") == 0)
			i_hdr->packing = MOVI_Read_Int(xin,size); 
  else if (strcmp(buf,"ORIENTATION") == 0)
			i_hdr->orientation = MOVI_Read_Int(xin,size); 
  else if (strcmp(buf,"PIXEL_ASPECT") == 0)
			i_hdr->pixel_aspect = MOVI_Read_Float(xin,size); 
  else if (strcmp(buf,"FPS") == 0)
			i_hdr->fps = MOVI_Read_Float(xin,size); 
  else if (strcmp(buf,"Q_SPATIAL") == 0)
			i_hdr->q_spatial = MOVI_Read_Float(xin,size); 
  else if (strcmp(buf,"Q_TEMPORAL") == 0)
			i_hdr->q_temporal = MOVI_Read_Float(xin,size); 
  else /* unknown */
  {
    fprintf(stderr,"MOVI: unknown I HDR var %s with size %d\n",buf,size);
    while(size--) (void)xin->Read_U8(xin);
  }
  return(xaTRUE);
}


xaULONG MOVI_Read_Header(xin,anim_hdr,movi,movi_hdr)
XA_INPUT *xin;
XA_ANIM_HDR *anim_hdr;
XA_ANIM_SETUP *movi;
MOVI_HDR *movi_hdr;
{ xaULONG t,version;

  t 			= xin->Read_MSB_U32(xin); /* skip MOVI */

  /* Really have no clue about this, BUT some have
   *
   *        MOVI 0000_00003 0000_0000   => MOVI_Read_Header3
   *
   *        MOVI 0002_width hite_0000   => MOVI_Read_Header2
   */

  version 		= xin->Read_MSB_U16(xin); 
  if (version == 0)
  { version = xin->Read_MSB_U16(xin);
    if (version == 3)
    { movi_hdr->version = 3;
      t = xin->Read_MSB_U32(xin); /* unknown */
      return( MOVI_Read_Header3(xin,anim_hdr,movi,movi_hdr) );
    }
  }
  else if (version == 2)
  {  movi_hdr->version = 2;
     t = xin->Read_MSB_U16(xin); /* width */
     t = xin->Read_MSB_U16(xin); /* height */
     t = xin->Read_MSB_U16(xin); /* unknown */
     return( MOVI_Read_Header2(xin,anim_hdr,movi,movi_hdr) );
  }
  /* fall through cases */
  fprintf(stderr,"MOVI: Unknown format variation\n");
  return(xaFALSE);
}

xaULONG MOVI_Read_Header3(xin,anim_hdr,movi,movi_hdr)
XA_INPUT *xin;
XA_ANIM_HDR *anim_hdr;
XA_ANIM_SETUP *movi;
MOVI_HDR *movi_hdr;
{ xaULONG t,size;
  xaULONG vars;
  char buf[16];


  /*** MOVI Header Group ***/
  movi_hdr->i_tracks	= 0;
  movi_hdr->a_tracks	= 0;
  movi_hdr->loop_mode	= 0;
  movi_hdr->num_loops	= 0;
  movi_hdr->optimized	= 0;
  movi_hdr->a_hdr	= 0;
  movi_hdr->i_hdr	= 0;

  t 			= xin->Read_MSB_U32(xin); /* unk */
  vars 			= xin->Read_MSB_U32(xin); /* num of variables */
  t 			= xin->Read_MSB_U32(xin); /* unk */
  while(vars--)
  { int i; for(i=0;i<16;i++) buf[i] = xin->Read_U8(xin);
    size = xin->Read_MSB_U32(xin); /* size */
    DEBUG_LEVEL1 fprintf(stderr," MOVI: size %x buf %s\n",size,buf);
    if (MOVI_Parse_MOVI_HDR(xin,movi_hdr,buf,size)==xaFALSE) return(xaFALSE);
  }

  /*** MOVI Audio Header Group ***/

  if (movi_hdr->a_tracks > 1)
  { fprintf(stderr,"MOVI: multiple audio tracks not yet tolerated\n");
    return(xaFALSE);
  }
  if (movi_hdr->a_tracks == 1)  /* assume audio is optional */
  { MOVI_A_HDR *a_hdr = (MOVI_A_HDR *)malloc(sizeof(MOVI_A_HDR));
    if (a_hdr == 0) TheEnd1("MOVI: a_hdr malloc err");
/* POD TBD - free up MOVI_A_HDR and MOVI_I_HDR at end of read */
    movi_hdr->a_hdr = a_hdr;
    a_hdr->frames	= 0;
    a_hdr->width	= 0;
    a_hdr->chans	= 0;
    a_hdr->format	= 0;
    a_hdr->rate		= 0.0;
    a_hdr->compression	= 0;

    t 			= xin->Read_MSB_U32(xin); /* unk */
    vars 		= xin->Read_MSB_U32(xin); /* num of variables */
    t 			= xin->Read_MSB_U32(xin); /* unk */
    while(vars--)
    { int i; for(i=0;i<16;i++) buf[i] = xin->Read_U8(xin);
      size = xin->Read_MSB_U32(xin); /* size */
      DEBUG_LEVEL1 fprintf(stderr," MOVI: size %x buf %s\n",size,buf);
      if (MOVI_Parse_MOVI_A_HDR(xin,a_hdr,buf,size)==xaFALSE) return(xaFALSE);
    }
    if (xa_verbose)
    {
      fprintf(stdout,
		"  Audio: freq %5.1f chans %d  bps %d form %d comp %d\n",
		a_hdr->rate, a_hdr->chans, a_hdr->width, a_hdr->format, 
		a_hdr->compression);
    }
/* POD check for 0 frames?? */
    if (a_hdr->frames == 0) TheEnd1("MOVI: Audio err");
    a_hdr->off = (xaULONG *)malloc( a_hdr->frames * sizeof(xaULONG) );
    a_hdr->size = (xaULONG *)malloc( a_hdr->frames * sizeof(xaULONG) );

    movi_audio_freq =  (xaULONG)a_hdr->rate;
    movi_audio_chans = a_hdr->chans;
    movi_audio_bps   = a_hdr->width;
    movi_audio_type = XA_AUDIO_INVALID;

    if (   (a_hdr->compression == MOVI_ACOMP_100)
	&& (a_hdr->format == MOVI_ACOMP_401))
    {
      movi_audio_type = XA_AUDIO_SIGNED;
    }
    else if (   (a_hdr->compression == MOVI_ACOMP_100)
	     && (a_hdr->format == MOVI_ACOMP_402))
    {
      movi_audio_type = XA_AUDIO_LINEAR;
    }
  
    if ( (movi_audio_bps > 2) || (movi_audio_bps == 0) )
					movi_audio_type = XA_AUDIO_INVALID;
    if (movi_audio_chans == 2) movi_audio_type |= XA_AUDIO_STEREO_MSK;
    if (movi_audio_bps == 2)   
    {
      movi_audio_type |= XA_AUDIO_BPS_2_MSK;
      movi_audio_type |= XA_AUDIO_BIGEND_MSK;
    }
    if (movi_audio_type == XA_AUDIO_INVALID)
    {
      if (movi_audio_attempt == xaTRUE) 
	fprintf(stderr,"MOVI: Audio Type unsupported %d \n",
						a_hdr->compression);
      movi_audio_attempt = xaFALSE;
    }
  } /* end audio group */

  /*** MOVI Video Header Group ***/
  if (movi_hdr->i_tracks > 1)
  { fprintf(stderr,"MOVI: multiple video tracks not yet tolerated\n");
    return(xaFALSE);
  }
  if (movi_hdr->i_tracks == 1)
  { MOVI_I_HDR *i_hdr = (MOVI_I_HDR *)malloc(sizeof(MOVI_I_HDR));
    if (i_hdr == 0) TheEnd1("MOVI: i_hdr malloc err");
    movi_hdr->i_hdr 	= i_hdr;
    i_hdr->frames	= 0;
    i_hdr->width	= 0;
    i_hdr->height	= 0;
    i_hdr->compression	= 0;
    i_hdr->interlacing	= 0;
    i_hdr->packing	= 0;
    i_hdr->orientation	= 0;
    i_hdr->pixel_aspect	= 0.0;
    i_hdr->fps		= 0.0;
    i_hdr->q_spatial	= 0.0;
    i_hdr->q_temporal	= 0.0;

    t 			= xin->Read_MSB_U32(xin); /* unk */
    vars 		= xin->Read_MSB_U32(xin); /* num of variables */
    t 			= xin->Read_MSB_U32(xin); /* unk */
    while(vars--)
    { int i; for(i=0;i<16;i++) buf[i] = xin->Read_U8(xin);
      size = xin->Read_MSB_U32(xin); /* size */
      DEBUG_LEVEL1 fprintf(stderr," MOVI: size %x buf %s\n",size,buf);
      if (MOVI_Parse_MOVI_I_HDR(xin,i_hdr,buf,size)==xaFALSE) return(xaFALSE);
    }
    if (xa_verbose)
    {
      fprintf(stdout, "  Video: %dx%d  codec ", i_hdr->width, i_hdr->height);
      switch(i_hdr->compression)
      { case MOVI_ICOMP_MVC1: fprintf(stdout,"MVC1"); break;
	case MOVI_ICOMP_MVC2: fprintf(stdout,"MVC2"); break;
	case MOVI_ICOMP_JPEG: fprintf(stdout,"JPEG"); break;
	default: fprintf(stdout,"unk(%d)",i_hdr->compression);
      }
      fprintf(stdout," frames %d fps %3.2f\n",i_hdr->frames,i_hdr->fps);
    }

/* POD check for 0 frames?? */
    if (i_hdr->frames == 0) TheEnd1("MOVI: Video err");
    i_hdr->off = (xaULONG *)malloc( i_hdr->frames * sizeof(xaULONG) );
    i_hdr->size = (xaULONG *)malloc( i_hdr->frames * sizeof(xaULONG) );

    UTIL_FPS_2_Time(movi, ((double)(i_hdr->fps)) );

    switch(i_hdr->compression)
    {  
      case MOVI_ICOMP_JPEG:
        movi->compression = MOVI_ICOMP_JPEG;
        movi->depth = 24;
        movi->imagex = i_hdr->width;
        movi->imagey = i_hdr->height;
        movi->imagex = 4 * ((movi->imagex + 3)/4);
        movi->imagey = 4 * ((movi->imagey + 3)/4);
        XA_Gen_YUV_Tabs(anim_hdr);
        JPG_Alloc_MCU_Bufs(anim_hdr,movi->imagex,0,xaFALSE);
	JPG_Setup_Samp_Limit_Table(anim_hdr);
        break;

      case MOVI_ICOMP_MVC1:
        movi->compression = MOVI_ICOMP_MVC1;
        movi->depth = 16;
        movi->imagex = i_hdr->width;
        movi->imagey = i_hdr->height;
        movi->imagex = 4 * ((movi->imagex + 3)/4);
        movi->imagey = 4 * ((movi->imagey + 3)/4);
	JPG_Setup_Samp_Limit_Table(anim_hdr);
        break;

      case MOVI_ICOMP_MVC2:
        movi->compression = MOVI_ICOMP_UNK;
        movi->depth = 8;
        movi->imagex = i_hdr->width;
        movi->imagey = i_hdr->height;
        fprintf(stderr,"  MOVI: Video Codec MVC2 not supported\n"); 
        return(xaFALSE);
        break;

      default:
        movi->compression = MOVI_ICOMP_UNK;
        movi->depth = 8;
        movi->imagex = i_hdr->width;
        movi->imagey = i_hdr->height;
        fprintf(stderr," MOVI: Unknown Video Codec not supported\n"); 
        return(xaFALSE);
        break;
    }

	/* POD temp kludge for unknown movies */
/* Debug and testing only
*/
    if (movi->compression == MOVI_ICOMP_UNK)
    {
      movi->chdr = CMAP_Create_332(movi->cmap,&movi->imagec);
    }
    else 
    if ( (cmap_true_map_flag == xaFALSE) /* depth 16 and not true_map */
        || (!(xin->load_flag & XA_IN_LOAD_BUF)) )
    {
       if (cmap_true_to_332 == xaTRUE)
             movi->chdr = CMAP_Create_332(movi->cmap,&movi->imagec);
       else    movi->chdr = CMAP_Create_Gray(movi->cmap,&movi->imagec);
    }

    if ( (movi->pic==0) && (xin->load_flag & XA_IN_LOAD_BUF))
    {
      movi->pic_size = movi->imagex * movi->imagey;
      if ( (cmap_true_map_flag == xaTRUE) && (movi->depth > 8) )
                movi->pic = (xaUBYTE *) malloc( 3 * movi->pic_size );
      else movi->pic = (xaUBYTE *) malloc( XA_PIC_SIZE(movi->pic_size) );
      if (movi->pic == 0) TheEnd1("MOVI_Buffer_Action: malloc failed");
    }
  }

  return(xaTRUE);
}

xaULONG MOVI_Read_Header2(xin,anim_hdr,movi,movi_hdr)
XA_INPUT *xin;
XA_ANIM_HDR *anim_hdr;
XA_ANIM_SETUP *movi;
MOVI_HDR *movi_hdr;
{ xaULONG t;

  /*** MOVI Header Group ***/
  movi_hdr->i_tracks	= 0;
  movi_hdr->a_tracks	= 0;
  movi_hdr->loop_mode	= 0;
  movi_hdr->num_loops	= 0;
  movi_hdr->optimized	= 0;
  movi_hdr->a_hdr	= 0;
  movi_hdr->i_hdr	= 0;

  t 			= xin->Read_MSB_U32(xin); /* unk 0000_0000 */
  t 			= xin->Read_MSB_U32(xin); /* unk 402e_0000 */
  t 			= xin->Read_MSB_U32(xin); /* unk 0000_0000 */
  t 			= xin->Read_MSB_U32(xin); /* unk 0002_0001 */


  { MOVI_I_HDR *i_hdr = (MOVI_I_HDR *)malloc(sizeof(MOVI_I_HDR));
    if (i_hdr == 0) TheEnd1("MOVI: i_hdr malloc err");

    movi_hdr->i_hdr 	= i_hdr;
    i_hdr->frames	= 0;
    i_hdr->width	= 0;
    i_hdr->height	= 0;
    i_hdr->compression	= 0;
    i_hdr->interlacing	= 0;
    i_hdr->packing	= 0;
    i_hdr->orientation	= 0;
    i_hdr->pixel_aspect	= 0.0;
    i_hdr->fps		= 0.0;
    i_hdr->q_spatial	= 0.0;
    i_hdr->q_temporal	= 0.0;

    i_hdr->frames	= xin->Read_MSB_U32(xin);
    i_hdr->compression	= xin->Read_MSB_U32(xin); /* ??? */
    i_hdr->width	= xin->Read_MSB_U32(xin);
    i_hdr->height	= xin->Read_MSB_U32(xin);

    t 			= xin->Read_MSB_U32(xin); /* unk 0000_0004 */
    t 			= xin->Read_MSB_U32(xin); /* unk 0000_0002 */
    t 			= xin->Read_MSB_U32(xin); /* unk 0000_0001 */
    t 			= xin->Read_MSB_U32(xin); /* unk 0000_0000 */
    t 			= xin->Read_MSB_U32(xin); /* unk 0000_0000 */

    t 			= xin->Read_MSB_U32(xin); /* unk 0000_0000 */
    t 			= xin->Read_MSB_U32(xin); /* unk 0000_0000 */
    t 			= xin->Read_MSB_U32(xin); /* unk 0000_0000 */
    t 			= xin->Read_MSB_U32(xin); /* unk 0000_0000 */

    t = 0x80; while(t--) (void)xin->Read_U8(xin);	/* video title */
    t = 0x80; while(t--) (void)xin->Read_U8(xin);	/* audio title */
    t = 0x80; while(t--) (void)xin->Read_U8(xin);	/* unknown     */
    t = 0x80; while(t--) (void)xin->Read_U8(xin);	/* unknown     */

    i_hdr->fps = 15.0;   /* POD: TBD just a guess */
    if (xa_verbose)
    {
      fprintf(stdout,
		"  Video: %dx%d  comp %d  fps %3.2f\n",
		i_hdr->width, i_hdr->height, i_hdr->compression, i_hdr->fps);
    }

/*POD TBD free up i_hdr off and size arrays same for a_hdr */
       /* POD check for 0 frames?? */
    if (i_hdr->frames == 0) TheEnd1("MOVI: Video err");
    i_hdr->off = (xaULONG *)malloc( i_hdr->frames * sizeof(xaULONG) );
    i_hdr->size = (xaULONG *)malloc( i_hdr->frames * sizeof(xaULONG) );

    UTIL_FPS_2_Time(movi, ((double)(i_hdr->fps)) );

    switch(i_hdr->compression)
    {  
      case 1:
        movi->compression = MOVI_ICOMP_MVC1;
        movi->depth = 16;
        movi->imagex = i_hdr->width;
        movi->imagey = i_hdr->height;
        movi->imagex = 4 * ((movi->imagex + 3)/4);
        movi->imagey = 4 * ((movi->imagey + 3)/4);
        break;

      default:
        movi->compression = MOVI_ICOMP_UNK;
        movi->depth = 8;
        movi->imagex = i_hdr->width;
        movi->imagey = i_hdr->height;
        break;
    }

/* POD this part can be shared with Header3 somehow */
	/* POD temp kludge for unknown movies */
    if (movi->compression == MOVI_ICOMP_UNK)
    {
      movi->chdr = CMAP_Create_332(movi->cmap,&movi->imagec);
    }
    else if ( (cmap_true_map_flag == xaFALSE) /* depth 16 and not true_map */
        || (!(xin->load_flag & XA_IN_LOAD_BUF)) )
    {
       if (cmap_true_to_332 == xaTRUE)
             movi->chdr = CMAP_Create_332(movi->cmap,&movi->imagec);
       else    movi->chdr = CMAP_Create_Gray(movi->cmap,&movi->imagec);
    }

    if ( (movi->pic==0) && (xin->load_flag & XA_IN_LOAD_BUF))
    {
      movi->pic_size = movi->imagex * movi->imagey;
      if ( (cmap_true_map_flag == xaTRUE) && (movi->depth > 8) )
                movi->pic = (xaUBYTE *) malloc( 3 * movi->pic_size );
      else movi->pic = (xaUBYTE *) malloc( XA_PIC_SIZE(movi->pic_size) );
      if (movi->pic == 0) TheEnd1("MOVI_Buffer_Action: malloc failed");
    }
  }

  return(xaTRUE);
}


xaULONG MOVI_Read_Index3(xin,anim_hdr,movi,movi_hdr)
XA_INPUT *xin;
XA_ANIM_HDR *anim_hdr;
XA_ANIM_SETUP *movi;
MOVI_HDR *movi_hdr;
{ xaLONG i,t; 

  if (movi_hdr->a_hdr)  /* do audio 1st */
  { MOVI_A_HDR *a_hdr = movi_hdr->a_hdr;
    for(i=0; i< a_hdr->frames; i++)
    {
       a_hdr->off[i]	= xin->Read_MSB_U32(xin);
       a_hdr->size[i]	= xin->Read_MSB_U32(xin);
       t		= xin->Read_MSB_U32(xin);  /* unk */
       t		= xin->Read_MSB_U32(xin);  /* unk */
DEBUG_LEVEL1 fprintf(stderr,"A: %x %x\n",a_hdr->off[i], a_hdr->size[i]);
    }
  }
  if (movi_hdr->i_hdr) 
  { MOVI_I_HDR *i_hdr = movi_hdr->i_hdr;
    for(i=0; i< i_hdr->frames; i++)
    {
       i_hdr->off[i]	= xin->Read_MSB_U32(xin);
       i_hdr->size[i]	= xin->Read_MSB_U32(xin);
       t		= xin->Read_MSB_U32(xin);  /* unk */
       t		= xin->Read_MSB_U32(xin);  /* unk */
DEBUG_LEVEL1 fprintf(stderr,"I: %x %x\n",i_hdr->off[i], i_hdr->size[i]);
    }

  }
  return(xaTRUE);
}

xaULONG MOVI_Read_Index2(xin,anim_hdr,movi,movi_hdr)
XA_INPUT *xin;
XA_ANIM_HDR *anim_hdr;
XA_ANIM_SETUP *movi;
MOVI_HDR *movi_hdr;
{ xaLONG i,t; 

  if (movi_hdr->a_hdr)  /* do audio 1st */
  { MOVI_A_HDR *a_hdr = movi_hdr->a_hdr;
    for(i=0; i< a_hdr->frames; i++)
    { a_hdr->off[i]	= xin->Read_MSB_U32(xin);
      t		= xin->Read_MSB_U32(xin);  /* unk */
      a_hdr->size[i]	= xin->Read_MSB_U32(xin);
      t		= xin->Read_MSB_U32(xin);  /* unk */
      t		= xin->Read_MSB_U32(xin);  /* unk */
      DEBUG_LEVEL1 fprintf(stderr,"A: %x %x\n",
				a_hdr->off[i], a_hdr->size[i]);
    }
  }
  if (movi_hdr->i_hdr) 
  { MOVI_I_HDR *i_hdr = movi_hdr->i_hdr;
    for(i=0; i< i_hdr->frames; i++)
    {  i_hdr->off[i]	= xin->Read_MSB_U32(xin);
       t		= xin->Read_MSB_U32(xin);  /* unk */
       i_hdr->size[i]	= xin->Read_MSB_U32(xin);
       t		= xin->Read_MSB_U32(xin);  /* unk */
       t		= xin->Read_MSB_U32(xin);  /* unk */
       DEBUG_LEVEL1 fprintf(stderr,"I: %x %x\n",
				i_hdr->off[i], i_hdr->size[i]);
    }
  }
  return(xaTRUE);
}


void MOVI_Add_Video_Frames(xin,anim_hdr,movi,movi_hdr)
XA_INPUT *xin;
XA_ANIM_HDR *anim_hdr;
XA_ANIM_SETUP *movi;
MOVI_HDR *movi_hdr;
{ MOVI_I_HDR *i_hdr = movi_hdr->i_hdr;
  int i;

  if (i_hdr == 0) return;
  for(i = 0; i < i_hdr->frames; i++)
  { xaULONG dlta_len;
    XA_ACTION *act;
    ACT_DLTA_HDR *dlta_hdr;
  
    dlta_len = i_hdr->size[i];

    act = ACT_Get_Action(anim_hdr,ACT_DELTA);
    if (xin->load_flag & XA_IN_LOAD_FILE)
    { dlta_hdr = (ACT_DLTA_HDR *) malloc(sizeof(ACT_DLTA_HDR));
      if (dlta_hdr == 0) TheEnd1("MOVI: dlta malloc err");
      act->data = (xaUBYTE *)dlta_hdr;
      dlta_hdr->flags = ACT_SNGL_BUF;
      dlta_hdr->fsize = dlta_len;
      dlta_hdr->fpos  = i_hdr->off[i];
      if (dlta_len > movi->max_fvid_size) movi->max_fvid_size = dlta_len;
    }
    else
    { xaULONG d; xaLONG ret;
      d = dlta_len + (sizeof(ACT_DLTA_HDR));
      dlta_hdr = (ACT_DLTA_HDR *) malloc( d );
      if (dlta_hdr == 0) TheEnd1("MOVI: malloc failed");
      act->data = (xaUBYTE *)dlta_hdr;
      dlta_hdr->flags = ACT_SNGL_BUF | DLTA_DATA;
      dlta_hdr->fpos = 0; dlta_hdr->fsize = dlta_len;
      ret = xin->Seek_FPos(xin, i_hdr->off[i], 0);
      ret = xin->Read_Block(xin,dlta_hdr->data,dlta_len);
      if (ret < dlta_len) { fprintf(stderr,"MOVI: read err\n"); return; }
    }
    MOVI_Add_Frame(movi->vid_time,movi->vid_timelo,act);
    dlta_hdr->xpos = dlta_hdr->ypos = 0;
    dlta_hdr->xsize = movi->imagex;
    dlta_hdr->ysize = movi->imagey;

    switch(movi->compression)
    { case MOVI_ICOMP_JPEG:
	dlta_hdr->special = 0;
	dlta_hdr->extra = (void *)(0x0);
	dlta_hdr->xapi_rev = 0x0001;
	dlta_hdr->delta = JFIF_Decode_JPEG;
	break;
      case MOVI_ICOMP_MVC1:
	dlta_hdr->special = 0;
	dlta_hdr->extra = (void *)(0x0);
	dlta_hdr->xapi_rev = 0x0001;
	dlta_hdr->delta = MOVI_Decode_MVC1;
	break;
      default:
	dlta_hdr->special = 0;
	dlta_hdr->extra = (void *)(0x0);
	dlta_hdr->xapi_rev = 0x0001;
	dlta_hdr->delta = FLI_Decode_BLACK;
	break;
    }
    ACT_Setup_Delta(movi,act,dlta_hdr,xin);
  } /* end of i thru frames */
}

/***********
 *
 ***********/
void MOVI_Add_Audio_Frames(xin,anim_hdr,movi,movi_hdr)
XA_INPUT *xin;
XA_ANIM_HDR *anim_hdr;
XA_ANIM_SETUP *movi;
MOVI_HDR *movi_hdr;
{ MOVI_A_HDR *a_hdr = movi_hdr->a_hdr;
  int i;

  if (a_hdr == 0) return;
  for(i = 0; i < a_hdr->frames; i++)
  { xaULONG snd_size = a_hdr->size[i];

    if (movi_audio_attempt==xaTRUE)
    { xaLONG ret;
      if (xin->load_flag & XA_IN_LOAD_FILE)
      { xaLONG rets;
	rets = XA_Add_Sound(anim_hdr,0,movi_audio_type, a_hdr->off[i],
                        movi_audio_freq, snd_size,
                        &movi->aud_time,&movi->aud_timelo, 0, 0);
	if (rets==xaFALSE)			movi_audio_attempt = xaFALSE;
	if (snd_size > movi->max_faud_size)	movi->max_faud_size = snd_size;

      }
      else
      { xaUBYTE *snd = (xaUBYTE *)malloc(snd_size);
	if (snd==0) TheEnd1("MOVI: snd malloc err");
	ret = xin->Seek_FPos( xin, a_hdr->off[i], 0);
	ret = xin->Read_Block(xin, snd, snd_size);
	if (ret < snd_size) fprintf(stderr,"MOVI: snd rd err\n");
	else
	{ int rets;
	  rets = XA_Add_Sound(anim_hdr,snd,movi_audio_type, -1,
                                        movi_audio_freq, snd_size,
                                        &movi->aud_time, &movi->aud_timelo, 0, 0);
	  if (rets==xaFALSE) movi_audio_attempt = xaFALSE;
	}
      }
    }
  } /* end of i thru chunks */
}


#define MOVI_BLOCK_INC(x,y,imagex) { x += 4; if (x>=imagex) { x=0; y += 4; } }
#define MOVI_GET_16(data,dptr) {data = *dptr++ << 8; data |= *dptr++; }

#define MVC1_COLOR2(dp,p0,c0,c1,c2,c3,c4,c5,c6,c7,mflag,map,chdr,CAST) \
{ c0 = c2 = c4 = c6 = (CAST)MOVI_Get_Color(p0,mflag,map,chdr);	\
  p0 = *dp++ << 8; p0 |= *dp++;						\
  c1 = c3 = c5 = c7 = (CAST)MOVI_Get_Color(p0,mflag,map,chdr);	}

#define MVC1_COLOR8(dp,p0,c0,c1,c2,c3,c4,c5,c6,c7,mflag,map,chdr,CAST) \
{ c0 = (CAST)MOVI_Get_Color(p0,mflag,map,chdr);		\
  p0 = *dp++ << 8; p0 |= *dp++; c1 = (CAST)MOVI_Get_Color(p0,mflag,map,chdr); \
  p0 = *dp++ << 8; p0 |= *dp++; c2 = (CAST)MOVI_Get_Color(p0,mflag,map,chdr); \
  p0 = *dp++ << 8; p0 |= *dp++; c3 = (CAST)MOVI_Get_Color(p0,mflag,map,chdr); \
  p0 = *dp++ << 8; p0 |= *dp++; c4 = (CAST)MOVI_Get_Color(p0,mflag,map,chdr); \
  p0 = *dp++ << 8; p0 |= *dp++; c5 = (CAST)MOVI_Get_Color(p0,mflag,map,chdr); \
  p0 = *dp++ << 8; p0 |= *dp++; c6 = (CAST)MOVI_Get_Color(p0,mflag,map,chdr); \
  p0 = *dp++ << 8; p0 |= *dp++; c7 = (CAST)MOVI_Get_Color(p0,mflag,map,chdr); }


#define MVC1_BLK8(ip,mask,c0,c1,c2,c3,c4,c5,c6,c7) { \
  *ip++ = (mask & 0x0001)?(c0):(c1);			\
  *ip++ = (mask & 0x0002)?(c0):(c1);			\
  *ip++ = (mask & 0x0004)?(c2):(c3);			\
  *ip   = (mask & 0x0008)?(c2):(c3); ip += row_inc;	\
  *ip++ = (mask & 0x0010)?(c0):(c1);			\
  *ip++ = (mask & 0x0020)?(c0):(c1);			\
  *ip++ = (mask & 0x0040)?(c2):(c3);			\
  *ip   = (mask & 0x0080)?(c2):(c3); ip += row_inc;	\
  *ip++ = (mask & 0x0100)?(c4):(c5);			\
  *ip++ = (mask & 0x0200)?(c4):(c5);			\
  *ip++ = (mask & 0x0400)?(c6):(c7);			\
  *ip   = (mask & 0x0800)?(c6):(c7); ip += row_inc;	\
  *ip++ = (mask & 0x1000)?(c4):(c5);			\
  *ip++ = (mask & 0x2000)?(c4):(c5);			\
  *ip++ = (mask & 0x4000)?(c6):(c7);			\
  *ip   = (mask & 0x8000)?(c6):(c7);			}


#define MVC1_PIX_2_RGB(pix,r,g,b) { xaULONG ra,ga,ba;		\
  ra = (pix>>10) & 0x1f; ga = (pix>>5) & 0x1f; ba = pix & 0x1f;	\
  r = (ra << 3) | (ra >> 2); g = (ga << 3) | (ga >> 2);		\
  b = (ba << 3) | (ba >> 2);	}


#define MVC1_RGB2(dp,p0,r,g,b) \
{ MVC1_PIX_2_RGB(p0,r[0],g[0],b[0]);	r[2] = r[4] = r[6] = r[0];	\
  g[2] = g[4] = g[6] = g[0];	b[2] = b[4] = b[6] = b[0];		\
  p0 = *dp++ << 8; p0 |= *dp++;						\
  MVC1_PIX_2_RGB(p0,r[1],g[1],b[1]);	r[3] = r[5] = r[7] = r[1];	\
  g[3] = g[5] = g[7] = g[1];	b[3] = b[5] = b[7] = b[1];	}


#define MVC1_RGB8(dp,p0,r,g,b)	\
{ MVC1_PIX_2_RGB(p0,r[0],g[0],b[0]);	\
  p0 = *dp++ << 8; p0 |= *dp++;	MVC1_PIX_2_RGB(p0,r[1],g[1],b[1]);	\
  p0 = *dp++ << 8; p0 |= *dp++;	MVC1_PIX_2_RGB(p0,r[2],g[2],b[2]);	\
  p0 = *dp++ << 8; p0 |= *dp++;	MVC1_PIX_2_RGB(p0,r[3],g[3],b[3]);	\
  p0 = *dp++ << 8; p0 |= *dp++;	MVC1_PIX_2_RGB(p0,r[4],g[4],b[4]);	\
  p0 = *dp++ << 8; p0 |= *dp++;	MVC1_PIX_2_RGB(p0,r[5],g[5],b[5]);	\
  p0 = *dp++ << 8; p0 |= *dp++;	MVC1_PIX_2_RGB(p0,r[6],g[6],b[6]);	\
  p0 = *dp++ << 8; p0 |= *dp++;	MVC1_PIX_2_RGB(p0,r[7],g[7],b[7]);	}

#define MVC1_RGB_BLK8(ip,m,r,g,b) { register xaULONG ix;    \
  ix = (m & 0x0001)?(0):(1); *ip++ = r[ix]; *ip++ = g[ix]; *ip++ = b[ix]; \
  ix = (m & 0x0002)?(0):(1); *ip++ = r[ix]; *ip++ = g[ix]; *ip++ = b[ix]; \
  ix = (m & 0x0004)?(2):(3); *ip++ = r[ix]; *ip++ = g[ix]; *ip++ = b[ix]; \
  ix = (m & 0x0008)?(2):(3); *ip++ = r[ix]; *ip++ = g[ix]; *ip   = b[ix]; \
  ip += row_inc;							  \
  ix = (m & 0x0010)?(0):(1); *ip++ = r[ix]; *ip++ = g[ix]; *ip++ = b[ix]; \
  ix = (m & 0x0020)?(0):(1); *ip++ = r[ix]; *ip++ = g[ix]; *ip++ = b[ix]; \
  ix = (m & 0x0040)?(2):(3); *ip++ = r[ix]; *ip++ = g[ix]; *ip++ = b[ix]; \
  ix = (m & 0x0080)?(2):(3); *ip++ = r[ix]; *ip++ = g[ix]; *ip   = b[ix]; \
  ip += row_inc;							  \
  ix = (m & 0x0100)?(4):(5); *ip++ = r[ix]; *ip++ = g[ix]; *ip++ = b[ix]; \
  ix = (m & 0x0200)?(4):(5); *ip++ = r[ix]; *ip++ = g[ix]; *ip++ = b[ix]; \
  ix = (m & 0x0400)?(6):(7); *ip++ = r[ix]; *ip++ = g[ix]; *ip++ = b[ix]; \
  ix = (m & 0x0800)?(6):(7); *ip++ = r[ix]; *ip++ = g[ix]; *ip   = b[ix]; \
  ip += row_inc;							  \
  ix = (m & 0x1000)?(4):(5); *ip++ = r[ix]; *ip++ = g[ix]; *ip++ = b[ix]; \
  ix = (m & 0x2000)?(4):(5); *ip++ = r[ix]; *ip++ = g[ix]; *ip++ = b[ix]; \
  ix = (m & 0x4000)?(6):(7); *ip++ = r[ix]; *ip++ = g[ix]; *ip++ = b[ix]; \
  ix = (m & 0x8000)?(6):(7); *ip++ = r[ix]; *ip++ = g[ix]; *ip   = b[ix]; }

#define MVC1_PIX_2_DITH(pix,_r,_g,_b) { \
_r = (pix & 0x7c00); _g = (pix & 0x03e0); _b = (pix & 0x001f);       \
_r = _r | (_r >> 5);  _g = (_g << 5) | _g; _b = (_b << 10) | (_b << 5); }

#define MVC1_DITH2(dp,p0,r,g,b) \
{ MVC1_PIX_2_DITH(p0,r[0],g[0],b[0]);	r[2] = r[4] = r[6] = r[0];	\
  g[2] = g[4] = g[6] = g[0];	b[2] = b[4] = b[6] = b[0];		\
  p0 = *dp++ << 8; p0 |= *dp++;						\
  MVC1_PIX_2_DITH(p0,r[1],g[1],b[1]);	r[3] = r[5] = r[7] = r[1];	\
  g[3] = g[5] = g[7] = g[1];	b[3] = b[5] = b[7] = b[1];	}


#define MVC1_DITH8(dp,p0,r,g,b)	\
{ MVC1_PIX_2_DITH(p0,r[0],g[0],b[0]);	\
  p0 = *dp++ << 8; p0 |= *dp++;	MVC1_PIX_2_DITH(p0,r[1],g[1],b[1]);	\
  p0 = *dp++ << 8; p0 |= *dp++;	MVC1_PIX_2_DITH(p0,r[2],g[2],b[2]);	\
  p0 = *dp++ << 8; p0 |= *dp++;	MVC1_PIX_2_DITH(p0,r[3],g[3],b[3]);	\
  p0 = *dp++ << 8; p0 |= *dp++;	MVC1_PIX_2_DITH(p0,r[4],g[4],b[4]);	\
  p0 = *dp++ << 8; p0 |= *dp++;	MVC1_PIX_2_DITH(p0,r[5],g[5],b[5]);	\
  p0 = *dp++ << 8; p0 |= *dp++;	MVC1_PIX_2_DITH(p0,r[6],g[6],b[6]);	\
  p0 = *dp++ << 8; p0 |= *dp++;	MVC1_PIX_2_DITH(p0,r[7],g[7],b[7]);	}


#define MVC1_DITH_GET_RGB(r,g,b,re,ge,be,col) { xaLONG r1,g1,b1; \
  r1 = (xaLONG)rnglimit[(r + re) >> 7]; \
  g1 = (xaLONG)rnglimit[(g + ge) >> 7]; \
  b1 = (xaLONG)rnglimit[(b + be) >> 7]; \
  col = (r1 & 0xe0) | ((g1 & 0xe0) >> 3) | ((b1 & 0xc0) >> 6); }

#define MVC1_DITH_GET_ERR(r,g,b,re,ge,be,col,cmap) { \
  re =  ((xaLONG)(r) - (xaLONG)(cmap[col].red   >> 1)) >> 1; \
  ge =  ((xaLONG)(g) - (xaLONG)(cmap[col].green >> 1)) >> 1; \
  be =  ((xaLONG)(b) - (xaLONG)(cmap[col].blue  >> 1)) >> 1; }


xaULONG MOVI_Decode_MVC1(image,delta,dsize,dec_info)
xaUBYTE *image;         /* Image Buffer. */
xaUBYTE *delta;         /* delta data. */
xaULONG dsize;          /* delta size */
XA_DEC_INFO *dec_info;  /* Decoder Info Header */
{ xaULONG imagex = dec_info->imagex;    xaULONG imagey = dec_info->imagey;
  xaULONG mflag = dec_info->map_flag;        xaULONG *map = dec_info->map;
  xaULONG special = dec_info->special;
  XA_CHDR *chdr = dec_info->chdr;
  xaULONG row_inc;
  xaLONG x = 0, y = 0;
  xaUBYTE *dptr = delta;
  xaULONG dither = 0;

  dec_info->xs = dec_info->ys = 0; dec_info->xe = imagex; dec_info->ye = imagey;
  if (dec_info->skip_flag > 0) return(ACT_DLTA_DROP);

  if (chdr) { if (chdr->new_chdr) chdr = chdr->new_chdr; }
	/* POD rethink this */
  if ((cmap_color_func == 4) && (chdr))
  { if (cmap_cache == 0) CMAP_Cache_Init(0);
    if (chdr != cmap_cache_chdr) { CMAP_Cache_Clear(); cmap_cache_chdr = chdr; }
  }

  if (   (!special) && (x11_bytes_pixel==1)
      && (chdr) && (x11_display_type == XA_PSEUDOCOLOR)
      && (cmap_color_func != 4)
      && (cmap_true_to_332 == xaTRUE) && (x11_cmap_size == 256)
      && (xa_dither_flag==xaTRUE)
     ) dither = 1;

  
  row_inc = (special)?((3*(imagex-4))+1):(imagex - 3);

  if (special)
  { while( (dsize > 0) && (y < imagey))
    { xaUBYTE *ip = (xaUBYTE *)(image + 3 * (y * imagex + x) );
      xaUBYTE r[8],g[8],b[8];	xaULONG mask,p0;
      MOVI_GET_16(mask,dptr); MOVI_GET_16(p0,dptr);
      if (p0 & 0x8000)	{ MVC1_RGB8(dptr,p0,r,g,b); dsize -= 18; }
      else		{ MVC1_RGB2(dptr,p0,r,g,b); dsize -=  6; }
      MVC1_RGB_BLK8(ip,mask,r,g,b);
      MOVI_BLOCK_INC(x,y,imagex);
    }
  }
  else if (dither)
  { xaUBYTE *rnglimit = xa_byte_limit;
    ColorReg *cmap = chdr->cmap;
    while( (dsize > 0) && (y < imagey))
    { xaUBYTE *ip0 = (xaUBYTE *)(image + (y * imagex + x) );
      xaULONG r[8],g[8],b[8];	xaULONG mask,p0;
      MOVI_GET_16(mask,dptr); MOVI_GET_16(p0,dptr);
      if (p0 & 0x8000)  { MVC1_DITH8(dptr,p0,r,g,b); dsize -= 12; }
      else		{ MVC1_DITH2(dptr,p0,r,g,b); dsize -=  6; }

      { xaUBYTE *ip1 = ip0, *ip2, *ip3;
	xaLONG ix, ra, ga, ba, col, re = 0, ge = 0, be = 0;
	ip1 += imagex; ip2 = ip1; ip2 += imagex; ip3 = ip2; ip3 += imagex;
	/** 0 0 */
	    ix = (mask & 0x0001)?(0):(1); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip0[0] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/*  0 1 */
	    ix = (mask & 0x0002)?(0):(1); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip0[1] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/*  1 1 */
	    ix = (mask & 0x0020)?(0):(1); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip1[1] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/*  1 0 */
	    ix = (mask & 0x0010)?(0):(1); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip1[0] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/** 2 0 */
	    ix = (mask & 0x0100)?(4):(5); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip2[0] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/*  3 0 */
	    ix = (mask & 0x1000)?(4):(5); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip3[0] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/*  3 1 */
	    ix = (mask & 0x2000)?(4):(5); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip3[1] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/*  2 1 */
	    ix = (mask & 0x0200)?(4):(5); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip2[1] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/** 2 2 */
	    ix = (mask & 0x0400)?(6):(7); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip2[2] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/*  3 2 */
	    ix = (mask & 0x4000)?(6):(7); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip3[2] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/*  3 3 */
	    ix = (mask & 0x8000)?(6):(7); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip3[3] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/*  2 3 */
	    ix = (mask & 0x0800)?(6):(7); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip2[3] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/** 1 3 */
	    ix = (mask & 0x0080)?(2):(3); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip1[3] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/*  1 2 */
	    ix = (mask & 0x0040)?(2):(3); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip1[2] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/*  0 2 */
	    ix = (mask & 0x0004)?(2):(3); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip0[2] = (xaUBYTE)col;
	    MVC1_DITH_GET_ERR(ra,ga,ba,re,ge,be,col,cmap);
	/*  0 3 */
	    ix = (mask & 0x0008)?(2):(3); ra = r[ix]; ga = g[ix]; ba = b[ix];
	    MVC1_DITH_GET_RGB(ra,ga,ba,re,ge,be,col);
	    if (mflag) col = map[col];	ip0[3] = (xaUBYTE)col;
      }
      MOVI_BLOCK_INC(x,y,imagex);
    }
  }
  else if ((x11_bytes_pixel == 1) || (mflag == xaFALSE))
  { while( (dsize > 0) && (y < imagey))
    { xaUBYTE *ip = (xaUBYTE *)(image + (y * imagex + x) );
      xaUBYTE c0,c1,c2,c3,c4,c5,c6,c7;
      xaULONG mask,p0;
      MOVI_GET_16(mask,dptr); MOVI_GET_16(p0,dptr);	dsize -= 6;
      if (p0 & 0x8000)  { dsize -= 12; 
	  MVC1_COLOR8(dptr,p0,c0,c1,c2,c3,c4,c5,c6,c7,mflag,map,chdr,xaUBYTE); }
      else MVC1_COLOR2(dptr,p0,c0,c1,c2,c3,c4,c5,c6,c7,mflag,map,chdr,xaUBYTE)
      MVC1_BLK8(ip,mask,c0,c1,c2,c3,c4,c5,c6,c7);
      MOVI_BLOCK_INC(x,y,imagex);
    }
  } /* end depth 8 */
  else if (x11_bytes_pixel == 4)
  { 
    while( (dsize > 0) && (y < imagey))
    { xaULONG *ip = (xaULONG *)(image + ((y * imagex + x)<<2) );
      xaULONG c0,c1,c2,c3,c4,c5,c6,c7;
      xaULONG mask,p0;
      MOVI_GET_16(mask,dptr); MOVI_GET_16(p0,dptr);	dsize -= 6;
      if (p0 & 0x8000)  { dsize -= 12; 
	  MVC1_COLOR8(dptr,p0,c0,c1,c2,c3,c4,c5,c6,c7,mflag,map,chdr,xaULONG); }
      else MVC1_COLOR2(dptr,p0,c0,c1,c2,c3,c4,c5,c6,c7,mflag,map,chdr,xaULONG)

      MVC1_BLK8(ip,mask,c0,c1,c2,c3,c4,c5,c6,c7);
      MOVI_BLOCK_INC(x,y,imagex);
    }
  } /* end depth 24/32 */
  else /* if (x11_bytes_pixel == 2) */
  { while( (dsize > 0) && (y < imagey))
    { xaUSHORT *ip = (xaUSHORT *)(image + ((y * imagex + x)<<1) );
      xaUSHORT c0,c1,c2,c3,c4,c5,c6,c7;
      xaULONG mask,p0;		dsize -= 6;
      MOVI_GET_16(mask,dptr); MOVI_GET_16(p0,dptr);	dsize -= 6;
      if (p0 & 0x8000)  { dsize -= 12; 
	  MVC1_COLOR8(dptr,p0,c0,c1,c2,c3,c4,c5,c6,c7,mflag,map,chdr,xaUSHORT); }
      else MVC1_COLOR2(dptr,p0,c0,c1,c2,c3,c4,c5,c6,c7,mflag,map,chdr,xaUSHORT)
      MVC1_BLK8(ip,mask,c0,c1,c2,c3,c4,c5,c6,c7);
      MOVI_BLOCK_INC(x,y,imagex);
    }
  } /* end depth 16 */

  if (mflag) return(ACT_DLTA_MAPD);
  else return(ACT_DLTA_NORM);
}

xaULONG MOVI_Decode_MVC2(image,delta,dsize,dec_info)
xaUBYTE *image;         /* Image Buffer. */
xaUBYTE *delta;         /* delta data. */
xaULONG dsize;          /* delta size */
XA_DEC_INFO *dec_info;  /* Decoder Info Header */
{ xaULONG imagex = dec_info->imagex;    xaULONG imagey = dec_info->imagey;
  xaULONG mflag = dec_info->map_flag;        xaULONG *map = dec_info->map;
  xaULONG special = dec_info->special;
  XA_CHDR *chdr = dec_info->chdr;
  xaULONG row_inc;
  xaLONG x = 0, y = 0;
  xaUBYTE *dptr = delta;
  xaULONG dither = 0;

  dec_info->xs = dec_info->ys = 0; dec_info->xe = imagex; dec_info->ye = imagey;
  if (dec_info->skip_flag > 0) return(ACT_DLTA_DROP);

  if (chdr) { if (chdr->new_chdr) chdr = chdr->new_chdr; }
	/* POD rethink this */
  if ((cmap_color_func == 4) && (chdr))
  { if (cmap_cache == 0) CMAP_Cache_Init(0);
    if (chdr != cmap_cache_chdr) { CMAP_Cache_Clear(); cmap_cache_chdr = chdr; }
  }

  if (   (!special) && (x11_bytes_pixel==1)
      && (chdr) && (x11_display_type == XA_PSEUDOCOLOR)
      && (cmap_color_func != 4)
      && (cmap_true_to_332 == xaTRUE) && (x11_cmap_size == 256)
      && (xa_dither_flag==xaTRUE)
     ) dither = 1;

  
  row_inc = (special)?((3*(imagex-4))+1):(imagex - 3);

  if (special)
  { fprintf(stderr,"NOT YET\n"); }
  else if (dither)
  { fprintf(stderr,"NOT YET\n"); }
  else if ((x11_bytes_pixel == 1) || (mflag == xaFALSE))
  { while( (dsize > 0) && (y < imagey))
    { xaUBYTE *ip = (xaUBYTE *)(image + (y * imagex + x) );
      xaUBYTE c0,c1,c2,c3,c4,c5,c6,c7;
      xaULONG mask,p0;
      MOVI_GET_16(mask,dptr); MOVI_GET_16(p0,dptr);	dsize -= 6;
      if (p0 & 0x8000)  { dsize -= 12; 
	  MVC1_COLOR8(dptr,p0,c0,c1,c2,c3,c4,c5,c6,c7,mflag,map,chdr,xaUBYTE); }
      else MVC1_COLOR2(dptr,p0,c0,c1,c2,c3,c4,c5,c6,c7,mflag,map,chdr,xaUBYTE)
      MVC1_BLK8(ip,mask,c0,c1,c2,c3,c4,c5,c6,c7);
      MOVI_BLOCK_INC(x,y,imagex);
    }
  }
  else if (x11_bytes_pixel == 4)
  { fprintf(stderr,"NOT YET\n"); }
  else /* if (x11_bytes_pixel == 2) */
  { fprintf(stderr,"NOT YET\n"); }

  if (mflag) return(ACT_DLTA_MAPD);
  else return(ACT_DLTA_NORM);
}

xaULONG MOVI_Get_Color(color,map_flag,map,chdr)
xaULONG color,map_flag,*map;
XA_CHDR *chdr;
{
  register xaULONG clr,ra,ga,ba,tr,tg,tb;
 
  ra = (color >> 10) & 0x1f;
  ga = (color >>  5) & 0x1f;
  ba =  color & 0x1f;
  tr = (ra << 3) | (ra >> 2);
  tg = (ga << 3) | (ga >> 2);
  tb = (ba << 3) | (ba >> 2);
  if (xa_gamma_flag==xaTRUE) { tr = xa_gamma_adj[tr]>>8;  
     tg = xa_gamma_adj[tg]>>8; tb = xa_gamma_adj[tb]>>8; }

 
  if (x11_display_type & XA_X11_TRUE) clr = X11_Get_True_Color(tr,tg,tb,8);
  else
  { 
    if ((cmap_color_func == 4) && (chdr))
    { register xaULONG cache_i = color & 0x7fff;

      if (cmap_cache[cache_i] == 0xffff)
      {
        clr = chdr->coff + 
	   CMAP_Find_Closest(chdr->cmap,chdr->csize,ra,ga,ba,5,5,5,xaTRUE);
        cmap_cache[cache_i] = (xaUSHORT)clr;
      }
      else clr = (xaULONG)cmap_cache[cache_i];
    }
    else
    {
      if (cmap_true_to_332 == xaTRUE) 
	  clr=CMAP_GET_332(ra,ga,ba,CMAP_SCALE5);
      else   clr = CMAP_GET_GRAY(ra,ga,ba,CMAP_SCALE10);
      if (map_flag) clr = map[clr];
    }
  }
  return(clr);
}
