/* getvlc.c, variable length decoding                                       */

/* Copyright (C) 1994, MPEG Software Simulation Group. All Rights Reserved. */

/*
 * Disclaimer of Warranty
 *
 * These software programs are available to the user without any license fee or
 * royalty on an "as is" basis.  The MPEG Software Simulation Group disclaims
 * any and all warranties, whether express, implied, or statuary, including any
 * implied warranties or merchantability or of fitness for a particular
 * purpose.  In no event shall the copyright-holder be liable for any
 * incidental, punitive, or consequential damages of any kind whatsoever
 * arising from the use of these programs.
 *
 * This disclaimer of warranty extends to the user of these programs and user's
 * customers, employees, agents, transferees, successors, and assigns.
 *
 * The MPEG Software Simulation Group does not represent or warrant that the
 * programs furnished hereunder are free of infringement of any third-party
 * patents.
 *
 * Commercial implementations of MPEG-1 and MPEG-2 video, including shareware,
 * are subject to royalty fees to patent holders.  Many of these patents are
 * general enough such that they are unavoidable regardless of implementation
 * design.
 *
 */

#include <stdio.h>

#include "config.h"
#include "global.h"
#include "getvlc.h"

/* private prototypes */
static int getIMBtype _ANSI_ARGS_((void));
static int getPMBtype _ANSI_ARGS_((void));
static int getBMBtype _ANSI_ARGS_((void));
static int getDMBtype _ANSI_ARGS_((void));
static int getspIMBtype _ANSI_ARGS_((void));
static int getspPMBtype _ANSI_ARGS_((void));
static int getspBMBtype _ANSI_ARGS_((void));
static int getSNRMBtype _ANSI_ARGS_((void));

int getMBtype()
{
  int mb_type;

  if (ld->scalable_mode==SC_SNR)
    mb_type = getSNRMBtype();
  else
  {
    switch (pict_type)
    {
    case I_TYPE:
      mb_type = ld->pict_scal ? getspIMBtype() : getIMBtype();
      break;
    case P_TYPE:
      mb_type = ld->pict_scal ? getspPMBtype() : getPMBtype();
      break;
    case B_TYPE:
      mb_type = ld->pict_scal ? getspBMBtype() : getBMBtype();
      break;
    case D_TYPE:
      /* MPEG-1 only, not implemented */
      mb_type = getDMBtype();
      break;
    }
  }

  return mb_type;
}

static int getIMBtype()
{
  if (trace)
    printf("mb_type(I) ");

  if (getbits1())
  {
    if (trace)
      printf("(1): Intra (1)\n");
    return 1;
  }

  if (!getbits1())
  {
    if (!quiet)
      fprintf(stderr,"Invalid macroblock_type code\n");
    fault = 1;
  }

  if (trace)
    printf("(01): Intra, Quant (17)\n");

  return 17;
}

static char *MBdescr[]={
  "",                  "Intra",        "No MC, Coded",         "",
  "Bwd, Not Coded",    "",             "Bwd, Coded",           "",
  "Fwd, Not Coded",    "",             "Fwd, Coded",           "",
  "Interp, Not Coded", "",             "Interp, Coded",        "",
  "",                  "Intra, Quant", "No MC, Coded, Quant",  "",
  "",                  "",             "Bwd, Coded, Quant",    "",
  "",                  "",             "Fwd, Coded, Quant",    "",
  "",                  "",             "Interp, Coded, Quant", ""
};

static int getPMBtype()
{
  int code;

  if (trace)
    printf("mb_type(P) (");

  if ((code = showbits(6))>=8)
  {
    code >>= 3;
    flushbits(PMBtab0[code].len);
    if (trace)
    {
      printbits(code,3,PMBtab0[code].len);
      printf("): %s (%d)\n",MBdescr[PMBtab0[code].val],PMBtab0[code].val);
    }
    return PMBtab0[code].val;
  }

  if (code==0)
  {
    if (!quiet)
      fprintf(stderr,"Invalid macroblock_type code\n");
    fault = 1;
    return 0;
  }

  flushbits(PMBtab1[code].len);

  if (trace)
  {
    printbits(code,6,PMBtab1[code].len);
    printf("): %s (%d)\n",MBdescr[PMBtab1[code].val],PMBtab1[code].val);
  }

  return PMBtab1[code].val;
}

static int getBMBtype()
{
  int code;

  if (trace)
    printf("mb_type(B) (");

  if ((code = showbits(6))>=8)
  {
    code >>= 2;
    flushbits(BMBtab0[code].len);

    if (trace)
    {
      printbits(code,4,BMBtab0[code].len);
      printf("): %s (%d)\n",MBdescr[BMBtab0[code].val],BMBtab0[code].val);
    }

    return BMBtab0[code].val;
  }

  if (code==0)
  {
    if (!quiet)
      fprintf(stderr,"Invalid macroblock_type code\n");
    fault = 1;
    return 0;
  }

  flushbits(BMBtab1[code].len);

  if (trace)
  {
    printbits(code,6,BMBtab1[code].len);
    printf("): %s (%d)\n",MBdescr[BMBtab1[code].val],BMBtab1[code].val);
  }

  return BMBtab1[code].val;
}

