static int bfi_decode_frame(AVCodecContext * avctx, void *data,

                            int *data_size, AVPacket *avpkt)

{

    const uint8_t *buf = avpkt->data;

    int buf_size = avpkt->size;

    BFIContext *bfi = avctx->priv_data;

    uint8_t *dst = bfi->dst;

    uint8_t *src, *dst_offset, colour1, colour2;

    uint8_t *frame_end = bfi->dst + avctx->width * avctx->height;

    uint32_t *pal;

    int i, j, height = avctx->height;



    if (bfi->frame.data[0])

        avctx->release_buffer(avctx, &bfi->frame);



    bfi->frame.reference = 1;



    if (avctx->get_buffer(avctx, &bfi->frame) < 0) {

        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");

        return -1;

    }



    /* Set frame parameters and palette, if necessary */

    if (!avctx->frame_number) {

        bfi->frame.pict_type = FF_I_TYPE;

        bfi->frame.key_frame = 1;

        /* Setting the palette */

        if(avctx->extradata_size>768) {

            av_log(NULL, AV_LOG_ERROR, "Palette is too large.\n");

            return -1;

        }

        pal = (uint32_t *) bfi->frame.data[1];

        for (i = 0; i < avctx->extradata_size / 3; i++) {

            int shift = 16;

            *pal = 0;

            for (j = 0; j < 3; j++, shift -= 8)

                *pal +=

                    ((avctx->extradata[i * 3 + j] << 2) |

                    (avctx->extradata[i * 3 + j] >> 4)) << shift;

            pal++;

        }

        bfi->frame.palette_has_changed = 1;

    } else {

        bfi->frame.pict_type = FF_P_TYPE;

        bfi->frame.key_frame = 0;

    }



    buf += 4; //Unpacked size, not required.



    while (dst != frame_end) {

        static const uint8_t lentab[4]={0,2,0,1};

        unsigned int byte = *buf++, av_uninit(offset);

        unsigned int code = byte >> 6;

        unsigned int length = byte & ~0xC0;



        /* Get length and offset(if required) */

        if (length == 0) {

            if (code == 1) {

                length = bytestream_get_byte(&buf);

                offset = bytestream_get_le16(&buf);

            } else {

                length = bytestream_get_le16(&buf);

                if (code == 2 && length == 0)

                    break;

            }

        } else {

            if (code == 1)

                offset = bytestream_get_byte(&buf);

        }



        /* Do boundary check */

        if (dst + (length<<lentab[code]) > frame_end)

            break;



        switch (code) {



        case 0:                //Normal Chain

            bytestream_get_buffer(&buf, dst, length);

            dst += length;

            break;



        case 1:                //Back Chain

            dst_offset = dst - offset;

            length *= 4;        //Convert dwords to bytes.

            if (dst_offset < bfi->dst)

                break;

            while (length--)

                *dst++ = *dst_offset++;

            break;



        case 2:                //Skip Chain

            dst += length;

            break;



        case 3:                //Fill Chain

            colour1 = bytestream_get_byte(&buf);

            colour2 = bytestream_get_byte(&buf);

            while (length--) {

                *dst++ = colour1;

                *dst++ = colour2;

            }

            break;



        }

    }



    src = bfi->dst;

    dst = bfi->frame.data[0];

    while (height--) {

        memcpy(dst, src, avctx->width);

        src += avctx->width;

        dst += bfi->frame.linesize[0];

    }

    *data_size = sizeof(AVFrame);

    *(AVFrame *) data = bfi->frame;

    return buf_size;

}
