static int kempf_decode_tile(G2MContext *c, int tile_x, int tile_y,

                             const uint8_t *src, int src_size)

{

    int width, height;

    int hdr, zsize, npal, tidx = -1, ret;

    int i, j;

    const uint8_t *src_end = src + src_size;

    uint8_t pal[768], transp[3];

    uLongf dlen = (c->tile_width + 1) * c->tile_height;

    int sub_type;

    int nblocks, cblocks, bstride;

    int bits, bitbuf, coded;

    uint8_t *dst = c->framebuf + tile_x * c->tile_width * 3 +

                   tile_y * c->tile_height * c->framebuf_stride;



    if (src_size < 2)

        return AVERROR_INVALIDDATA;



    width  = FFMIN(c->width  - tile_x * c->tile_width,  c->tile_width);

    height = FFMIN(c->height - tile_y * c->tile_height, c->tile_height);



    hdr = *src++;

    sub_type = hdr >> 5;

    if (sub_type == 0) {

        int j;

        memcpy(transp, src, 3);

        src += 3;

        for (j = 0; j < height; j++, dst += c->framebuf_stride)

            for (i = 0; i < width; i++)

                memcpy(dst + i * 3, transp, 3);

        return 0;

    } else if (sub_type == 1) {

        return jpg_decode_data(&c->jc, width, height, src, src_end - src,

                               dst, c->framebuf_stride, NULL, 0, 0, 0);

    }



    if (sub_type != 2) {

        memcpy(transp, src, 3);

        src += 3;

    }

    npal = *src++ + 1;

    memcpy(pal, src, npal * 3); src += npal * 3;

    if (sub_type != 2) {

        for (i = 0; i < npal; i++) {

            if (!memcmp(pal + i * 3, transp, 3)) {

               tidx = i;

               break;

            }

        }

    }



    if (src_end - src < 2)

        return 0;

    zsize = (src[0] << 8) | src[1]; src += 2;



    if (src_end - src < zsize)

        return AVERROR_INVALIDDATA;



    ret = uncompress(c->kempf_buf, &dlen, src, zsize);

    if (ret)

        return AVERROR_INVALIDDATA;

    src += zsize;



    if (sub_type == 2) {

        kempf_restore_buf(c->kempf_buf, dlen, dst, c->framebuf_stride,

                          NULL, 0, width, height, pal, npal, tidx);

        return 0;

    }



    nblocks = *src++ + 1;

    cblocks = 0;

    bstride = FFALIGN(width, 16) >> 4;

    // blocks are coded LSB and we need normal bitreader for JPEG data

    bits = 0;

    for (i = 0; i < (FFALIGN(height, 16) >> 4); i++) {

        for (j = 0; j < (FFALIGN(width, 16) >> 4); j++) {

            if (!bits) {

                bitbuf = *src++;

                bits   = 8;

            }

            coded = bitbuf & 1;

            bits--;

            bitbuf >>= 1;

            cblocks += coded;

            if (cblocks > nblocks)

                return AVERROR_INVALIDDATA;

            c->kempf_flags[j + i * bstride] = coded;

        }

    }



    memset(c->jpeg_tile, 0, c->tile_stride * height);

    jpg_decode_data(&c->jc, width, height, src, src_end - src,

                    c->jpeg_tile, c->tile_stride,

                    c->kempf_flags, bstride, nblocks, 0);



    kempf_restore_buf(c->kempf_buf, dlen, dst, c->framebuf_stride,

                      c->jpeg_tile, c->tile_stride,

                      width, height, pal, npal, tidx);



    return 0;

}
