1/*
2 * Renderware TeXture Dictionary (.txd) image decoder
3 * Copyright (c) 2007 Ivo van Poorten
4 *
5 * See also: http://wiki.multimedia.cx/index.php?title=TXD
6 *
7 * This file is part of FFmpeg.
8 *
9 * FFmpeg is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * FFmpeg is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with FFmpeg; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24#include "libavutil/intreadwrite.h"
25#include "avcodec.h"
26#include "s3tc.h"
27
28typedef struct TXDContext {
29    AVFrame picture;
30} TXDContext;
31
32static av_cold int txd_init(AVCodecContext *avctx) {
33    TXDContext *s = avctx->priv_data;
34
35    avcodec_get_frame_defaults(&s->picture);
36    avctx->coded_frame = &s->picture;
37
38    return 0;
39}
40
41static int txd_decode_frame(AVCodecContext *avctx, void *data, int *data_size,
42                            AVPacket *avpkt) {
43    const uint8_t *buf = avpkt->data;
44    TXDContext * const s = avctx->priv_data;
45    AVFrame *picture = data;
46    AVFrame * const p = &s->picture;
47    unsigned int version, w, h, d3d_format, depth, stride, mipmap_count, flags;
48    unsigned int y, v;
49    uint8_t *ptr;
50    const uint8_t *cur = buf;
51    const uint32_t *palette = (const uint32_t *)(cur + 88);
52    uint32_t *pal;
53
54    version         = AV_RL32(cur);
55    d3d_format      = AV_RL32(cur+76);
56    w               = AV_RL16(cur+80);
57    h               = AV_RL16(cur+82);
58    depth           = AV_RL8 (cur+84);
59    mipmap_count    = AV_RL8 (cur+85);
60    flags           = AV_RL8 (cur+87);
61    cur            += 92;
62
63    if (version < 8 || version > 9) {
64        av_log(avctx, AV_LOG_ERROR, "texture data version %i is unsupported\n",
65                                                                    version);
66        return -1;
67    }
68
69    if (depth == 8) {
70        avctx->pix_fmt = PIX_FMT_PAL8;
71        cur += 1024;
72    } else if (depth == 16 || depth == 32)
73        avctx->pix_fmt = PIX_FMT_RGB32;
74    else {
75        av_log(avctx, AV_LOG_ERROR, "depth of %i is unsupported\n", depth);
76        return -1;
77    }
78
79    if (p->data[0])
80        avctx->release_buffer(avctx, p);
81
82    if (avcodec_check_dimensions(avctx, w, h))
83        return -1;
84    if (w != avctx->width || h != avctx->height)
85        avcodec_set_dimensions(avctx, w, h);
86    if (avctx->get_buffer(avctx, p) < 0) {
87        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
88        return -1;
89    }
90
91    p->pict_type = FF_I_TYPE;
92
93    ptr    = p->data[0];
94    stride = p->linesize[0];
95
96    if (depth == 8) {
97        pal = (uint32_t *) p->data[1];
98        for (y=0; y<256; y++) {
99            v = AV_RB32(palette+y);
100            pal[y] = (v>>8) + (v<<24);
101        }
102        for (y=0; y<h; y++) {
103            memcpy(ptr, cur, w);
104            ptr += stride;
105            cur += w;
106        }
107    } else if (depth == 16) {
108        switch (d3d_format) {
109        case 0:
110            if (!flags&1) goto unsupported;
111        case FF_S3TC_DXT1:
112            ff_decode_dxt1(cur, ptr, w, h, stride);
113            break;
114        case FF_S3TC_DXT3:
115            ff_decode_dxt3(cur, ptr, w, h, stride);
116            break;
117        default:
118            goto unsupported;
119        }
120    } else if (depth == 32) {
121        switch (d3d_format) {
122        case 0x15:
123        case 0x16:
124            for (y=0; y<h; y++) {
125                memcpy(ptr, cur, w*4);
126                ptr += stride;
127                cur += w*4;
128            }
129            break;
130        default:
131            goto unsupported;
132        }
133    }
134
135    for (; mipmap_count > 1; mipmap_count--)
136        cur += AV_RL32(cur) + 4;
137
138    *picture   = s->picture;
139    *data_size = sizeof(AVPicture);
140
141    return cur - buf;
142
143unsupported:
144    av_log(avctx, AV_LOG_ERROR, "unsupported d3d format (%08x)\n", d3d_format);
145    return -1;
146}
147
148static av_cold int txd_end(AVCodecContext *avctx) {
149    TXDContext *s = avctx->priv_data;
150
151    if (s->picture.data[0])
152        avctx->release_buffer(avctx, &s->picture);
153
154    return 0;
155}
156
157AVCodec txd_decoder = {
158    "txd",
159    AVMEDIA_TYPE_VIDEO,
160    CODEC_ID_TXD,
161    sizeof(TXDContext),
162    txd_init,
163    NULL,
164    txd_end,
165    txd_decode_frame,
166    CODEC_CAP_DR1,
167    NULL,
168    .long_name = NULL_IF_CONFIG_SMALL("Renderware TXD (TeXture Dictionary) image"),
169};
170