/* * Resolume DXV decoder * Copyright (C) 2015 Vittorio Giovara * Copyright (C) 2018 Paul B Mahol * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "libavutil/imgutils.h" #include "mathops.h" #include "avcodec.h" #include "bytestream.h" #include "codec_internal.h" #include "lzf.h" #include "texturedsp.h" #include "thread.h" typedef struct DXVContext { TextureDSPContext texdsp; GetByteContext gbc; uint8_t *tex_data; // Compressed texture uint8_t *ctex_data; // Compressed texture int tex_rat; // Compression ratio int tex_step; // Distance between blocks int ctex_step; // Distance between blocks int64_t tex_size; // Texture size int64_t ctex_size; // Texture size /* Optimal number of slices for parallel decoding */ int slice_count; uint8_t *op_data[4]; // Opcodes int64_t op_size[4]; // Opcodes size int texture_block_w; int texture_block_h; int ctexture_block_w; int ctexture_block_h; /* Pointer to the selected decompression function */ int (*tex_funct)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block); int (*tex_funct_planar[2])(uint8_t *plane0, ptrdiff_t stride0, uint8_t *plane1, ptrdiff_t stride1, const uint8_t *block); } DXVContext; static void decompress_indices(uint8_t *dst, const uint8_t *src) { int block, i; for (block = 0; block < 2; block++) { int tmp = AV_RL24(src); /* Unpack 8x3 bit from last 3 byte block */ for (i = 0; i < 8; i++) dst[i] = (tmp >> (i * 3)) & 0x7; src += 3; dst += 8; } } static int extract_component(int yo0, int yo1, int code) { int yo; if (yo0 == yo1) { yo = yo0; } else if (code == 0) { yo = yo0; } else if (code == 1) { yo = yo1; } else { if (yo0 > yo1) { yo = (uint8_t) (((8 - code) * yo0 + (code - 1) * yo1) / 7); } else { if (code == 6) { yo = 0; } else if (code == 7) { yo = 255; } else { yo = (uint8_t) (((6 - code) * yo0 + (code - 1) * yo1) / 5); } } } return yo; } static int cocg_block(uint8_t *plane0, ptrdiff_t stride0, uint8_t *plane1, ptrdiff_t stride1, const uint8_t *block) { uint8_t co_indices[16]; uint8_t cg_indices[16]; uint8_t co0 = *(block); uint8_t co1 = *(block + 1); uint8_t cg0 = *(block + 8); uint8_t cg1 = *(block + 9); int x, y; decompress_indices(co_indices, block + 2); decompress_indices(cg_indices, block + 10); for (y = 0; y < 4; y++) { for (x = 0; x < 4; x++) { int co_code = co_indices[x + y * 4]; int cg_code = cg_indices[x + y * 4]; plane0[x] = extract_component(cg0, cg1, cg_code); plane1[x] = extract_component(co0, co1, co_code); } plane0 += stride0; plane1 += stride1; } return 16; } static void yao_subblock(uint8_t *dst, uint8_t *yo_indices, ptrdiff_t stride, const uint8_t *block) { uint8_t yo0 = *(block); uint8_t yo1 = *(block + 1); int x, y; decompress_indices(yo_indices, block + 2); for (y = 0; y < 4; y++) { for (x = 0; x < 4; x++) { int yo_code = yo_indices[x + y * 4]; dst[x] = extract_component(yo0, yo1, yo_code); } dst += stride; } } static int yo_block(uint8_t *dst, ptrdiff_t stride, uint8_t *unused0, ptrdiff_t unused1, const uint8_t *block) { uint8_t yo_indices[16]; yao_subblock(dst, yo_indices, stride, block); yao_subblock(dst + 4, yo_indices, stride, block + 8); yao_subblock(dst + 8, yo_indices, stride, block + 16); yao_subblock(dst + 12, yo_indices, stride, block + 24); return 32; } static int yao_block(uint8_t *plane0, ptrdiff_t stride0, uint8_t *plane3, ptrdiff_t stride1, const uint8_t *block) { uint8_t yo_indices[16]; uint8_t a_indices[16]; yao_subblock(plane0, yo_indices, stride0, block); yao_subblock(plane3, a_indices, stride1, block + 8); yao_subblock(plane0 + 4, yo_indices, stride0, block + 16); yao_subblock(plane3 + 4, a_indices, stride1, block + 24); yao_subblock(plane0 + 8, yo_indices, stride0, block + 32); yao_subblock(plane3 + 8, a_indices, stride1, block + 40); yao_subblock(plane0 + 12, yo_indices, stride0, block + 48); yao_subblock(plane3 + 12, a_indices, stride1, block + 56); return 64; } static int decompress_texture_thread(AVCodecContext *avctx, void *arg, int slice, int thread_nb) { DXVContext *ctx = avctx->priv_data; AVFrame *frame = arg; const uint8_t *d = ctx->tex_data; int w_block = avctx->coded_width / ctx->texture_block_w; int h_block = avctx->coded_height / ctx->texture_block_h; int x, y; int start_slice, end_slice; start_slice = h_block * slice / ctx->slice_count; end_slice = h_block * (slice + 1) / ctx->slice_count; if (ctx->tex_funct) { for (y = start_slice; y < end_slice; y++) { uint8_t *p = frame->data[0] + y * frame->linesize[0] * ctx->texture_block_h; int off = y * w_block; for (x = 0; x < w_block; x++) { ctx->tex_funct(p + x * 4 * ctx->texture_block_w, frame->linesize[0], d + (off + x) * ctx->tex_step); } } } else { const uint8_t *c = ctx->ctex_data; for (y = start_slice; y < end_slice; y++) { uint8_t *p0 = frame->data[0] + y * frame->linesize[0] * ctx->texture_block_h; uint8_t *p3 = ctx->tex_step != 64 ? NULL : frame->data[3] + y * frame->linesize[3] * ctx->texture_block_h; int off = y * w_block; for (x = 0; x < w_block; x++) { ctx->tex_funct_planar[0](p0 + x * ctx->texture_block_w, frame->linesize[0], p3 != NULL ? p3 + x * ctx->texture_block_w : NULL, frame->linesize[3], d + (off + x) * ctx->tex_step); } } w_block = (avctx->coded_width / 2) / ctx->ctexture_block_w; h_block = (avctx->coded_height / 2) / ctx->ctexture_block_h; start_slice = h_block * slice / ctx->slice_count; end_slice = h_block * (slice + 1) / ctx->slice_count; for (y = start_slice; y < end_slice; y++) { uint8_t *p0 = frame->data[1] + y * frame->linesize[1] * ctx->ctexture_block_h; uint8_t *p1 = frame->data[2] + y * frame->linesize[2] * ctx->ctexture_block_h; int off = y * w_block; for (x = 0; x < w_block; x++) { ctx->tex_funct_planar[1](p0 + x * ctx->ctexture_block_w, frame->linesize[1], p1 + x * ctx->ctexture_block_w, frame->linesize[2], c + (off + x) * ctx->ctex_step); } } } return 0; } /* This scheme addresses already decoded elements depending on 2-bit status: * 0 -> copy new element * 1 -> copy one element from position -x * 2 -> copy one element from position -(get_byte() + 2) * x * 3 -> copy one element from position -(get_16le() + 0x102) * x * x is always 2 for dxt1 and 4 for dxt5. */ #define CHECKPOINT(x) \ do { \ if (state == 0) { \ if (bytestream2_get_bytes_left(gbc) < 4) \ return AVERROR_INVALIDDATA; \ value = bytestream2_get_le32(gbc); \ state = 16; \ } \ op = value & 0x3; \ value >>= 2; \ state--; \ switch (op) { \ case 1: \ idx = x; \ break; \ case 2: \ idx = (bytestream2_get_byte(gbc) + 2) * x; \ if (idx > pos) { \ av_log(avctx, AV_LOG_ERROR, "idx %d > %d\n", idx, pos); \ return AVERROR_INVALIDDATA; \ } \ break; \ case 3: \ idx = (bytestream2_get_le16(gbc) + 0x102) * x; \ if (idx > pos) { \ av_log(avctx, AV_LOG_ERROR, "idx %d > %d\n", idx, pos); \ return AVERROR_INVALIDDATA; \ } \ break; \ } \ } while(0) static int dxv_decompress_dxt1(AVCodecContext *avctx) { DXVContext *ctx = avctx->priv_data; GetByteContext *gbc = &ctx->gbc; uint32_t value, prev, op; int idx = 0, state = 0; int pos = 2; /* Copy the first two elements */ AV_WL32(ctx->tex_data, bytestream2_get_le32(gbc)); AV_WL32(ctx->tex_data + 4, bytestream2_get_le32(gbc)); /* Process input until the whole texture has been filled */ while (pos + 2 <= ctx->tex_size / 4) { CHECKPOINT(2); /* Copy two elements from a previous offset or from the input buffer */ if (op) { prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; } else { CHECKPOINT(2); if (op) prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); else prev = bytestream2_get_le32(gbc); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; CHECKPOINT(2); if (op) prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); else prev = bytestream2_get_le32(gbc); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; } } return 0; } typedef struct OpcodeTable { int16_t next; uint8_t val1; uint8_t val2; } OpcodeTable; static int fill_ltable(GetByteContext *gb, uint32_t *table, int *nb_elements) { unsigned half = 512, bits = 1023, left = 1024, input, mask; int value, counter = 0, rshift = 10, lshift = 30; mask = bytestream2_get_le32(gb) >> 2; while (left) { if (counter >= 256) return AVERROR_INVALIDDATA; value = bits & mask; left -= bits & mask; mask >>= rshift; lshift -= rshift; table[counter++] = value; if (lshift < 16) { if (bytestream2_get_bytes_left(gb) <= 0) return AVERROR_INVALIDDATA; input = bytestream2_get_le16(gb); mask += input << lshift; lshift += 16; } if (left < half) { half >>= 1; bits >>= 1; rshift--; } } for (; !table[counter - 1]; counter--) if (counter <= 0) return AVERROR_INVALIDDATA; *nb_elements = counter; if (counter < 256) memset(&table[counter], 0, 4 * (256 - counter)); if (lshift >= 16) bytestream2_seek(gb, -2, SEEK_CUR); return 0; } static int fill_optable(unsigned *table0, OpcodeTable *table1, int nb_elements) { unsigned table2[256] = { 0 }; unsigned x = 0; int val0, val1, i, j = 2, k = 0; table2[0] = table0[0]; for (i = 0; i < nb_elements - 1; i++, table2[i] = val0) { val0 = table0[i + 1] + table2[i]; } if (!table2[0]) { do { k++; } while (!table2[k]); } j = 2; for (i = 1024; i > 0; i--) { for (table1[x].val1 = k; k < 256 && j > table2[k]; k++); x = (x - 383) & 0x3FF; j++; } if (nb_elements > 0) memcpy(&table2[0], table0, 4 * nb_elements); for (i = 0; i < 1024; i++) { val0 = table1[i].val1; val1 = table2[val0]; table2[val0]++; x = 31 - ff_clz(val1); if (x > 10) return AVERROR_INVALIDDATA; table1[i].val2 = 10 - x; table1[i].next = (val1 << table1[i].val2) - 1024; } return 0; } static int get_opcodes(GetByteContext *gb, uint32_t *table, uint8_t *dst, int op_size, int nb_elements) { OpcodeTable optable[1024]; int sum, x, val, lshift, rshift, ret, i, idx; int64_t size_in_bits; unsigned endoffset, newoffset, offset; unsigned next; uint8_t *src = (uint8_t *)gb->buffer; ret = fill_optable(table, optable, nb_elements); if (ret < 0) return ret; size_in_bits = bytestream2_get_le32(gb); endoffset = ((size_in_bits + 7) >> 3) - 4; if (endoffset <= 0 || bytestream2_get_bytes_left(gb) < endoffset) return AVERROR_INVALIDDATA; offset = endoffset; next = AV_RL32(src + endoffset); rshift = (((size_in_bits & 0xFF) - 1) & 7) + 15; lshift = 32 - rshift; idx = (next >> rshift) & 0x3FF; for (i = 0; i < op_size; i++) { dst[i] = optable[idx].val1; val = optable[idx].val2; sum = val + lshift; x = (next << lshift) >> 1 >> (31 - val); newoffset = offset - (sum >> 3); lshift = sum & 7; idx = x + optable[idx].next; offset = newoffset; if (offset > endoffset) return AVERROR_INVALIDDATA; next = AV_RL32(src + offset); } bytestream2_skip(gb, (size_in_bits + 7 >> 3) - 4); return 0; } static int dxv_decompress_opcodes(GetByteContext *gb, void *dstp, size_t op_size) { int pos = bytestream2_tell(gb); int flag = bytestream2_peek_byte(gb); if ((flag & 3) == 0) { bytestream2_skip(gb, 1); bytestream2_get_buffer(gb, dstp, op_size); } else if ((flag & 3) == 1) { bytestream2_skip(gb, 1); memset(dstp, bytestream2_get_byte(gb), op_size); } else { uint32_t table[256]; int ret, elements = 0; ret = fill_ltable(gb, table, &elements); if (ret < 0) return ret; ret = get_opcodes(gb, table, dstp, op_size, elements); if (ret < 0) return ret; } return bytestream2_tell(gb) - pos; } static int dxv_decompress_cgo(DXVContext *ctx, GetByteContext *gb, uint8_t *tex_data, int tex_size, uint8_t *op_data, int *oindex, int op_size, uint8_t **dstp, int *statep, uint8_t **tab0, uint8_t **tab1, int offset) { uint8_t *dst = *dstp; uint8_t *tptr0, *tptr1, *tptr3; int oi = *oindex; int state = *statep; int opcode, v, vv; if (state <= 0) { if (oi >= op_size) return AVERROR_INVALIDDATA; opcode = op_data[oi++]; if (!opcode) { v = bytestream2_get_byte(gb); if (v == 255) { do { if (bytestream2_get_bytes_left(gb) <= 0) return AVERROR_INVALIDDATA; opcode = bytestream2_get_le16(gb); v += opcode; } while (opcode == 0xFFFF); } AV_WL32(dst, AV_RL32(dst - (8 + offset))); AV_WL32(dst + 4, AV_RL32(dst - (4 + offset))); state = v + 4; goto done; } switch (opcode) { case 1: AV_WL32(dst, AV_RL32(dst - (8 + offset))); AV_WL32(dst + 4, AV_RL32(dst - (4 + offset))); break; case 2: vv = (8 + offset) * (bytestream2_get_le16(gb) + 1); if (vv < 0 || vv > dst - tex_data) return AVERROR_INVALIDDATA; tptr0 = dst - vv; v = AV_RL32(tptr0); AV_WL32(dst, AV_RL32(tptr0)); AV_WL32(dst + 4, AV_RL32(tptr0 + 4)); tab0[0x9E3779B1 * (uint16_t)v >> 24] = dst; tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 3: AV_WL32(dst, bytestream2_get_le32(gb)); AV_WL32(dst + 4, bytestream2_get_le32(gb)); tab0[0x9E3779B1 * AV_RL16(dst) >> 24] = dst; tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 4: tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; AV_WL16(dst, bytestream2_get_le16(gb)); AV_WL16(dst + 2, AV_RL16(tptr3)); dst[4] = tptr3[2]; AV_WL16(dst + 5, bytestream2_get_le16(gb)); dst[7] = bytestream2_get_byte(gb); tab0[0x9E3779B1 * AV_RL16(dst) >> 24] = dst; break; case 5: tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; AV_WL16(dst, bytestream2_get_le16(gb)); AV_WL16(dst + 2, bytestream2_get_le16(gb)); dst[4] = bytestream2_get_byte(gb); AV_WL16(dst + 5, AV_RL16(tptr3)); dst[7] = tptr3[2]; tab0[0x9E3779B1 * AV_RL16(dst) >> 24] = dst; tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 6: tptr0 = tab1[bytestream2_get_byte(gb)]; if (!tptr0) return AVERROR_INVALIDDATA; tptr1 = tab1[bytestream2_get_byte(gb)]; if (!tptr1) return AVERROR_INVALIDDATA; AV_WL16(dst, bytestream2_get_le16(gb)); AV_WL16(dst + 2, AV_RL16(tptr0)); dst[4] = tptr0[2]; AV_WL16(dst + 5, AV_RL16(tptr1)); dst[7] = tptr1[2]; tab0[0x9E3779B1 * AV_RL16(dst) >> 24] = dst; break; case 7: v = (8 + offset) * (bytestream2_get_le16(gb) + 1); if (v < 0 || v > dst - tex_data) return AVERROR_INVALIDDATA; tptr0 = dst - v; AV_WL16(dst, bytestream2_get_le16(gb)); AV_WL16(dst + 2, AV_RL16(tptr0 + 2)); AV_WL32(dst + 4, AV_RL32(tptr0 + 4)); tab0[0x9E3779B1 * AV_RL16(dst) >> 24] = dst; tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 8: tptr1 = tab0[bytestream2_get_byte(gb)]; if (!tptr1) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(tptr1)); AV_WL16(dst + 2, bytestream2_get_le16(gb)); AV_WL32(dst + 4, bytestream2_get_le32(gb)); tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 9: tptr1 = tab0[bytestream2_get_byte(gb)]; if (!tptr1) return AVERROR_INVALIDDATA; tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(tptr1)); AV_WL16(dst + 2, AV_RL16(tptr3)); dst[4] = tptr3[2]; AV_WL16(dst + 5, bytestream2_get_le16(gb)); dst[7] = bytestream2_get_byte(gb); tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 10: tptr1 = tab0[bytestream2_get_byte(gb)]; if (!tptr1) return AVERROR_INVALIDDATA; tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(tptr1)); AV_WL16(dst + 2, bytestream2_get_le16(gb)); dst[4] = bytestream2_get_byte(gb); AV_WL16(dst + 5, AV_RL16(tptr3)); dst[7] = tptr3[2]; tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 11: tptr0 = tab0[bytestream2_get_byte(gb)]; if (!tptr0) return AVERROR_INVALIDDATA; tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; tptr1 = tab1[bytestream2_get_byte(gb)]; if (!tptr1) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(tptr0)); AV_WL16(dst + 2, AV_RL16(tptr3)); dst[4] = tptr3[2]; AV_WL16(dst + 5, AV_RL16(tptr1)); dst[7] = tptr1[2]; break; case 12: tptr1 = tab0[bytestream2_get_byte(gb)]; if (!tptr1) return AVERROR_INVALIDDATA; v = (8 + offset) * (bytestream2_get_le16(gb) + 1); if (v < 0 || v > dst - tex_data) return AVERROR_INVALIDDATA; tptr0 = dst - v; AV_WL16(dst, AV_RL16(tptr1)); AV_WL16(dst + 2, AV_RL16(tptr0 + 2)); AV_WL32(dst + 4, AV_RL32(tptr0 + 4)); tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 13: AV_WL16(dst, AV_RL16(dst - (8 + offset))); AV_WL16(dst + 2, bytestream2_get_le16(gb)); AV_WL32(dst + 4, bytestream2_get_le32(gb)); tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 14: tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(dst - (8 + offset))); AV_WL16(dst + 2, AV_RL16(tptr3)); dst[4] = tptr3[2]; AV_WL16(dst + 5, bytestream2_get_le16(gb)); dst[7] = bytestream2_get_byte(gb); tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 15: tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(dst - (8 + offset))); AV_WL16(dst + 2, bytestream2_get_le16(gb)); dst[4] = bytestream2_get_byte(gb); AV_WL16(dst + 5, AV_RL16(tptr3)); dst[7] = tptr3[2]; tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 16: tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; tptr1 = tab1[bytestream2_get_byte(gb)]; if (!tptr1) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(dst - (8 + offset))); AV_WL16(dst + 2, AV_RL16(tptr3)); dst[4] = tptr3[2]; AV_WL16(dst + 5, AV_RL16(tptr1)); dst[7] = tptr1[2]; break; case 17: v = (8 + offset) * (bytestream2_get_le16(gb) + 1); if (v < 0 || v > dst - tex_data) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(dst - (8 + offset))); AV_WL16(dst + 2, AV_RL16(&dst[-v + 2])); AV_WL32(dst + 4, AV_RL32(&dst[-v + 4])); tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; default: break; } } else { done: AV_WL32(dst, AV_RL32(dst - (8 + offset))); AV_WL32(dst + 4, AV_RL32(dst - (4 + offset))); state--; } if (dst - tex_data + 8 > tex_size) return AVERROR_INVALIDDATA; dst += 8; *oindex = oi; *dstp = dst; *statep = state; return 0; } static int dxv_decompress_cocg(DXVContext *ctx, GetByteContext *gb, uint8_t *tex_data, int tex_size, uint8_t *op_data0, uint8_t *op_data1, int max_op_size0, int max_op_size1) { uint8_t *dst, *tab2[256] = { 0 }, *tab0[256] = { 0 }, *tab3[256] = { 0 }, *tab1[256] = { 0 }; int op_offset = bytestream2_get_le32(gb); unsigned op_size0 = bytestream2_get_le32(gb); unsigned op_size1 = bytestream2_get_le32(gb); int data_start = bytestream2_tell(gb); int skip0, skip1, oi0 = 0, oi1 = 0; int ret, state0 = 0, state1 = 0; if (op_offset < 12 || op_offset - 12 > bytestream2_get_bytes_left(gb)) return AVERROR_INVALIDDATA; dst = tex_data; bytestream2_skip(gb, op_offset - 12); if (op_size0 > max_op_size0) return AVERROR_INVALIDDATA; skip0 = dxv_decompress_opcodes(gb, op_data0, op_size0); if (skip0 < 0) return skip0; if (op_size1 > max_op_size1) return AVERROR_INVALIDDATA; skip1 = dxv_decompress_opcodes(gb, op_data1, op_size1); if (skip1 < 0) return skip1; bytestream2_seek(gb, data_start, SEEK_SET); AV_WL32(dst, bytestream2_get_le32(gb)); AV_WL32(dst + 4, bytestream2_get_le32(gb)); AV_WL32(dst + 8, bytestream2_get_le32(gb)); AV_WL32(dst + 12, bytestream2_get_le32(gb)); tab0[0x9E3779B1 * AV_RL16(dst) >> 24] = dst; tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFF) >> 24] = dst + 2; tab2[0x9E3779B1 * AV_RL16(dst + 8) >> 24] = dst + 8; tab3[0x9E3779B1 * (AV_RL32(dst + 10) & 0xFFFFFF) >> 24] = dst + 10; dst += 16; while (dst + 10 < tex_data + tex_size) { ret = dxv_decompress_cgo(ctx, gb, tex_data, tex_size, op_data0, &oi0, op_size0, &dst, &state0, tab0, tab1, 8); if (ret < 0) return ret; ret = dxv_decompress_cgo(ctx, gb, tex_data, tex_size, op_data1, &oi1, op_size1, &dst, &state1, tab2, tab3, 8); if (ret < 0) return ret; } bytestream2_seek(gb, data_start - 12 + op_offset + skip0 + skip1, SEEK_SET); return 0; } static int dxv_decompress_yo(DXVContext *ctx, GetByteContext *gb, uint8_t *tex_data, int tex_size, uint8_t *op_data, int max_op_size) { int op_offset = bytestream2_get_le32(gb); unsigned op_size = bytestream2_get_le32(gb); int data_start = bytestream2_tell(gb); uint8_t *dst, *table0[256] = { 0 }, *table1[256] = { 0 }; int ret, state = 0, skip, oi = 0, v, vv; if (op_offset < 8 || op_offset - 8 > bytestream2_get_bytes_left(gb)) return AVERROR_INVALIDDATA; dst = tex_data; bytestream2_skip(gb, op_offset - 8); if (op_size > max_op_size) return AVERROR_INVALIDDATA; skip = dxv_decompress_opcodes(gb, op_data, op_size); if (skip < 0) return skip; bytestream2_seek(gb, data_start, SEEK_SET); v = bytestream2_get_le32(gb); AV_WL32(dst, v); vv = bytestream2_get_le32(gb); table0[0x9E3779B1 * (uint16_t)v >> 24] = dst; AV_WL32(dst + 4, vv); table1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFF) >> 24] = dst + 2; dst += 8; while (dst < tex_data + tex_size) { ret = dxv_decompress_cgo(ctx, gb, tex_data, tex_size, op_data, &oi, op_size, &dst, &state, table0, table1, 0); if (ret < 0) return ret; } bytestream2_seek(gb, data_start + op_offset + skip - 8, SEEK_SET); return 0; } static int dxv_decompress_ycg6(AVCodecContext *avctx) { DXVContext *ctx = avctx->priv_data; GetByteContext *gb = &ctx->gbc; int ret; ret = dxv_decompress_yo(ctx, gb, ctx->tex_data, ctx->tex_size, ctx->op_data[0], ctx->op_size[0]); if (ret < 0) return ret; return dxv_decompress_cocg(ctx, gb, ctx->ctex_data, ctx->ctex_size, ctx->op_data[1], ctx->op_data[2], ctx->op_size[1], ctx->op_size[2]); } static int dxv_decompress_yg10(AVCodecContext *avctx) { DXVContext *ctx = avctx->priv_data; GetByteContext *gb = &ctx->gbc; int ret; ret = dxv_decompress_cocg(ctx, gb, ctx->tex_data, ctx->tex_size, ctx->op_data[0], ctx->op_data[3], ctx->op_size[0], ctx->op_size[3]); if (ret < 0) return ret; return dxv_decompress_cocg(ctx, gb, ctx->ctex_data, ctx->ctex_size, ctx->op_data[1], ctx->op_data[2], ctx->op_size[1], ctx->op_size[2]); } static int dxv_decompress_dxt5(AVCodecContext *avctx) { DXVContext *ctx = avctx->priv_data; GetByteContext *gbc = &ctx->gbc; uint32_t value, op, prev; int idx, state = 0; int pos = 4; int run = 0; int probe, check; /* Copy the first four elements */ AV_WL32(ctx->tex_data + 0, bytestream2_get_le32(gbc)); AV_WL32(ctx->tex_data + 4, bytestream2_get_le32(gbc)); AV_WL32(ctx->tex_data + 8, bytestream2_get_le32(gbc)); AV_WL32(ctx->tex_data + 12, bytestream2_get_le32(gbc)); /* Process input until the whole texture has been filled */ while (pos + 2 <= ctx->tex_size / 4) { if (run) { run--; prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; } else { if (bytestream2_get_bytes_left(gbc) < 1) return AVERROR_INVALIDDATA; if (state == 0) { value = bytestream2_get_le32(gbc); state = 16; } op = value & 0x3; value >>= 2; state--; switch (op) { case 0: /* Long copy */ check = bytestream2_get_byte(gbc) + 1; if (check == 256) { do { probe = bytestream2_get_le16(gbc); check += probe; } while (probe == 0xFFFF); } while (check && pos + 4 <= ctx->tex_size / 4) { prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; check--; } /* Restart (or exit) the loop */ continue; break; case 1: /* Load new run value */ run = bytestream2_get_byte(gbc); if (run == 255) { do { probe = bytestream2_get_le16(gbc); run += probe; } while (probe == 0xFFFF); } /* Copy two dwords from previous data */ prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; break; case 2: /* Copy two dwords from a previous index */ idx = 8 + bytestream2_get_le16(gbc); if (idx > pos || (unsigned int)(pos - idx) + 2 > ctx->tex_size / 4) return AVERROR_INVALIDDATA; prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; break; case 3: /* Copy two dwords from input */ prev = bytestream2_get_le32(gbc); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; prev = bytestream2_get_le32(gbc); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; break; } } CHECKPOINT(4); if (pos + 2 > ctx->tex_size / 4) return AVERROR_INVALIDDATA; /* Copy two elements from a previous offset or from the input buffer */ if (op) { if (idx > pos || (unsigned int)(pos - idx) + 2 > ctx->tex_size / 4) return AVERROR_INVALIDDATA; prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; } else { CHECKPOINT(4); if (op && (idx > pos || (unsigned int)(pos - idx) + 2 > ctx->tex_size / 4)) return AVERROR_INVALIDDATA; if (op) prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); else prev = bytestream2_get_le32(gbc); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; CHECKPOINT(4); if (op) prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); else prev = bytestream2_get_le32(gbc); AV_WL32(ctx->tex_data + 4 * pos, prev); pos++; } } return 0; } static int dxv_decompress_lzf(AVCodecContext *avctx) { DXVContext *ctx = avctx->priv_data; return ff_lzf_uncompress(&ctx->gbc, &ctx->tex_data, &ctx->tex_size); } static int dxv_decompress_raw(AVCodecContext *avctx) { DXVContext *ctx = avctx->priv_data; GetByteContext *gbc = &ctx->gbc; if (bytestream2_get_bytes_left(gbc) < ctx->tex_size) return AVERROR_INVALIDDATA; bytestream2_get_buffer(gbc, ctx->tex_data, ctx->tex_size); return 0; } static int dxv_decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *avpkt) { DXVContext *ctx = avctx->priv_data; GetByteContext *gbc = &ctx->gbc; int (*decompress_tex)(AVCodecContext *avctx); const char *msgcomp, *msgtext; uint32_t tag; int version_major, version_minor = 0; int size = 0, old_type = 0; int ret; bytestream2_init(gbc, avpkt->data, avpkt->size); ctx->texture_block_h = 4; ctx->texture_block_w = 4; avctx->pix_fmt = AV_PIX_FMT_RGBA; avctx->colorspace = AVCOL_SPC_RGB; ctx->tex_funct = NULL; ctx->tex_funct_planar[0] = NULL; ctx->tex_funct_planar[1] = NULL; tag = bytestream2_get_le32(gbc); switch (tag) { case MKBETAG('D', 'X', 'T', '1'): decompress_tex = dxv_decompress_dxt1; ctx->tex_funct = ctx->texdsp.dxt1_block; ctx->tex_rat = 8; ctx->tex_step = 8; msgcomp = "DXTR1"; msgtext = "DXT1"; break; case MKBETAG('D', 'X', 'T', '5'): decompress_tex = dxv_decompress_dxt5; ctx->tex_funct = ctx->texdsp.dxt5_block; ctx->tex_rat = 4; ctx->tex_step = 16; msgcomp = "DXTR5"; msgtext = "DXT5"; break; case MKBETAG('Y', 'C', 'G', '6'): decompress_tex = dxv_decompress_ycg6; ctx->tex_funct_planar[0] = yo_block; ctx->tex_funct_planar[1] = cocg_block; ctx->tex_rat = 8; ctx->tex_step = 32; ctx->ctex_step = 16; msgcomp = "YOCOCG6"; msgtext = "YCG6"; ctx->ctex_size = avctx->coded_width * avctx->coded_height / 4; ctx->texture_block_h = 4; ctx->texture_block_w = 16; ctx->ctexture_block_h = 4; ctx->ctexture_block_w = 4; avctx->pix_fmt = AV_PIX_FMT_YUV420P; avctx->colorspace = AVCOL_SPC_YCOCG; break; case MKBETAG('Y', 'G', '1', '0'): decompress_tex = dxv_decompress_yg10; ctx->tex_funct_planar[0] = yao_block; ctx->tex_funct_planar[1] = cocg_block; ctx->tex_rat = 4; ctx->tex_step = 64; ctx->ctex_step = 16; msgcomp = "YAOCOCG10"; msgtext = "YG10"; ctx->ctex_size = avctx->coded_width * avctx->coded_height / 4; ctx->texture_block_h = 4; ctx->texture_block_w = 16; ctx->ctexture_block_h = 4; ctx->ctexture_block_w = 4; avctx->pix_fmt = AV_PIX_FMT_YUVA420P; avctx->colorspace = AVCOL_SPC_YCOCG; break; default: /* Old version does not have a real header, just size and type. */ size = tag & 0x00FFFFFF; old_type = tag >> 24; version_major = (old_type & 0x0F) - 1; if (old_type & 0x80) { msgcomp = "RAW"; decompress_tex = dxv_decompress_raw; } else { msgcomp = "LZF"; decompress_tex = dxv_decompress_lzf; } if (old_type & 0x40) { msgtext = "DXT5"; ctx->tex_funct = ctx->texdsp.dxt5_block; ctx->tex_step = 16; } else if (old_type & 0x20 || version_major == 1) { msgtext = "DXT1"; ctx->tex_funct = ctx->texdsp.dxt1_block; ctx->tex_step = 8; } else { av_log(avctx, AV_LOG_ERROR, "Unsupported header (0x%08"PRIX32")\n.", tag); return AVERROR_INVALIDDATA; } ctx->tex_rat = 1; break; } ctx->slice_count = av_clip(avctx->thread_count, 1, avctx->coded_height / FFMAX(ctx->texture_block_h, ctx->ctexture_block_h)); /* New header is 12 bytes long. */ if (!old_type) { version_major = bytestream2_get_byte(gbc) - 1; version_minor = bytestream2_get_byte(gbc); /* Encoder copies texture data when compression is not advantageous. */ if (bytestream2_get_byte(gbc)) { msgcomp = "RAW"; ctx->tex_rat = 1; decompress_tex = dxv_decompress_raw; } bytestream2_skip(gbc, 1); // unknown size = bytestream2_get_le32(gbc); } av_log(avctx, AV_LOG_DEBUG, "%s compression with %s texture (version %d.%d)\n", msgcomp, msgtext, version_major, version_minor); if (size != bytestream2_get_bytes_left(gbc)) { av_log(avctx, AV_LOG_ERROR, "Incomplete or invalid file (header %d, left %u).\n", size, bytestream2_get_bytes_left(gbc)); return AVERROR_INVALIDDATA; } ctx->tex_size = avctx->coded_width * avctx->coded_height * 4 / ctx->tex_rat; ret = av_reallocp(&ctx->tex_data, ctx->tex_size + AV_INPUT_BUFFER_PADDING_SIZE); if (ret < 0) return ret; if (ctx->ctex_size) { int i; ctx->op_size[0] = avctx->coded_width * avctx->coded_height / 16; ctx->op_size[1] = avctx->coded_width * avctx->coded_height / 32; ctx->op_size[2] = avctx->coded_width * avctx->coded_height / 32; ctx->op_size[3] = avctx->coded_width * avctx->coded_height / 16; ret = av_reallocp(&ctx->ctex_data, ctx->ctex_size + AV_INPUT_BUFFER_PADDING_SIZE); if (ret < 0) return ret; for (i = 0; i < 4; i++) { ret = av_reallocp(&ctx->op_data[i], ctx->op_size[i]); if (ret < 0) return ret; } } /* Decompress texture out of the intermediate compression. */ ret = decompress_tex(avctx); if (ret < 0) return ret; { int w_block = avctx->coded_width / ctx->texture_block_w; int h_block = avctx->coded_height / ctx->texture_block_h; if (w_block * h_block * ctx->tex_step > ctx->tex_size * 8LL) return AVERROR_INVALIDDATA; } ret = ff_thread_get_buffer(avctx, frame, 0); if (ret < 0) return ret; /* Now decompress the texture with the standard functions. */ avctx->execute2(avctx, decompress_texture_thread, frame, NULL, ctx->slice_count); /* Frame is ready to be output. */ frame->pict_type = AV_PICTURE_TYPE_I; frame->key_frame = 1; *got_frame = 1; return avpkt->size; } static int dxv_init(AVCodecContext *avctx) { DXVContext *ctx = avctx->priv_data; int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "Invalid image size %dx%d.\n", avctx->width, avctx->height); return ret; } /* Codec requires 16x16 alignment. */ avctx->coded_width = FFALIGN(avctx->width, 16); avctx->coded_height = FFALIGN(avctx->height, 16); ff_texturedsp_init(&ctx->texdsp); return 0; } static int dxv_close(AVCodecContext *avctx) { DXVContext *ctx = avctx->priv_data; av_freep(&ctx->tex_data); av_freep(&ctx->ctex_data); av_freep(&ctx->op_data[0]); av_freep(&ctx->op_data[1]); av_freep(&ctx->op_data[2]); av_freep(&ctx->op_data[3]); return 0; } const FFCodec ff_dxv_decoder = { .p.name = "dxv", .p.long_name = NULL_IF_CONFIG_SMALL("Resolume DXV"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_DXV, .init = dxv_init, FF_CODEC_DECODE_CB(dxv_decode), .close = dxv_close, .priv_data_size = sizeof(DXVContext), .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, };