
/*
 * xa_jmov.c
 *
 * Copyright (C) 1995,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_jmov.h" 

xaULONG JMOV_Read_File();
JMOV_FRAME *JMOV_Add_Frame();
void JMOV_Free_Frame_List();
void ACT_Setup_Delta();
xaULONG JMOV_Read_Header();
void JMOV_Read_Index();
void JMOV_Read_Frame();


/* CODEC ROUTINES */
xaULONG JFIF_Decode_JPEG();
void JFIF_Read_IJPG_Tables();
extern void XA_Gen_YUV_Tabs();
extern JPG_Alloc_MCU_Bufs();
extern jpg_search_marker();
extern IJPG_Tab1[64];
extern IJPG_Tab2[64];
extern JFIF_Init_IJPG_Tables();

void CMAP_Cache_Clear();
void CMAP_Cache_Init();

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();


xaULONG jmov_audio_attempt;
xaULONG jmov_audio_type;
xaULONG jmov_audio_freq;
xaULONG jmov_audio_chans;
xaULONG jmov_audio_bps;
xaULONG XA_Add_Sound();


xaULONG jmov_frame_cnt;
JMOV_FRAME *jmov_frame_start,*jmov_frame_cur;

JMOV_FRAME *JMOV_Add_Frame(time,timelo,act)
xaULONG time,timelo;
XA_ACTION *act;
{
  JMOV_FRAME *fframe;
 
  fframe = (JMOV_FRAME *) malloc(sizeof(JMOV_FRAME));
  if (fframe == 0) TheEnd1("JMOV_Add_Frame: malloc err");
 
  fframe->time   = time;
  fframe->timelo = timelo;
  fframe->act = act;
  fframe->next = 0;
 
  if (jmov_frame_start == 0) jmov_frame_start = fframe;
  else jmov_frame_cur->next = fframe;
 
  jmov_frame_cur = fframe;
  jmov_frame_cnt++;
  return(fframe);
}

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


xaULONG JMOV_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 *jmov;
  JMOV_HDR   jmov_hdr;
 
  jmov = XA_Get_Anim_Setup();
  jmov->vid_time = XA_GET_TIME( 100 ); /* default */

  jmov_frame_cnt	= 0;
  jmov_frame_start	= 0;
  jmov_frame_cur	= 0;
  jmov_audio_attempt	= audio_attempt;

  if (JMOV_Read_Header(xin,anim_hdr,jmov,&jmov_hdr) == xaFALSE)
  {
    fprintf(stderr,"JMOV: read header error\n");
    xin->Close_File(xin);
    return(xaFALSE);
  }

  JMOV_Read_Index(xin,anim_hdr,jmov,&jmov_hdr);


  if (xa_verbose) 
  {
    fprintf(stderr,"JMOV %dx%dx%d frames %d\n",
		jmov->imagex,jmov->imagey,jmov->imagec,jmov_frame_cnt);
  }
  if (jmov_frame_cnt == 0)
  { 
    fprintf(stderr,"JMOV: No supported video frames exist in this file.\n");
    return(xaFALSE);
  }

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

  jmov_frame_cur = jmov_frame_start;
  i = 0;
  t_time = 0;
  t_timelo = 0;
  while(jmov_frame_cur != 0)
  {
    if (i > jmov_frame_cnt)
    {
      fprintf(stderr,"JMOV_Read_Anim: frame inconsistency %d %d\n",
                i,jmov_frame_cnt);
      break;
    }
    anim_hdr->frame_lst[i].time_dur = jmov_frame_cur->time;
    anim_hdr->frame_lst[i].zztime = t_time;
    t_time += jmov_frame_cur->time;
    t_timelo += jmov_frame_cur->timelo;
    while(t_timelo > (1<<24)) {t_time++; t_timelo -= (1<<24);}
    anim_hdr->frame_lst[i].act = jmov_frame_cur->act;
    jmov_frame_cur = jmov_frame_cur->next;
    i++;
  }
  anim_hdr->imagex = jmov->imagex;
  anim_hdr->imagey = jmov->imagey;
  anim_hdr->imagec = jmov->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 = jmov->max_fvid_size;
  anim_hdr->max_faud_size = jmov->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;
  }
  JMOV_Free_Frame_List(jmov_frame_start);
  XA_Free_Anim_Setup(jmov);
  return(xaTRUE);
} /* end of read file */


