/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: h263dec.c,v 1.3.34.1 2004/07/09 01:56:22 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include "dllindex.h"
#include "h261defs.h"
#include "vldstate.h"
#include "vldtabs.h"
#include "h261func.h"
#include "vldecode.h"       /* set the proper pragmas for VLDecode          */
#include "h263plus.h"

//#include <stdio.h>
//#include <stdlib.h>
//#define BS_ERR_MSG(a)   a   // Generate messages on bitstream error (i.e., illegal 
                            // bitstream syntax) to simplify debugging
#define BS_ERR_MSG(a)       // Suppress error messages 

//#define CHECKSYM(a)     a     // Check symbol types to verify decoder state tables
#define CHECKSYM(a)             // Don't check symbol types

////////////  Static function declarations  ////////////
//  initPBtabs - Initialize tables for Bquant and mvB/mvF
static void initPBtabs( PICTURE_DESCR * pic );
//  decMvd - Decode "num" motion vectors
static int  decMvd( BS_PTR * bs,    // Bitstream pointer; updated by this routine
                    int * maxBits,  // max bits to decode; updated by this routine
                    int num,        // number of motion vectors to decode
                    S8  x[],        // hor. components decoded by this routine
                    S8  y[]         // vert. components decoded by this routine
                    );
// getMvComp - Compute mv component in H.263 decoder
static int getMvComp( int pred, S8 diff, int unrestrictedMv );
//  dec263blocks - Decode block layer for a macroblock
static int  dec263blocks(   BS_PTR * bs,    // Bitstream pointer; updated by this routine
                            int * maxBits,  // max bits to decode; updated by this routine
                            int cbp,        // Coded Block Pattern
                            int intraFlag,  // 0: INTER block, otherwise INTRA
                            int advancedIntraMode, // 0: off else on
                            BLOCK_DESCR block[6],   // block descriptors generated by this routine
                            SYMBOL sym[],   // array for symbols generated by this routine
                            int maxsym      // size of sym[] array
                            );
//  parseSymbols - Create block descriptor and merge ESC-RUN and ESC-LEVEL symbols
//  Return number of symbols
static int parseSymbols( SYMBOL insym[],        // Input symbols
                        int nsym,               // # input symbols
                        SYMBOL outsym[]         // output symbols
                        );



