+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, size_in_bits, i, idx;
+ 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;
+
+ 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;
+ bytestream2_seek(gb, data_start + op_offset + skip0 - 12, SEEK_SET);
+ 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 + op_offset + skip0 + skip1 - 12, 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;
+
+ 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]);
+}
+