xaULONG JMOV_Read_Header(xin,anim_hdr,jmov,jmov_hdr)
XA_INPUT *xin;
XA_ANIM_HDR *anim_hdr;
XA_ANIM_SETUP *jmov;
JMOV_HDR *jmov_hdr;
{ xaULONG t;
  char *audio_desc;
  t 			= xin->Read_MSB_U32(xin); /* skip 8 bytes of header */
  t 			= xin->Read_MSB_U32(xin); 
  jmov_hdr->version	= xin->Read_MSB_U32(xin);
  jmov_hdr->fps		= xin->Read_MSB_U32(xin);
  jmov_hdr->frames	= xin->Read_MSB_U32(xin);
  jmov_hdr->width	= xin->Read_MSB_U32(xin);  
  jmov_hdr->height	= xin->Read_MSB_U32(xin);
  jmov_hdr->bandwidth	= xin->Read_MSB_U32(xin);
  jmov_hdr->qfactor	= xin->Read_MSB_U32(xin);
  jmov_hdr->mapsize	= xin->Read_MSB_U32(xin);
  jmov_hdr->indexbuf	= xin->Read_MSB_U32(xin);
  jmov_hdr->tracks	= xin->Read_MSB_U32(xin);
  jmov_hdr->volbase 	= xin->Read_MSB_U32(xin); 
  jmov_hdr->audioslice	= xin->Read_MSB_U32(xin);
/* Sun's Audio_hdr */
  jmov_hdr->freq	= xin->Read_MSB_U32(xin);
  xin->Read_MSB_U32(xin); /* sja throw away samples per unit */
  jmov_hdr->prec	= xin->Read_MSB_U32(xin);
  jmov_hdr->chans	= xin->Read_MSB_U32(xin);
  jmov_hdr->codec	= xin->Read_MSB_U32(xin);

  UTIL_FPS_2_Time(jmov, ((double)(jmov_hdr->fps)) );

  jmov_audio_freq = jmov_hdr->freq;
  jmov_audio_chans = jmov_hdr->chans;
  jmov_audio_bps   = jmov_hdr->prec;

  jmov_audio_type = XA_AUDIO_INVALID;
  switch(jmov_hdr->codec)
  {
    case JMOV_AUDIO_ENC_NONE:
    case JMOV_AUDIO_ENC_PCM:
	if (jmov_audio_bps == 1) jmov_audio_type = XA_AUDIO_LINEAR;
	else			 jmov_audio_type = XA_AUDIO_SIGNED;
        audio_desc = "PCM";
	break;
    case JMOV_AUDIO_ENC_ULAW:
	jmov_audio_type = XA_AUDIO_ULAW;
        audio_desc = "ULAW";
	break;
  }
  if ( (jmov_audio_chans > 2) || (jmov_audio_chans == 0) )
					jmov_audio_type = XA_AUDIO_INVALID;
  if ( (jmov_audio_bps > 2) || (jmov_audio_bps == 0) )
					jmov_audio_type = XA_AUDIO_INVALID;

  if (jmov_audio_chans == 2) jmov_audio_type |= XA_AUDIO_STEREO_MSK;
  if (jmov_audio_bps == 2)   
  {
    jmov_audio_type |= XA_AUDIO_BPS_2_MSK;
    jmov_audio_type |= XA_AUDIO_BIGEND_MSK;
  }

  if (jmov_hdr->audioslice)
  { if (jmov_audio_type == XA_AUDIO_INVALID)
    { if (jmov_audio_attempt == xaTRUE) 
	fprintf(stderr,"JMOV: Audio Type unsupported %d \n",jmov_hdr->codec);
      jmov_audio_attempt = xaFALSE;
    }
    else
    { if (xa_verbose) 
      { fprintf(stderr,"  Audio Codec: %s Rate=%d Chans=%d bps=%d\n",
	audio_desc,jmov_audio_freq,jmov_audio_chans,
			(jmov_audio_bps==1)?(8):(16) );
      }
    }
  }

  /* JFIF_Init_IJPG_Tables( jmov_hdr->qfactor ); */
  JFIF_Init_IJPG_Tables( 1 );

DEBUG_LEVEL1
{
  fprintf(stderr,"JMOV: ver %d  fps %d frame %d  res %dx%d\n",
	jmov_hdr->version, jmov_hdr->fps, jmov_hdr->frames,
	jmov_hdr->width, jmov_hdr->height);
  fprintf(stderr,"      qf %d mapsize %d indx %x tracks %d audsz %x\n",
	jmov_hdr->qfactor, jmov_hdr->mapsize, jmov_hdr->indexbuf,
	jmov_hdr->tracks, jmov_hdr->audioslice);
}

  jmov->depth = 24;
  jmov->imagex = jmov_hdr->width;
  jmov->imagey = jmov_hdr->height;
  jmov->imagex = 4 * ((jmov->imagex + 3)/4);
  jmov->imagey = 4 * ((jmov->imagey + 3)/4);
  XA_Gen_YUV_Tabs(anim_hdr);
  JPG_Alloc_MCU_Bufs(anim_hdr,jmov->imagex,0,xaFALSE);

  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)
             jmov->chdr = CMAP_Create_332(jmov->cmap,&jmov->imagec);
     else    jmov->chdr = CMAP_Create_Gray(jmov->cmap,&jmov->imagec);
  }
  if ( (jmov->pic==0) && (xin->load_flag & XA_IN_LOAD_BUF))
  {
    jmov->pic_size = jmov->imagex * jmov->imagey;
    if ( (cmap_true_map_flag == xaTRUE) && (jmov->depth > 8) )
                jmov->pic = (xaUBYTE *) malloc( 3 * jmov->pic_size );
    else jmov->pic = (xaUBYTE *) malloc( XA_PIC_SIZE(jmov->pic_size) );
    if (jmov->pic == 0) TheEnd1("JMOV_Buffer_Action: malloc failed");
  }
  return(xaTRUE);
}