//  DecPicLayer263 - Decode H.263 Picture Layer information
extern int  DecPicLayer263( BS_PTR * bs, int nbits, PICTURE_DESCR * pic,
                            GOB_DESCR * gob, int * decPtype )
{
    int     gn, tr, ptype, quant, pei, pspare, cpm;
#ifdef DO_H263_PLUS
    int eptype=0;
#endif
    // Decode GN
    if (FLDecSymbol( bs, 5, &nbits, &gn )  !=  OK)  return( OUT_OF_BITS );
    if (gn != 0)  return( H261_ERROR );     // GN=0 for Picture Start Code
    // Decode TR
    if (FLDecSymbol( bs, 8, &nbits, &tr )  !=  OK)  return( OUT_OF_BITS );
    pic->tr = tr;
    // Decode PTYPE
    if (FLDecSymbol( bs, 13, &nbits, &ptype )  !=  OK)  return( OUT_OF_BITS );

#ifdef DO_H263_PLUS
    if ((ptype & 0xe0) == PTYPE263_EPTYPE) {
        if(FLDecSymbol( bs, 14, &nbits, &eptype )  !=  OK)  return( OUT_OF_BITS );
        // the picture type is in bits 1-3 of EPTYPE, put those bits into bits 6-8 of PTYPE
        ptype &= 0xFFFFFF1F; // clear bits 6-8
        ptype |= (0x3800 & eptype) >> 6; 
    }
#endif

    pic->ptype = ptype;
    *decPtype = ptype;
    pic->splitscreen = ptype & PTYPE263_SPLITSCREEN;
    pic->doccamera = ptype & PTYPE263_DOCCAMERA;
    pic->fp_release = ptype & PTYPE263_FP_RELEASE;
    switch (ptype & 0xe0)
    {
    case PTYPE263_SQCIF:
        pic->format = SQCIF;
        break;
    case PTYPE263_QCIF:
        pic->format = QCIF;
        break;
    case PTYPE263_CIF:
        pic->format = CIF;
        break;
    case PTYPE263_4CIF:
        pic->format = CIF4;
        break;
    case PTYPE263_16CIF:
        pic->format = CIF16;
        break;
    case PTYPE263_RESERVED:
        pic->format = ANYSIZE;
        break;
    default:
        return( H261_ERROR );
        break;
    }
    if ((ptype & 0x800) != 0  || (ptype & 0x1000) == 0) return( H261_ERROR );
    pic->interFrame = ptype & PTYPE263_INTER;
    pic->unrestrictedMv = ptype & PTYPE263_UNRESTRICTED_MV;
    pic->advancedPred = ptype & PTYPE263_ADVANCED_PRED;
    pic->syntax_basedAC = ptype & PTYPE263_SYNTAX_BASED_AC;
    if (pic->syntax_basedAC) {
        BS_ERR_MSG( H261ErrMsg("Syntax-based AC not yet supported"); )
        return( H261_ERROR );
    }
    pic->PBframeMode = (ptype & PTYPE263_PB_FRAME) != 0;

#ifdef DO_H263_PLUS
    // Do PB frame - either orignal or improved mode
    pic->PBframeMode = (ptype & PTYPE263_PB_FRAME) ? 1 : 0;
    if(pic->PBframeMode) {
        if( eptype & EPTYPE263PLUS_IMPROVED_PBFRAME_MODE) {
            pic->PBframeMode = H263PLUS_IMPROVED_PBFRAME_MODE;
        }
    } else {
        if( eptype & EPTYPE263PLUS_IMPROVED_PBFRAME_MODE) {
            return ( H261_ERROR );
        }
    }
    // Are deblocking filters on?
    pic->deblockingFilterMode = eptype & EPTYPE263PLUS_DEBLOCKING_FILTER_MODE;
    // Use advanced intra mode?
    pic->advancedIntraMode = eptype & EPTYPE263PLUS_ADVANCED_INTRA_MODE;
    // Using Reduced-resolution Update mode?
    pic->reducedResUpdate = eptype & EPTYPE263PLUS_REDUCED_RES_UPDATE;
    //if (pic->reducedResUpdate && (!pic->interFrame || pic->PBframeMode)) return( H261_ERROR );
#else
    pic->reducedResUpdate = 0;
#endif

    if(pic->interFrame == 0 && pic->PBframeMode != 0) return( H261_ERROR );
    // Decode PQUANT
    if (FLDecSymbol( bs, 5, &nbits, &quant )  !=  OK)  return( OUT_OF_BITS );
    if (quant < QUANT_MIN  ||  quant > QUANT_MAX)  return( H261_ERROR );
    gob->gquant = quant;
     // Decode CPM (continuous presence multipoint). For now, flag error if not zero.
    if (FLDecSymbol( bs, 1, &nbits, &cpm )  !=  OK)  return( OUT_OF_BITS );
    // if (cpm != 0)  return( H261_ERROR );
    pic->cpm = cpm;     // Save (we may need it?)
    if (pic->PBframeMode) {
        // Decode TRB
        if (FLDecSymbol( bs, 3, &nbits, &tr )  !=  OK)  return( OUT_OF_BITS );
        pic->tempRefBframe = tr;
        // Decode DBQUANT
        if (FLDecSymbol( bs, 2, &nbits, &quant )  !=  OK)  return( OUT_OF_BITS );
        pic->dbQuant = quant;
        initPBtabs( pic );  // Generate lookup tables for Bquant and mvB/mvF
    }
    pic->trPrev = pic->tr;  // Hold on to TR for decoding of next picture
    // Decode PEI
    if (FLDecSymbol( bs, 1, &nbits, &pei )  !=  OK)  return( OUT_OF_BITS );
    pic->peiCount = 0;
    // Decode PSPARE
    while (pei) {   // Loop until PEI=0
        if (pic->peiCount < MAX_PEI_COUNT) {
            FLDecSymbol( bs, 8, &nbits, &pspare );
            pic->pSpare[pic->peiCount] = pspare;
        } else {
            ++bs->byteptr;  // Drop PSPARE on the floor
        }
        ++pic->peiCount;
        if (FLDecSymbol( bs, 1, &nbits, &pei )  !=  OK)  return( OUT_OF_BITS );
    }
    // Indicate that GOB header has no "spares"
    gob->gei = 0;
    gob->num_gspare = 0;
    return( OK );
}


