static int cdg_decode_frame(AVCodecContext *avctx,

                            void *data, int *got_frame, AVPacket *avpkt)

{

    const uint8_t *buf = avpkt->data;

    int buf_size       = avpkt->size;

    int ret;

    uint8_t command, inst;

    uint8_t cdg_data[CDG_DATA_SIZE];

    AVFrame *frame = data;

    CDGraphicsContext *cc = avctx->priv_data;



    if (buf_size < CDG_MINIMUM_PKT_SIZE) {

        av_log(avctx, AV_LOG_ERROR, "buffer too small for decoder\n");

        return AVERROR(EINVAL);

    }

    if (buf_size > CDG_HEADER_SIZE + CDG_DATA_SIZE) {

        av_log(avctx, AV_LOG_ERROR, "buffer too big for decoder\n");

        return AVERROR(EINVAL);

    }



    if ((ret = ff_reget_buffer(avctx, cc->frame)) < 0)

        return ret;

    if (!avctx->frame_number) {

        memset(cc->frame->data[0], 0, cc->frame->linesize[0] * avctx->height);

        memset(cc->frame->data[1], 0, AVPALETTE_SIZE);

    }



    command = bytestream_get_byte(&buf);

    inst    = bytestream_get_byte(&buf);

    inst    &= CDG_MASK;

    buf += 2;  /// skipping 2 unneeded bytes



    if (buf_size > CDG_HEADER_SIZE)

        bytestream_get_buffer(&buf, cdg_data, buf_size - CDG_HEADER_SIZE);



    if ((command & CDG_MASK) == CDG_COMMAND) {

        switch (inst) {

        case CDG_INST_MEMORY_PRESET:

            if (!(cdg_data[1] & 0x0F))

                memset(cc->frame->data[0], cdg_data[0] & 0x0F,

                       cc->frame->linesize[0] * CDG_FULL_HEIGHT);

            break;

        case CDG_INST_LOAD_PAL_LO:

        case CDG_INST_LOAD_PAL_HIGH:

            if (buf_size - CDG_HEADER_SIZE < CDG_DATA_SIZE) {

                av_log(avctx, AV_LOG_ERROR, "buffer too small for loading palette\n");

                return AVERROR(EINVAL);

            }



            cdg_load_palette(cc, cdg_data, inst == CDG_INST_LOAD_PAL_LO);

            break;

        case CDG_INST_BORDER_PRESET:

            cdg_border_preset(cc, cdg_data);

            break;

        case CDG_INST_TILE_BLOCK_XOR:

        case CDG_INST_TILE_BLOCK:

            if (buf_size - CDG_HEADER_SIZE < CDG_DATA_SIZE) {

                av_log(avctx, AV_LOG_ERROR, "buffer too small for drawing tile\n");

                return AVERROR(EINVAL);

            }



            ret = cdg_tile_block(cc, cdg_data, inst == CDG_INST_TILE_BLOCK_XOR);

            if (ret) {

                av_log(avctx, AV_LOG_ERROR, "tile is out of range\n");

                return ret;

            }

            break;

        case CDG_INST_SCROLL_PRESET:

        case CDG_INST_SCROLL_COPY:

            if (buf_size - CDG_HEADER_SIZE < CDG_MINIMUM_SCROLL_SIZE) {

                av_log(avctx, AV_LOG_ERROR, "buffer too small for scrolling\n");

                return AVERROR(EINVAL);

            }



            if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0)

                return ret;



            cdg_scroll(cc, cdg_data, frame, inst == CDG_INST_SCROLL_COPY);

            av_frame_unref(cc->frame);

            ret = av_frame_ref(cc->frame, frame);

            if (ret < 0)

                return ret;

            break;

        default:

            break;

        }



        if (!frame->data[0]) {

            ret = av_frame_ref(frame, cc->frame);

            if (ret < 0)

                return ret;

        }

        *got_frame = 1;

    } else {

        *got_frame = 0;

        buf_size   = 0;

    }



    return buf_size;

}