static int getDMBtype()
{
  if (!getbits1())
  {
    if (!quiet)
      fprintf(stderr,"Invalid macroblock_type code\n");
    fault=1;
  }

  return 1;
}

/* macroblock_type for pictures with spatial scalability */
static int getspIMBtype()
{
  int code;

  if (trace)
    printf("mb_type(I,spat) (");

  code = showbits(4);

  if (code==0)
  {
    if (!quiet)
      fprintf(stderr,"Invalid macroblock_type code\n");
    fault = 1;
    return 0;
  }

  if (trace)
  {
    printbits(code,4,spIMBtab[code].len);
    printf("): %02x\n",spIMBtab[code].val);
  }

  flushbits(spIMBtab[code].len);
  return spIMBtab[code].val;
}

static int getspPMBtype()
{
  int code;

  if (trace)
    printf("mb_type(P,spat) (");

  code = showbits(7);

  if (code<2)
  {
    if (!quiet)
      fprintf(stderr,"Invalid macroblock_type code\n");
    fault = 1;
    return 0;
  }

  if (code>=16)
  {
    code >>= 3;
    flushbits(spPMBtab0[code].len);

    if (trace)
    {
      printbits(code,4,spPMBtab0[code].len);
      printf("): %02x\n",spPMBtab0[code].val);
    }

    return spPMBtab0[code].val;
  }

  flushbits(spPMBtab1[code].len);

  if (trace)
  {
    printbits(code,7,spPMBtab1[code].len);
    printf("): %02x\n",spPMBtab1[code].val);
  }

  return spPMBtab1[code].val;
}

static int getspBMBtype()
{
  int code;
  VLCtab *p;

  if (trace)
    printf("mb_type(B,spat) (");

  code = showbits(9);

  if (code>=64)
    p = &spBMBtab0[(code>>5)-2];
  else if (code>=16)
    p = &spBMBtab1[(code>>2)-4];
  else if (code>=8)
    p = &spBMBtab2[code-8];
  else
  {
    if (!quiet)
      fprintf(stderr,"Invalid macroblock_type code\n");
    fault = 1;
    return 0;
  }

  flushbits(p->len);

  if (trace)
  {
    printbits(code,9,p->len);
    printf("): %02x\n",p->val);
  }

  return p->val;
}

static int getSNRMBtype()
{
  int code;

  code = showbits(3);

  if (code==0)
  {
    if (!quiet)
      fprintf(stderr,"Invalid macroblock_type code\n");
    fault = 1;
    return 0;
  }

  flushbits(SNRMBtab[code].len);
  return SNRMBtab[code].val;
}

int getMV()
{
  int code;

  if (trace)
    printf("motion_code (");

  if (getbits1())
  {
    if (trace)
      printf("0): 0\n");
    return 0;
  }

  if ((code = showbits(9))>=64)
  {
    code >>= 6;
    flushbits(MVtab0[code].len);

    if (trace)
    {
      printbits(code,3,MVtab0[code].len);
      printf("%d): %d\n",
        showbits(1),showbits(1)?-MVtab0[code].val:MVtab0[code].val);
    }

    return getbits1()?-MVtab0[code].val:MVtab0[code].val;
  }

  if (code>=24)
  {
    code >>= 3;
    flushbits(MVtab1[code].len);

    if (trace)
    {
      printbits(code,6,MVtab1[code].len);
      printf("%d): %d\n",
        showbits(1),showbits(1)?-MVtab1[code].val:MVtab1[code].val);
    }

    return getbits1()?-MVtab1[code].val:MVtab1[code].val;
  }

  if ((code-=12)<0)
  {
    if (!quiet)
      fprintf(stderr,"Invalid motion_vector code\n");
    fault=1;
    return 0;
  }

  flushbits(MVtab2[code].len);

  if (trace)
  {
    printbits(code+12,9,MVtab2[code].len);
    printf("%d): %d\n",
      showbits(1),showbits(1)?-MVtab2[code].val:MVtab2[code].val);
  }

  return getbits1() ? -MVtab2[code].val : MVtab2[code].val;
}

/* get differential motion vector (for dual prime prediction) */
int getDMV()
{
  if (trace)
    printf("dmvector (");

  if (getbits(1))
  {
    if (trace)
      printf(showbits(1) ? "11): -1\n" : "10): 1\n");
    return getbits(1) ? -1 : 1;
  }
  else
  {
    if (trace)
      printf("0): 0\n");
    return 0;
  }
}