//  InitMvTabs - Initialize tables for mvB/mvF
extern void InitMvTabs( int trD, int trB,
                        int tabMvF[], int tabMvB[]  // [UMV_MIN:UMV_MAX]
                        )
{
    int i;

    // Generate tables for B-picture motion vectors
    trD &= H263_TR_MASK;
    if (trD == 0)  trD = 2; // Avoid divide by 0
    if (trB >= trD)  trB = 0;   // Ensure trB < trD
    for (i = UMV_MIN; i <= UMV_MAX; ++i) {
        tabMvF[i] = (trB * i) / trD;          // Mv using prev. picture
        tabMvB[i] = ((trB - trD) * i) / trD;  // Mv using new P-picture
    }
}


// Tables for B-picture: Bquant and mvF/mvB
static U8   tabBquant[QUANT_MAX-QUANT_MIN+1];
static int  decMvF[UMV_MAX-UMV_MIN+1],  // Mv using prev. picture
            decMvB[UMV_MAX-UMV_MIN+1];  // Mv using new P-picture

//  initPBtabs - Initialize tables for Bquant and mvB/mvF
static void initPBtabs( PICTURE_DESCR * pic )
{
    int i, n;

    // Generate tables for B-picture motion vectors
    InitMvTabs( (pic->tr - pic->trPrev) & H263_TR_MASK, 
                pic->tempRefBframe,
                &decMvF[-UMV_MIN], &decMvB[-UMV_MIN] );
    // Generate table for Bquant
    n = pic->dbQuant + 5;
    for (i = QUANT_MIN; i <= QUANT_MAX; ++i) {
        tabBquant[i - QUANT_MIN] = min( (n*i) >> 2, QUANT_MAX );
    }
}


//  DecGobLayer263 - Decode H.263 GOB Layer information
extern int  DecGobLayer263( BS_PTR * bs, int nbits, GOB_DESCR * gob, int * gfid )
{
    int     gn, quant;

    // Decode GN
    if (FLDecSymbol( bs, 5, &nbits, &gn )  !=  OK)  return( OUT_OF_BITS );
    // Decode GFID
    if (FLDecSymbol( bs, 2, &nbits, gfid )  !=  OK)  return( OUT_OF_BITS );
    // Decode GQUANT
    if (FLDecSymbol( bs, 5, &nbits, &quant )  !=  OK)  return( OUT_OF_BITS );
    if (quant < QUANT_MIN  ||  quant > QUANT_MAX)  return( H261_ERROR );
    gob->gquant = quant;
    // Indicate that GOB header has no "spares"
    gob->gei = 0;
    gob->num_gspare = 0;
    return( OK );
}

//#define DB_DUMP_BLOCK_SYMBOLS
#ifdef DB_DUMP_BLOCK_SYMBOLS
FILE * pDecFile=0;
int bDecOpen=0;
int bDecClose=0;
#endif