void JMOV_Read_Index(xin,anim_hdr,jmov,jmov_hdr)
XA_INPUT *xin;
XA_ANIM_HDR *anim_hdr;
XA_ANIM_SETUP *jmov;
JMOV_HDR *jmov_hdr;
{ xaLONG ret;
  xaULONG idx_off = jmov_hdr->indexbuf;
  xaULONG idx_cnt = jmov_hdr->frames;
  xaULONG i,*index;
 

  index = (xaULONG *)malloc( (idx_cnt + 1) * sizeof(xaULONG) );

  ret = xin->Seek_FPos(xin,idx_off,0);
  for(i=0; i<idx_cnt; i++)
  {
    if (xin->At_EOF(xin,-1))
    {
      fprintf(stderr,"JMOV: index truncated cur %d tot %d\n",i,idx_cnt);
      idx_cnt = i;
      break;
    }
    index[i] = xin->Read_MSB_U32(xin);
  }
  index[idx_cnt] = 0;

  for(i=0; i<idx_cnt; i++)
  { xaULONG offset = index[i];
    xin->Seek_FPos(xin,offset,0);
    JMOV_Read_Frame(xin,anim_hdr,jmov,jmov_hdr); 
  } 
}

#define JMOV_END_FRAME  0xed
#define JMOV_CLR_FRAME  0xee
#define JMOV_AUDIO_0    0xe5
#define JMOV_AUDIO_1    0xe4
#define JMOV_AUDIO_2    0xe3
#define JMOV_AUDIO_3    0xe2
#define JMOV_CMAP       0xd9
#define JMOV_MMAP       0xf8
#define JMOV_FPS        0xdb
#define JMOV_JPEG       0xec