int getCBP()
{
  int code;

  if (trace)
    printf("coded_block_pattern_420 (");

  if ((code = showbits(9))>=128)
  {
    code >>= 4;
    flushbits(CBPtab0[code].len);

    if (trace)
    {
      printbits(code,5,CBPtab0[code].len);
      printf("): ");
      printbits(CBPtab0[code].val,6,6);
      printf(" (%d)\n",CBPtab0[code].val);
    }

    return CBPtab0[code].val;
  }

  if (code>=8)
  {
    code >>= 1;
    flushbits(CBPtab1[code].len);

    if (trace)
    {
      printbits(code,8,CBPtab1[code].len);
      printf("): ");
      printbits(CBPtab1[code].val,6,6);
      printf(" (%d)\n",CBPtab1[code].val);
    }

    return CBPtab1[code].val;
  }

  if (code<1)
  {
    if (!quiet)
      fprintf(stderr,"Invalid coded_block_pattern code\n");
    fault = 1;
    return 0;
  }

  flushbits(CBPtab2[code].len);

  if (trace)
  {
    printbits(code,9,CBPtab2[code].len);
    printf("): ");
    printbits(CBPtab2[code].val,6,6);
    printf(" (%d)\n",CBPtab2[code].val);
  }

  return CBPtab2[code].val;
}

int getMBA()
{
  int code, val;

  if (trace)
    printf("macroblock_address_increment (");

  val = 0;

  while ((code = showbits(11))<24)
  {
    if (code!=15) /* if not macroblock_stuffing */
    {
      if (code==8) /* if macroblock_escape */
      {
        if (trace)
          printf("00000001000 ");

        val+= 33;
      }
      else
      {
        if (!quiet)
          fprintf(stderr,"Invalid macroblock_address_increment code\n");

        fault = 1;
        return 1;
      }
    }
    else /* macroblock suffing */
    {
      if (trace)
        printf("00000001111 ");
    }

    flushbits(11);
  }

  if (code>=1024)
  {
    flushbits(1);
    if (trace)
      printf("1): %d\n",val+1);
    return val + 1;
  }

  if (code>=128)
  {
    code >>= 6;
    flushbits(MBAtab1[code].len);

    if (trace)
    {
      printbits(code,5,MBAtab1[code].len);
      printf("): %d\n",val+MBAtab1[code].val);
    }

    return val + MBAtab1[code].val;
  }

  code-= 24;
  flushbits(MBAtab2[code].len);

  if (trace)
  {
    printbits(code+24,11,MBAtab2[code].len);
    printf("): %d\n",val+MBAtab2[code].val);
  }

  return val + MBAtab2[code].val;
}

int getDClum()
{
  int code, size, val;

  if (trace)
    printf("dct_dc_size_luminance: (");

  /* decode length */
  code = showbits(5);

  if (code<31)
  {
    size = DClumtab0[code].val;
    flushbits(DClumtab0[code].len);

    if (trace)
    {
      printbits(code,5,DClumtab0[code].len);
      printf("): %d",size);
    }
  }
  else
  {
    code = showbits(9) - 0x1f0;
    size = DClumtab1[code].val;
    flushbits(DClumtab1[code].len);

    if (trace)
    {
      printbits(code+0x1f0,9,DClumtab1[code].len);
      printf("): %d",size);
    }
  }

  if (trace)
    printf(", dct_dc_differential (");

  if (size==0)
    val = 0;
  else
  {
    val = getbits(size);
    if (trace)
      printbits(val,size,size);
    if ((val & (1<<(size-1)))==0)
      val-= (1<<size) - 1;
  }

  if (trace)
    printf("): %d\n",val);

  return val;
}

int getDCchrom()
{
  int code, size, val;

  if (trace)
    printf("dct_dc_size_chrominance: (");

  /* decode length */
  code = showbits(5);

  if (code<31)
  {
    size = DCchromtab0[code].val;
    flushbits(DCchromtab0[code].len);

    if (trace)
    {
      printbits(code,5,DCchromtab0[code].len);
      printf("): %d",size);
    }
  }
  else
  {
    code = showbits(10) - 0x3e0;
    size = DCchromtab1[code].val;
    flushbits(DCchromtab1[code].len);

    if (trace)
    {
      printbits(code+0x3e0,10,DCchromtab1[code].len);
      printf("): %d",size);
    }
  }

  if (trace)
    printf(", dct_dc_differential (");

  if (size==0)
    val = 0;
  else
  {
    val = getbits(size);
    if (trace)
      printbits(val,size,size);
    if ((val & (1<<(size-1)))==0)
      val-= (1<<size) - 1;
  }

  if (trace)
    printf("): %d\n",val);

  return val;
}