//  DecMbLayer263 - Decode macroblock layer and block layer for a Group Of Blocks
//  This routine is very picky when determining whether bitstream is valid.
//  It returns OK only if a startcode ends the bitstream; otherwise, it
//  returns H261_ERROR.
extern int  DecMbLayer263(  BS_PTR * bs,    // Bitstream pointer
                            int nbits,      // Bits to decode (incl. trailing startcode)
                            GOB_DESCR * gob,        // GOB descriptor
                            MACROBLOCK_DESCR mb[],  // Packed array of "gob->num_mb" MB descr.
                            int interFrame, // 0: ptype=INTRA, otherwise ptype=INTER
                            int PBframe,    // 0: not PB frame, otherwise PB frame
                            int unrestrictedMv, // 0: -16/+15.5 motion, otherwise +/- 31.5
                            int advancedIntraMode, // 0: off else on
                            SYMBOL sym[],   // symbol array
                            int maxsym      // size of symbol array
                            )
{
    int     quant, isym, mcbpcTab, i, j, status, cbpyTab, intraFlag, horPredOnly, mvX, mvY;
    SYMBOL  s;
    int     mcbpc, modB, cbpB;
    BS_ERR_MSG ( char msg[120] ); /* Flawfinder: ignore */
    
    isym = 0;
    quant = gob->gquant;
    if (interFrame) {
        mcbpcTab = TAB263_MCBPC_INTER;
    } else {
        mcbpcTab = TAB263_MCBPC_INTRA;
    }
    for (i = 0; i < gob->num_mb; ++i) {
        // Decode MCBPC
        do {
            status = VLDecSymbol( bs, mcbpcTab, &nbits, &s );
			if (status != OK)  
                if (s.type != SYM_STARTCODE) {
					status = BITSTREAM_ERROR;
					goto error_exit;
				} else {
					return(OK);
				}
        } while( s.type == SYM_MCBPC_STUFFING );
        mcbpc = s.value;
        mb[i].mtype = mcbpc & 0xfc; // Mask off cbpC (two LSBs)

#ifdef DO_H263_PLUS
        if(advancedIntraMode && 
            (mb[i].mtype == MTYPE263_INTRA || mb[i].mtype == MTYPE263_INTRA_Q)) {
            // Decode INTRA_MODE
            status = VLDecSymbol( bs, TAB263PLUS_INTRA_MODE, &nbits, &s );
			if (status != OK)  goto error_exit;
            mb[i].intra_mode = s.value;
        } else {
            mb[i].intra_mode = ADV_INTRA_PRED_NONE;
        }
#endif
        //printf("DecMbLayer:  x = %d   y = %d   type = %d\n", mb[i].x, mb[i].y, mb[i].mtype );
        if (mb[i].mtype == MTYPE_SKIP) {
            mb[i].mv_x = 0;
            mb[i].mv_y = 0;
        } else {
            if (PBframe) {
                // Decode MODB
#ifdef DO_H263_PLUS
				if(PBframe==H263PLUS_IMPROVED_PBFRAME_MODE) {
					status = VLDecSymbol( bs, TAB263PLUS_MODB, &nbits, &s );
				} else {
					status = VLDecSymbol( bs, TAB263_MODB, &nbits, &s );
				}
#else
				status = VLDecSymbol( bs, TAB263_MODB, &nbits, &s );
#endif
				if (status != OK)  
					goto error_exit;
                modB = s.value;
                mb[i].modB = modB;
                if (BFRAME_HAS_CBP(&mb[i])) {    // Decode cbpB
                    status = FLDecSymbol( bs, 6, &nbits, &cbpB );
                    if (status != OK)  goto error_exit;
                    mb[i].cbpB = cbpB;
                } else {
                    mb[i].cbpB = 0;
                }
            } else {
                mb[i].modB = modB = 0;
            }
            // Decode cbpY
            if (mb[i].mtype == MTYPE263_INTRA  ||  mb[i].mtype == MTYPE263_INTRA_Q) {
                cbpyTab = TAB263_CBPY_INTRA;
            } else {
                cbpyTab = TAB263_CBPY;
            }
            status = VLDecSymbol( bs, cbpyTab, &nbits, &s );
            if (status != OK)  goto error_exit;
            mb[i].cbp = (s.value << 2) | (mcbpc & 0x3);
            // Decode DQUANT
            if (mb[i].mtype == MTYPE263_INTER_Q  ||  mb[i].mtype == MTYPE263_INTRA_Q) {
                status = VLDecSymbol( bs, TAB263_DQUANT, &nbits, &s );
                if (status != OK)  goto error_exit;
                mb[i].dquant = s.value;
                quant += mb[i].dquant;
                if (quant < QUANT_MIN)
                    quant = QUANT_MIN;
                if (quant > QUANT_MAX)
                    quant = QUANT_MAX;
            }
            mb[i].quant = quant;
            // Decode motion vectors and block layer
            if (i < gob->mb_width) {
                horPredOnly = YES;  // First row of macroblocks; don't use prev. row
            } else {
                horPredOnly = NO;
            }
            switch( mb[i].mtype ) {
                
            case MTYPE263_INTRA:
            case MTYPE263_INTRA_Q:
                if (PBframe) {  // Decode MVD
                    status = decMvd( bs, &nbits, 1, &mb[i].mvd_x, &mb[i].mvd_y );
                    if (status != OK)  goto error_exit;
                    MvPred( &mb[i], WHOLE_MACROBLOCK, gob->mb_offset, horPredOnly, &mvX, &mvY );
                    mb[i].mv_x = getMvComp( mvX, mb[i].mvd_x, unrestrictedMv );
                    mb[i].mv_y = getMvComp( mvY, mb[i].mvd_y, unrestrictedMv );
                    if (BFRAME_HAS_MOTION_VECTOR(&mb[i])) {  // Decode MVDB
                        status = decMvd( bs, &nbits, 1, &mb[i].mvdB_x, &mb[i].mvdB_y );
                        if (status != OK)  goto error_exit;
                    }
                } else {
                    mb[i].mv_x = 0;
                    mb[i].mv_y = 0;
                }

                intraFlag = YES;
#ifdef DO_H263_PLUS
                status = dec263blocks( bs, &nbits, mb[i].cbp, intraFlag, advancedIntraMode,
                                        mb[i].block, &sym[isym], maxsym - isym );
#else
                status = dec263blocks( bs, &nbits, mb[i].cbp, intraFlag, 0,
                                        mb[i].block, &sym[isym], maxsym - isym );
#endif


#ifdef DB_DUMP_BLOCK_SYMBOLS
                if(bDecClose) {
                    fclose(pDecFile);
                    pDecFile = NULL;
                }
                if(bDecOpen) {
                    pDecFile = fopen("C:\\temp\\dec_out.dmp", "wt");
                }

                if(pDecFile) {
                    int b,s;
                    fprintf(pDecFile, "\n \n Macroblock %d \n \n", i);
                    for(b=0; b<6; b++) {
                        fprintf(pDecFile, "\n Block %d \n", b);
                        for(s=0; s<mb[i].block[b].nsym; s++) {
                            fprintf(pDecFile, "lev = %d  run = %d\n", mb[i].block[b].sym[s].value, mb[i].block[b].sym[s].type);
                        }
                    }
                }
#endif

                if (status == H261_ERROR) {
                    status = BITSTREAM_ERROR;
                    goto error_exit;
                }
                isym += status;
                break;
                
            case MTYPE263_INTER:
            case MTYPE263_INTER_Q:
                // Decode MVD
                status = decMvd( bs, &nbits, 1, &mb[i].mvd_x, &mb[i].mvd_y );
                if (status != OK)  goto error_exit;
                MvPred( &mb[i], WHOLE_MACROBLOCK, gob->mb_offset, horPredOnly, &mvX, &mvY );
                mb[i].mv_x = getMvComp( mvX, mb[i].mvd_x, unrestrictedMv );
                mb[i].mv_y = getMvComp( mvY, mb[i].mvd_y, unrestrictedMv );
                if (BFRAME_HAS_MOTION_VECTOR(&mb[i])) {  // Decode MVDB
                    status = decMvd( bs, &nbits, 1, &mb[i].mvdB_x, &mb[i].mvdB_y );
                    if (status != OK)  goto error_exit;
                }
                intraFlag = 0;
                status = dec263blocks( bs, &nbits, mb[i].cbp, intraFlag, 0,
                                        mb[i].block, &sym[isym], maxsym - isym );
                if (status == H261_ERROR) {
                    status = BITSTREAM_ERROR;
                    goto error_exit;
                }
                isym += status;
                break;
                
            case MTYPE263_INTER4V:
                // Decode MVD1-4
                status = decMvd( bs, &nbits, 4, mb[i].blkDiffX, mb[i].blkDiffY );
                if (status != OK)  goto error_exit;
                for (j = 0; j < 4; ++j) {
                    MvPred( &mb[i], j, gob->mb_offset, horPredOnly, &mvX, &mvY );
                    mb[i].blkMvX[j] = getMvComp( mvX, mb[i].blkDiffX[j], unrestrictedMv );
                    mb[i].blkMvY[j] = getMvComp( mvY, mb[i].blkDiffY[j], unrestrictedMv );
                }
                if (BFRAME_HAS_MOTION_VECTOR(&mb[i])) {  // Decode MVDB
                    status = decMvd( bs, &nbits, 1, &mb[i].mvdB_x, &mb[i].mvdB_y );
                    if (status != OK)  goto error_exit;
                }
                intraFlag = 0;
                status = dec263blocks( bs, &nbits, mb[i].cbp, intraFlag, 0,
                                        mb[i].block, &sym[isym], maxsym - isym );
                if (status == H261_ERROR) {
                    status = BITSTREAM_ERROR;
                    goto error_exit;
                }
                isym += status;
                break;
                
            default:
                status = UNKNOWN_MTYPE;
                goto error_exit;
                break;
            }
            
            if (PBframe) {
                // Reconstruct forward and backward mv for B-frame
                GetMvBframe( &mb[i], unrestrictedMv, &decMvF[-UMV_MIN], &decMvB[-UMV_MIN] );
                mb[i].Bquant = tabBquant[quant - QUANT_MIN];
                if (BFRAME_HAS_CBP(&mb[i])) {    // Decode B blocks
                    intraFlag = 0;
                    status = dec263blocks( bs, &nbits, mb[i].cbpB, intraFlag, 0,
                                            mb[i].Bblock, &sym[isym], maxsym - isym );
                    if (status == H261_ERROR) {
                        status = BITSTREAM_ERROR;
                        goto error_exit;
                    }
                    isym += status;
                }
            }
        }
    }
    //printf("DecMbLayer:  Remove stuffing, then find startcode; nbits = %d\n", nbits);
    
	do {
        status = VLDecSymbol( bs, mcbpcTab, &nbits, &s );
        if (status != OK)  
			return(OK);
			// goto error_exit;
			// sometimes the end of stream is not that clean.. -gneel
    } while( s.type == SYM_MCBPC_STUFFING);
//    if (s.type != SYM_STARTCODE  ||  nbits != 0) {
//        status = BITSTREAM_ERROR;
//        goto error_exit;
//    }
// After all the mbs are over there may not be a start code.
	
    return( OK );

error_exit:
    BS_ERR_MSG( sprintf( msg, "DecMbLayer263: error %d occurred in block #%d", status, i); /* Flawfinder: ignore */
                                H261ErrMsg( msg ); )
    return( H261_ERROR );
}