void JMOV_Read_Frame(xin,anim_hdr,jmov,jmov_hdr)
XA_INPUT *xin;
XA_ANIM_HDR *anim_hdr;
XA_ANIM_SETUP *jmov;
JMOV_HDR *jmov_hdr;
{ xaULONG exit_flag;

  exit_flag = xaFALSE;
  while( (!xin->At_EOF(xin,-1)) & (exit_flag == xaFALSE))
  { xaULONG type;
    type = xin->Read_U8(xin);
    switch(type)
    {
      case JMOV_END_FRAME:
	DEBUG_LEVEL1 fprintf(stderr,"JMOV: END_FRAME\n");
	exit_flag = xaTRUE;
	break;
      case JMOV_CLR_FRAME:
	DEBUG_LEVEL1 fprintf(stderr,"JMOV: CLR_FRAME\n");
	break;
      case JMOV_AUDIO_0:
      case JMOV_AUDIO_1:
      case JMOV_AUDIO_2:
      case JMOV_AUDIO_3:
	{ xaULONG snd_size = jmov_hdr->audioslice;
	DEBUG_LEVEL1 fprintf(stderr,"JMOV: AUDIO %x\n",type);

	  if (jmov_audio_attempt==xaTRUE)
	  { xaLONG ret;
	    if (xin->load_flag & XA_IN_LOAD_FILE)
	    { xaLONG rets, cur_fpos = xin->Get_FPos(xin);
	      rets = XA_Add_Sound(anim_hdr,0,jmov_audio_type, cur_fpos,
			jmov_audio_freq, snd_size, 
			&jmov->aud_time,&jmov->aud_timelo, 0, 0);
	      if (rets==xaFALSE) jmov_audio_attempt = xaFALSE;
	      xin->Seek_FPos(xin,snd_size,1); /* move past this chunk */
	      if (snd_size > jmov->max_faud_size) 
				jmov->max_faud_size = snd_size;
 
	    }
	    else
	    { xaUBYTE *snd = (xaUBYTE *)malloc(snd_size);
	      if (snd==0) TheEnd1("JMOV: snd malloc err");
	      ret = xin->Read_Block(xin, snd, snd_size);
	      if (ret < snd_size) fprintf(stderr,"JMOV: snd rd err\n");
	      else
	      { int rets;
	        rets = XA_Add_Sound(anim_hdr,snd,jmov_audio_type, -1,
                                        jmov_audio_freq, snd_size,
                                        &jmov->aud_time, &jmov->aud_timelo, 0, 0);
	        if (rets==xaFALSE) jmov_audio_attempt = xaFALSE;
	      }
	    }
	  }
	  else xin->Seek_FPos(xin,snd_size,1); /* skip over */
	}
	break;
      case JMOV_CMAP:
	{ xaULONG size = jmov_hdr->mapsize * 3;
	  DEBUG_LEVEL1 fprintf(stderr,"JMOV: CMAP - ignored\n");
          if (jmov_hdr->mapsize > (16*1024) )
	  {
	    fprintf(stderr,"JMOV: CMAP just too big %d\n",jmov_hdr->mapsize);
	    return;
	  }
          xin->Seek_FPos(xin,size,1);  /* skip over */
	}
	break;
      case JMOV_MMAP:
	{ xaULONG cnt;
	  DEBUG_LEVEL1 fprintf(stderr,"JMOV: MMAP - ignored\n");

          cnt = xin->Read_U8(xin);
	  while(cnt--)
          { xaULONG slot,r,g,b;
	    slot = xin->Read_MSB_U16();
	    r = xin->Read_U8(xin); g = xin->Read_U8(xin); b = xin->Read_U8(xin);
          }
        }
	break;
      case JMOV_FPS:
        { xaULONG fps;
	  DEBUG_LEVEL1 fprintf(stderr,"JMOV: FPS\n");
	  fps = xin->Read_MSB_U32(xin);
	  UTIL_FPS_2_Time(jmov, ((double)(fps)) );
        }
	break;
      case JMOV_JPEG:
        { xaULONG dlta_len;
	  XA_ACTION *act;
          ACT_DLTA_HDR *dlta_hdr;

	  DEBUG_LEVEL1 fprintf(stderr,"JMOV: JPEG\n");
	  dlta_len = xin->Read_MSB_U32(xin);

	  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("JMOV: dlta malloc err");
	    act->data = (xaUBYTE *)dlta_hdr;
	    dlta_hdr->flags = ACT_SNGL_BUF;
	    dlta_hdr->fsize = dlta_len;
	    dlta_hdr->fpos  = xin->Get_FPos(xin);
	    if (dlta_len > jmov->max_fvid_size) jmov->max_fvid_size = dlta_len;
	    xin->Seek_FPos(xin,dlta_len,1);
	  }
	  else
	  { xaULONG d; xaLONG ret;
	    d = dlta_len + (sizeof(ACT_DLTA_HDR));
	    dlta_hdr = (ACT_DLTA_HDR *) malloc( d );
	    if (dlta_hdr == 0) TheEnd1("QT rle: 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->Read_Block(xin, dlta_hdr->data, dlta_len);
	    if (ret < dlta_len) { fprintf(stderr,"JMOV: read err\n"); return; }
	  }
	  JMOV_Add_Frame(jmov->vid_time,jmov->vid_timelo,act);
	  dlta_hdr->xpos = dlta_hdr->ypos = 0;
	  dlta_hdr->xsize = jmov->imagex;
	  dlta_hdr->ysize = jmov->imagey;
	  dlta_hdr->special = 0;
	  dlta_hdr->extra = (void *)(0x10);
	  dlta_hdr->xapi_rev = 0x0002;
	  dlta_hdr->delta = JFIF_Decode_JPEG;
	  ACT_Setup_Delta(jmov,act,dlta_hdr,xin);
        }
	break;
      default:
	fprintf(stderr,"JMOV: Unknown type %x - aborting\n",type);
	return;
	break;
    } /* end of switch */
  } /* end of while */
}