//  decMvd - Decode "num" motion vectors
static int  decMvd( BS_PTR * bs,    // Bitstream pointer; updated by this routine
                    int * maxBits,  // max bits to decode; updated by this routine
                    int num,        // number of motion vectors to decode
                    S8  x[],        // hor. components decoded by this routine
                    S8  y[]         // vert. components decoded by this routine
                    )
{
    int     i, status;
    SYMBOL  s;
    
    for (i = 0; i < num; ++i) {
        // Decode horizontal component
        status = VLDecSymbol( bs, TAB263_MVD, maxBits, &s );
        if (status != OK)  return( status );
        x[i] = s.value;
        // Decode vertical component
        status = VLDecSymbol( bs, TAB263_MVD, maxBits, &s );
        if (status != OK)  return( status );
        y[i] = s.value;
    }
    return( OK );
}


// getMvComp - Compute mv component in H.263 decoder
static int getMvComp( int pred, S8 diff, int unrestrictedMv )
{
    int output;
    
    output = pred + diff;
    if (unrestrictedMv == 0) {
        while (output < MVD263_MIN) {
            output += MV263_WRAP;
        }
        while (output > MVD263_MAX) {
            output -= MV263_WRAP;
        }
    } else if (pred < UMV_NEG_THRESH) {
        // UMV pred < -15.5: -31.5 <= output <= 0
        if (output < UMV_MIN) {
            output += MV263_WRAP;   // Need only check negative limit, since diff <= 15.5
        }
    } else if (pred > UMV_POS_THRESH  &&  output > UMV_MAX) {
        // UMV pred > 16: 0 <= output <= 31.5
        output -= MV263_WRAP;       // Need only check positive limit, since diff >= -16
    }   // else: Don't need to check for -15.5 <= pred <= 16
    return( output );
}


//  GetMvBframe - Reconstruct forward and backward motion vectors for B-frame
extern void GetMvBframe( MACROBLOCK_DESCR *mb, int unrestrictedMv,
                         int tabMvF[], int tabMvB[]  // [UMV_MIN:UMV_MAX]
                         )
{
    int j;

    if (mb->mtype == MTYPE263_INTER4V) {
        for (j = 0; j < 4; ++j) {
            // Forward prediction (using prev. frame)
            mb->blkMvFx[j] = tabMvF[ mb->blkMvX[j] ];
            mb->blkMvFy[j] = tabMvF[ mb->blkMvY[j] ];
            if (BFRAME_HAS_MOTION_VECTOR(mb)) {
                mb->blkMvFx[j] = getMvComp( mb->blkMvFx[j], mb->mvdB_x, unrestrictedMv );
                mb->blkMvFy[j] = getMvComp( mb->blkMvFy[j], mb->mvdB_y, unrestrictedMv );
                // Backward prediction (using next P-frame)
                mb->blkMvBx[j] = mb->blkMvFx[j] - mb->blkMvX[j];
                mb->blkMvBy[j] = mb->blkMvFy[j] - mb->blkMvY[j];
            } else {
                mb->blkMvBx[j] = tabMvB[ mb->blkMvX[j] ];
                mb->blkMvBy[j] = tabMvB[ mb->blkMvY[j] ];
            }
        }
    } else {    // Same motion vector for the whole macroblock
        // Forward prediction (using prev. frame)
        mb->blkMvFx[0] = tabMvF[ mb->mv_x ];
        mb->blkMvFy[0] = tabMvF[ mb->mv_y ];
        if (BFRAME_HAS_MOTION_VECTOR(mb)) {
            mb->blkMvFx[0] = getMvComp( mb->blkMvFx[0], mb->mvdB_x, unrestrictedMv );
            mb->blkMvFy[0] = getMvComp( mb->blkMvFy[0], mb->mvdB_y, unrestrictedMv );
            // Backward prediction (using next P-frame)
            mb->blkMvBx[0] = mb->blkMvFx[0] - mb->mv_x;
            mb->blkMvBy[0] = mb->blkMvFy[0] - mb->mv_y;
        } else {
            mb->blkMvBx[0] = tabMvB[ mb->mv_x ];
            mb->blkMvBy[0] = tabMvB[ mb->mv_y ];
        }
    }
}


//  dec263blocks - Decode block layer for a macroblock
static int  dec263blocks(   BS_PTR * bs,    // Bitstream pointer; updated by this routine
                            int * maxBits,  // max bits to decode; updated by this routine
                            int cbp,        // Coded Block Pattern
                            int intraFlag,  // 0: INTER block, otherwise INTRA
                            int advancedIntraMode, // 0: off else on
                            BLOCK_DESCR block[6],   // block descriptors generated by this routine
                            SYMBOL sym[],   // array for symbols generated by this routine
                            int maxsym      // size of sym[] array
                            )
{
    int startState, isym, mask, blk, state, status, parsed_bits, nsym;
    
    if (intraFlag) {
#ifdef DO_H263_PLUS
        if(advancedIntraMode) {
            startState = ST263PLUS_BLK_6;
        } else {
            startState = ST263_INTRA_DC_AC;
        }
#else
        startState = ST263_INTRA_DC_AC; // Decode DC and AC coeffs
#endif
    } else {
        startState = ST263_BLK_6;       // Decode one INTER block
    }
    isym = 0;
    mask = 0x20;    // Bit 5 indicates whether first block is coded
    for (blk = 0; blk < 6; ++blk) {
        if (cbp & mask) {
            state = startState;
            status = VLDECODE( *bs, *maxBits, &state, &parsed_bits, 
                                &nsym, &sym[isym], maxsym - isym );
            if (status != FINISHED_LAST_BLOCK)  return( H261_ERROR );
            IncBsPtr( bs, parsed_bits );
            *maxBits -= parsed_bits;
            status = parseSymbols( &sym[isym], nsym, &sym[isym] );
            if (status == H261_ERROR)  return( H261_ERROR );
            block[blk].sym = &sym[isym];
            block[blk].nsym = status;
            isym += status;
        } else if (intraFlag && !advancedIntraMode) { // Decode INTRA-DC
            if (VLDecSymbol( bs, TAB263_INTRA_DC, maxBits, &sym[isym] )  !=  OK) {
                return( H261_ERROR );
            }
            block[blk].sym = &sym[isym];
            block[blk].nsym = 1;
            ++isym;
        } else {    // No coeffs
            block[blk].nsym = 0;
        }
        mask >>= 1; // Check next block
    }
    return( isym );
}


//  parseSymbols - Merge ESC-RUN and ESC-LEVEL symbols
//  Return number of symbols
static int parseSymbols( SYMBOL insym[],        // Input symbols
                        int nsym,               // # input symbols
                        SYMBOL outsym[]         // output symbols
                        )
{
    int iIn, iOut, zzpos, last, run;
    BS_ERR_MSG ( char msg[120] ); /* Flawfinder: ignore */
    
    iIn = 0, iOut = 0, zzpos = 0;
    while (iIn < nsym) {
        if (insym[iIn].type == SYM_ESC_RUN) {   // Encountered ESCAPE-LAST-RUN-LEVEL symbol
            last = insym[iIn].value & 0x40;     // LAST is found in bit 6
            run = insym[iIn++].value & 0x3f;    // RUN in bits 0-5
            outsym[iOut].type = run;
            CHECKSYM( if (checksym( insym[iIn], SYM_ESC_LEVEL, "parseSymbols") != OK) return(H261_ERROR);)
            outsym[iOut++].value = insym[iIn++].value;
        } else {
            last = insym[iIn].type & 0x40;      // LAST is found in bit 6
            run = insym[iIn].type & 0x3f;     // RUN in bits 0-5
            outsym[iOut].type = run;
            outsym[iOut++].value = insym[iIn++].value;
        }
        zzpos += run + 1;
        //printf("parseSymbols: "); printsym(outsym[iOut-1 ); printf("   zzpos = %d\n", zzpos);
        if (zzpos > 64) {   // If we decoded coeff. 63, we will now have zzpos=64
            BS_ERR_MSG( sprintf( msg, "parseSymbols: Bitstream error, zzpos=%d", zzpos); /* Flawfinder: ignore */
            H261ErrMsg( msg ); )
            return (H261_ERROR);
        }
        if (last != 0  &&  iIn < nsym) {
            H261ErrMsg( "parseSymbols: Encountered LAST=1 prematurely" );
            return (H261_ERROR);
        }
    }
    /*{
        int i;
        static int iprint = YES;
        if (iprint) {
            printf("parseSymbols: Found %d symbols\n", iOut);
            for (i = 0; i < iOut; ++i) {
                printf("Symbol %d:  ", i);
                printsym( outsym[i] );
                printf("\n");
            }
            printf("parseSymbols: Continue printouts? [0/1]: ");
            scanf("%d", &iprint);
        }
    }*/
    if (last != LAST263_RUNVAL  ||  iIn != nsym) {
        H261ErrMsg( "parseSymbols: Did not find LAST=1" );
        return (H261_ERROR);
    }
    return (iOut);
}
