[PATCH] Add LucasArts SMUSH demuxer and decoder. (original) (raw)
Kostya Shishkov kostya.shishkov
Sun Jan 23 15:36:23 CET 2011
- Previous message: [FFmpeg-devel] [PATCH] Disable symbol versioning on some BSDs
- Next message: [FFmpeg-devel] [Toy] LucasArts SMUSH demuxer and decoder
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/avcodec.h | 1 + libavcodec/sanm.c | 1381 ++++++++++++++++++++++++++++++++++++++++++++++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/sanm.c | 395 +++++++++++++ 7 files changed, 1781 insertions(+), 0 deletions(-) create mode 100644 libavcodec/sanm.c create mode 100644 libavformat/sanm.c
diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 9abedfc..cf782a6 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -185,6 +185,7 @@ OBJS-$(CONFIG_RV20_DECODER) += rv10.o h263.o mpeg12data.o mpegvideo.o OBJS-$(CONFIG_RV20_ENCODER) += rv10.o mpegvideo_enc.o motion_est.o ratecontrol.o h263.o mpeg12data.o mpegvideo.o error_resilience.o OBJS-$(CONFIG_RV30_DECODER) += rv30.o rv34.o h264pred.o rv30dsp.o OBJS-$(CONFIG_RV40_DECODER) += rv40.o rv34.o h264pred.o rv40dsp.o +OBJS-$(CONFIG_SANM_DECODER) += sanm.o OBJS-$(CONFIG_SGI_DECODER) += sgidec.o OBJS-$(CONFIG_SGI_ENCODER) += sgienc.o rle.o OBJS-$(CONFIG_SHORTEN_DECODER) += shorten.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index f369c0a..8da8077 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -137,6 +137,7 @@ void avcodec_register_all(void) REGISTER_ENCDEC (RV20, rv20); REGISTER_DECODER (RV30, rv30); REGISTER_DECODER (RV40, rv40); + REGISTER_DECODER (SANM, sanm); REGISTER_ENCDEC (SGI, sgi); REGISTER_DECODER (SMACKER, smacker); REGISTER_DECODER (SMC, smc); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 6aa41b5..135b2bc 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -190,6 +190,7 @@ enum CodecID { CODEC_ID_MOTIONPIXELS, CODEC_ID_TGV, CODEC_ID_TGQ, + CODEC_ID_SANM, /* various PCM "codecs" / CODEC_ID_PCM_S16LE= 0x10000, diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c new file mode 100644 index 0000000..f01ab11 --- /dev/null +++ b/libavcodec/sanm.c @@ -0,0 +1,1381 @@ +/ + * LucasArts Smush v2 decoder + * Copyright (c) 2006 Cyril Zorin + * + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + / + +/* + * @file sanm.c + * LucasArts Smush v2 decoder + / + +/ + * Based on http://wiki.multimedia.cx/index.php?title=SANM + / + +#include "avcodec.h" +#include "bytestream.h" + +#define GLYPH_COORD_VECT_SIZE 16 +static const int8_t glyph4_x[GLYPH_COORD_VECT_SIZE] = { 0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0, 1, 2, 2, 1 }; +static const int8_t glyph4_y[GLYPH_COORD_VECT_SIZE] = { 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 2, 2 }; +static const int8_t glyph8_x[GLYPH_COORD_VECT_SIZE] = { 0, 2, 5, 7, 7, 7, 7, 7, 7, 5, 2, 0, 0, 0, 0, 0 }; +static const int8_t glyph8_y[GLYPH_COORD_VECT_SIZE] = { 0, 0, 0, 0, 1, 3, 4, 6, 7, 7, 7, 7, 6, 4, 3, 1 }; + +static const int8_t motion_vectors[256][2] = +{ + {0, 0}, {-1, -43}, {6, -43}, {-9, -42}, {13, -41}, + {-16, -40}, {19, -39}, {-23, -36}, {26, -34}, {-2, -33}, + {4, -33}, {-29, -32}, {-9, -32}, {11, -31}, {-16, -29}, + {32, -29}, {18, -28}, {-34, -26}, {-22, -25}, {-1, -25}, + {3, -25}, {-7, -24}, {8, -24}, {24, -23}, {36, -23}, + {-12, -22}, {13, -21}, {-38, -20}, {0, -20}, {-27, -19}, + {-4, -19}, {4, -19}, {-17, -18}, {-8, -17}, {8, -17}, + {18, -17}, {28, -17}, {39, -17}, {-12, -15}, {12, -15}, + {-21, -14}, {-1, -14}, {1, -14}, {-41, -13}, {-5, -13}, + {5, -13}, {21, -13}, {-31, -12}, {-15, -11}, {-8, -11}, + {8, -11}, {15, -11}, {-2, -10}, {1, -10}, {31, -10}, + {-23, -9}, {-11, -9}, {-5, -9}, {4, -9}, {11, -9}, + {42, -9}, {6, -8}, {24, -8}, {-18, -7}, {-7, -7}, + {-3, -7}, {-1, -7}, {2, -7}, {18, -7}, {-43, -6}, + {-13, -6}, {-4, -6}, {4, -6}, {8, -6}, {-33, -5}, + {-9, -5}, {-2, -5}, {0, -5}, {2, -5}, {5, -5}, + {13, -5}, {-25, -4}, {-6, -4}, {-3, -4}, {3, -4}, + {9, -4}, {-19, -3}, {-7, -3}, {-4, -3}, {-2, -3}, + {-1, -3}, {0, -3}, {1, -3}, {2, -3}, {4, -3}, + {6, -3}, {33, -3}, {-14, -2}, {-10, -2}, {-5, -2}, + {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, + {2, -2}, {3, -2}, {5, -2}, {7, -2}, {14, -2}, + {19, -2}, {25, -2}, {43, -2}, {-7, -1}, {-3, -1}, + {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, + {3, -1}, {10, -1}, {-5, 0}, {-3, 0}, {-2, 0}, + {-1, 0}, {1, 0}, {2, 0}, {3, 0}, {5, 0}, + {7, 0}, {-10, 1}, {-7, 1}, {-3, 1}, {-2, 1}, + {-1, 1}, {0, 1}, {1, 1}, {2, 1}, {3, 1}, + {-43, 2}, {-25, 2}, {-19, 2}, {-14, 2}, {-5, 2}, + {-3, 2}, {-2, 2}, {-1, 2}, {0, 2}, {1, 2}, + {2, 2}, {3, 2}, {5, 2}, {7, 2}, {10, 2}, + {14, 2}, {-33, 3}, {-6, 3}, {-4, 3}, {-2, 3}, + {-1, 3}, {0, 3}, {1, 3}, {2, 3}, {4, 3}, + {19, 3}, {-9, 4}, {-3, 4}, {3, 4}, {7, 4}, + {25, 4}, {-13, 5}, {-5, 5}, {-2, 5}, {0, 5}, + {2, 5}, {5, 5}, {9, 5}, {33, 5}, {-8, 6}, + {-4, 6}, {4, 6}, {13, 6}, {43, 6}, {-18, 7}, + {-2, 7}, {0, 7}, {2, 7}, {7, 7}, {18, 7}, + {-24, 8}, {-6, 8}, {-42, 9}, {-11, 9}, {-4, 9}, + {5, 9}, {11, 9}, {23, 9}, {-31, 10}, {-1, 10}, + {2, 10}, {-15, 11}, {-8, 11}, {8, 11}, {15, 11}, + {31, 12}, {-21, 13}, {-5, 13}, {5, 13}, {41, 13}, + {-1, 14}, {1, 14}, {21, 14}, {-12, 15}, {12, 15}, + {-39, 17}, {-28, 17}, {-18, 17}, {-8, 17}, {8, 17}, + {17, 18}, {-4, 19}, {0, 19}, {4, 19}, {27, 19}, + {38, 20}, {-13, 21}, {12, 22}, {-36, 23}, {-24, 23}, + {-8, 24}, {7, 24}, {-3, 25}, {1, 25}, {22, 25}, + {34, 26}, {-18, 28}, {-32, 29}, {16, 29}, {-11, 31}, + {9, 32}, {29, 32}, {-4, 33}, {2, 33}, {-26, 34}, + {23, 36}, {-19, 39}, {16, 40}, {-13, 41}, {9, 42}, + {-6, 43}, {1, 43}, {0, 0}, {0, 0}, {0, 0} +}; + +#define NGLYPHS 256 +static int8_t p4x4glyphs[NGLYPHS][16]; +static int8_t p8x8glyphs[NGLYPHS][64]; + +typedef struct sanm_ctx +{ + AVCodecContext avctx; + const uint8_t psrc; + + int version, subversion; + uint32_t pal[256]; + int16_t delta_pal[768]; + + int pitch; + int width, height; + int aligned_width, aligned_height; + int prev_seq; + + AVFrame frame, output; + uint16_t pdb0, * pdb1, * pdb2; + int rotate_code; + + long npixels, buf_size; + + uint16_t pcodebook; + uint16_t* psmall_codebook; +} sanm_ctx; + +typedef struct sanm_frame_header +{ + int seq_num, codec, rotate_code, rle_output_size; + + uint16_t bg_color; + uint32_t width, height; + + const uint8_t* pvstream; +} sanm_frame_header; + +typedef enum glyph_edge +{ + left_edge, + top_edge, + right_edge, + bottom_edge, + no_edge +} glyph_edge; + +typedef enum glyph_dir +{ + dir_left = 0, + dir_up, + dir_right, + dir_down +} glyph_dir; + +static uint16_t* point_at(uint16_t* pbuf, int x, int y, int width) +{ + return &pbuf[y * width + x]; +} + +static glyph_edge which_edge(int x, int y, int edge_size) +{ + glyph_edge edge; + const int edge_max = edge_size - 1; + + if (!y) + { + edge = bottom_edge; + } + else if (edge_max == y) + { + edge = top_edge; + } + else if (!x) + { + edge = left_edge; + } + else if (edge_max == x) + { + edge = right_edge; + } + else + { + edge = no_edge; + } + + return edge; +} + +static glyph_dir which_direction(glyph_edge edge0, glyph_edge edge1) +{ + glyph_dir dir = -1; + + if ((left_edge == edge0 && right_edge == edge1) || + (left_edge == edge1 && right_edge == edge0) || + (bottom_edge == edge0 && top_edge != edge1) || + (bottom_edge == edge1 && top_edge != edge0)) + { + dir = dir_up; + } + else if ((top_edge == edge0 && bottom_edge != edge1) || + (top_edge == edge1 && bottom_edge != edge0)) + { + dir = dir_down; + } + else if ((left_edge == edge0 && right_edge != edge1) || + (left_edge == edge1 && right_edge != edge0)) + { + dir = dir_left; + } + else if ((top_edge == edge0 && bottom_edge == edge1) || + (top_edge == edge1 && bottom_edge == edge0) || + (right_edge == edge0 && left_edge != edge1) || + (right_edge == edge1 && left_edge != edge0)) + { + dir = dir_right; + } + + return dir; +} + +static void interp_point(int8_t* ppoint, int x0, int y0, int x1, int y1, int ipoint, int npoints) +{ + if (npoints) + { + ppoint[0] = (x0 * ipoint + x1 * (npoints - ipoint) + npoints / 2) / npoints; + ppoint[1] = (y0 * ipoint + y1 * (npoints - ipoint) + npoints / 2) / npoints; + } + else + { + ppoint[0] = x0; + ppoint[1] = y0; + } +} + +static void make_glyphs(int8_t* pglyphs, const int8_t* pxvector, const int8_t* pyvector, const int side_length) +{ + const int glyph_size = side_length * side_length; + int8_t* pglyph = pglyphs; + + int i, j; + for (i = 0; i != GLYPH_COORD_VECT_SIZE; ++i) + { + int x0 = pxvector[i], y0 = pyvector[i]; + glyph_edge edge0 = which_edge(x0, y0, side_length); + + for (j = 0; j != GLYPH_COORD_VECT_SIZE; ++j, pglyph += glyph_size) + { + int x1 = pxvector[j], y1 = pyvector[j]; + glyph_edge edge1 = which_edge(x1, y1, side_length); + glyph_dir dir = which_direction(edge0, edge1); + int ipoint, npoints = FFMAX(FFABS(x1 - x0), FFABS(y1 - y0)); + + memset(pglyph, 0, glyph_size * sizeof(pglyph[0])); + for (ipoint = 0; ipoint <= npoints; ++ipoint) + { + int8_t point[2]; + int irow, icol; + + interp_point(point, x0, y0, x1, y1, ipoint, npoints); + + switch (dir) + { + case dir_up: + for (irow = point[1]; irow >= 0; --irow) + { + pglyph[point[0] + irowside_length] = 1; + } + break; + + case dir_down: + for (irow = point[1]; irow < side_length; ++irow) + { + pglyph[point[0] + irow*side_length] = 1; + } + break; + + case dir_left: + for (icol = point[0]; icol >= 0; --icol) + { + pglyph[icol + point[1]side_length] = 1; + } + break; + + case dir_right: + for (icol = point[0]; icol < side_length; ++icol) + { + pglyph[icol + point[1]*side_length] = 1; + } + break; + } + } + } + } +} + +static void init_sizes(sanm_ctx* pctx, int width, int height) +{ + pctx->width = width; + pctx->height = height; + pctx->npixels = width * height; + + pctx->aligned_width = (width + 7) & ~7; + pctx->aligned_height = (height + 7) & ~7; + + pctx->buf_size = pctx->aligned_width * pctx->aligned_height * sizeof(pctx->pdb0[0]); + pctx->pitch = width; +} + +static void init_buffers(sanm_ctx pctx) +{ + pctx->pdb0 = av_realloc(pctx->pdb0, pctx->buf_size); + memset(pctx->pdb0, 0, pctx->buf_size); + + pctx->pdb1 = av_realloc(pctx->pdb1, pctx->buf_size); + memset(pctx->pdb1, 0, pctx->buf_size); + + pctx->pdb2 = av_realloc(pctx->pdb2, pctx->buf_size); + memset(pctx->pdb2, 0, pctx->buf_size); +} + +static void destroy_buffers(sanm_ctx pctx) +{ + av_free(pctx->pdb0); + av_free(pctx->pdb1); + av_free(pctx->pdb2); +} + +static int decode_init(AVCodecContext* pav_ctx) +{ + sanm_ctx* pctx = pav_ctx->priv_data; + + pctx->avctx = pav_ctx; + /if (avcodec_check_dimensions(pav_ctx, pav_ctx->height, pav_ctx->width) < 0) + { + return -1; + }*/ + + pctx->version = !pav_ctx->extradata_size; + + pav_ctx->has_b_frames = 0; + pav_ctx->pix_fmt = pctx->version ? PIX_FMT_RGB565 : PIX_FMT_PAL8; + + init_sizes(pctx, pav_ctx->width, pav_ctx->height); + init_buffers(pctx); + pctx->output = &pctx->frame; + pctx->output->data[0] = 0; + pctx->output->quality = 1; + + make_glyphs(&p4x4glyphs[0][0], glyph4_x, glyph4_y, 4); + make_glyphs(&p8x8glyphs[0][0], glyph8_x, glyph8_y, 8); + + if(!pctx->version){ + int i; + pctx->subversion = AV_RL16(pav_ctx->extradata); + for(i = 0; i < 256; i++) + pctx->pal[i] = AV_RL32(pav_ctx->extradata + 2 + i4); + } + + return 0; +} + +static int decode_end(AVCodecContext* pav_ctx) +{ + sanm_ctx* pctx = pav_ctx->priv_data; + int i; + + destroy_buffers(pctx); + + if (pctx->frame.data[0]){ + pav_ctx->release_buffer(pav_ctx, &pctx->frame); + pctx->frame.data[0] = 0; + } + + return 0; +} + +static int read_frame_header(sanm_ctx* pctx, const uint8_t* pinput, sanm_frame_header* pheader) +{ + pinput += 8; // skip pad + + pheader->width = AV_RL32(pinput); pinput += 4; + pheader->height = AV_RL32(pinput); pinput += 4; + + if (pheader->width != pctx->width || pheader->height != pctx->height) + { + av_log(0, AV_LOG_ERROR, "sanm decoder: variable size frames are not implemented. video may be garbled until next keyframe.\n"); + return -1; + } + + pheader->seq_num = AV_RL16(pinput); pinput += 2; + pheader->codec = pinput; pinput += 1; + pheader->rotate_code = pinput; pinput += 1; + + pinput += 4; // skip pad + + pctx->psmall_codebook = (uint16_t) pinput; pinput += 8; + pheader->bg_color = AV_RL16(pinput); pinput += 2; + + pinput += 2; // skip pad + + pheader->rle_output_size = AV_RL32(pinput); pinput += 4; + pctx->pcodebook = (uint16_t) pinput; pinput += 512; // 256 entries in codebook + + pinput += 8; // skip pad + + pheader->pvstream = pinput; + + return 0; +} + +static void fill_db(uint16_t* pbuf, int buf_size, uint16_t color) +{ + while (buf_size--) + { + pbuf++ = color; + } +} + +static void swap(uint16_t* ppleft, uint16_t** ppright) +{ + uint16_t* ptemp = ppleft; + ppleft = ppright; + ppright = ptemp; +} + +static void rotate_bufs(sanm_ctx pctx, int rotate_code) +{ + if (1 == rotate_code) + { + swap(&pctx->pdb0, &pctx->pdb2); + } + else if (2 == rotate_code) + { + swap(&pctx->pdb1, &pctx->pdb2); + swap(&pctx->pdb2, &pctx->pdb0); + } +} + +static void codec0(sanm_ctx pctx, const uint8_t pinput) +{ + uint16_t pdb0 = pctx->pdb0; + + int x, y; + for (y = 0; y != pctx->height; ++y) + { + for (x = 0; x != pctx->width; ++x) + { + point_at(pdb0, x, y, pctx->pitch) = AV_RL16(pinput); pinput += 2; + } + } +} + +static void codec6(sanm_ctx pctx, const uint8_t* pinput) +{ + int npixels = pctx->npixels; + uint16_t* pdb0 = pctx->pdb0; + uint16_t* pcodebook = pctx->pcodebook; + + int index; + while (npixels--) + { + index = pinput++; + pdb0++ = AV_RL16(&pcodebook[index]); + } +} + +static void copy_block(uint16_t pdest, uint16_t psrc, int block_size, int pitch) +{ + int y; + for (y = 0; y != block_size; ++y, pdest += pitch, psrc += pitch) + { + memcpy(pdest, psrc, block_size * sizeof(pdest[0])); + } +} + +static void fill_block(uint16_t* pdest, uint16_t color, int block_size, int pitch) +{ + int x, y; + pitch -= block_size; + for (y = 0; y != block_size; ++y, pdest += pitch) + { + for (x = 0; x != block_size; ++x) + { + pdest++ = color; + } + } +} + +static void draw_glyph(uint16_t pdest, int index, uint16_t fg_color, uint16_t bg_color, int block_size, int pitch) +{ + int8_t* pglyph; + uint16_t colors[2] = { fg_color, bg_color }; + int x, y; + + if (index > NGLYPHS) + { + av_log(0, AV_LOG_ERROR, "sanm decoder: ignoring nonexistent glyph #%u.\n", index); + return; + } + + pglyph = (8 == block_size) ? p8x8glyphs[index] : p4x4glyphs[index]; + pitch -= block_size; + + for (y = 0; y != block_size; ++y, pdest += pitch) + { + for (x = 0; x != block_size; ++x) + { + pdest++ = colors[pglyph++]; + } + } +} + +static void opcode_0xf7(sanm_ctx pctx, int cx, int cy, int block_size, int pitch) +{ + if (2 == block_size) + { + uint16_t pcodebook = pctx->pcodebook; + uint16_t* pdest = point_at(pctx->pdb0, cx, cy, pctx->pitch); + uint32_t indices = AV_RL32(pctx->psrc); pctx->psrc += 4; + + pdest[0] = AV_RL16(&pcodebook[indices & 0xff]); indices >>= 8; + pdest[1] = AV_RL16(&pcodebook[indices & 0xff]); indices >>= 8; + pdest[pitch] = AV_RL16(&pcodebook[indices & 0xff]); indices >>= 8; + pdest[pitch + 1] = AV_RL16(&pcodebook[indices & 0xff]); + } + else + { + uint16_t fgcolor, bgcolor; + + int index = pctx->psrc++; + uint16_t color_indices = AV_RL16(pctx->psrc); pctx->psrc += 2; + fgcolor = AV_RL16(&pctx->pcodebook[color_indices >> 8]); + bgcolor = AV_RL16(&pctx->pcodebook[color_indices & 0xff]); + + draw_glyph(point_at(pctx->pdb0, cx, cy, pctx->pitch), index, fgcolor, bgcolor, block_size, pitch); + } +} + +static void opcode_0xf8(sanm_ctx pctx, int cx, int cy, int block_size, int pitch) +{ + if (2 == block_size) + { + uint16_t* pdest = point_at(pctx->pdb0, cx, cy, pctx->pitch); + pdest[0] = AV_RL16(pctx->psrc); pctx->psrc += 2; + pdest[1] = AV_RL16(pctx->psrc); pctx->psrc += 2; + pdest[pitch] = AV_RL16(pctx->psrc); pctx->psrc += 2; + pdest[pitch + 1] = AV_RL16(pctx->psrc); pctx->psrc += 2; + } + else + { + uint16_t fgcolor, bgcolor; + + int index = pctx->psrc++; + uint32_t colors = AV_RL32(pctx->psrc); pctx->psrc += 4; + fgcolor = colors >> 16; + bgcolor = colors & 0xffff; + + draw_glyph(point_at(pctx->pdb0, cx, cy, pctx->pitch), index, fgcolor, bgcolor, block_size, pitch); + } +} + +static int good_mvec(sanm_ctx pctx, int cx, int cy, int mx, int my, int block_size) +{ + int good = point_at(pctx->pdb2, cx + mx + block_size - 1, cy + my + block_size - 1, pctx->pitch) < (&pctx->pdb2[pctx->buf_size / 2]); + if (!good) + { + av_log(0, AV_LOG_ERROR, "sanm decoder: ignoring segfault-inducing motion vector (%i, %i)->(%u, %u), block size = %u.\n", + cx + mx, cy + my, cx, cy, block_size); + } + + return good; +} + +static void codec2level(sanm_ctx* pctx, int cx, int cy, int block_size) +{ + int16_t mx, my, index; + uint16_t color; + + int opcode = pctx->psrc++; + + switch (opcode) + { + default: + mx = motion_vectors[opcode][0]; + my = motion_vectors[opcode][1]; + + if (good_mvec(pctx, cx, cy, mx, my, block_size)) + { + copy_block(point_at(pctx->pdb0, cx, cy, pctx->pitch), + point_at(pctx->pdb2, cx + mx, cy + my, pctx->pitch), + block_size, pctx->pitch); + } + break; + + case 0xf5: + index = AV_RL16(pctx->psrc); pctx->psrc += 2; + + mx = index % pctx->width; + my = index / pctx->width; + + if (good_mvec(pctx, cx, cy, mx, my, block_size)) + { + copy_block(point_at(pctx->pdb0, cx, cy, pctx->pitch), + point_at(pctx->pdb2, cx + mx, cy + my, pctx->pitch), + block_size, pctx->pitch); + } + break; + + case 0xf6: + copy_block(point_at(pctx->pdb0, cx, cy, pctx->pitch), + point_at(pctx->pdb1, cx, cy, pctx->pitch), + block_size, pctx->pitch); + break; + + case 0xf7: + opcode_0xf7(pctx, cx, cy, block_size, pctx->pitch); + break; + + case 0xf8: + opcode_0xf8(pctx, cx, cy, block_size, pctx->pitch); + break; + + case 0xf9: + case 0xfa: + case 0xfb: + case 0xfc: + color = AV_RL16(&pctx->psmall_codebook[opcode - 0xf9]); + fill_block(point_at(pctx->pdb0, cx, cy, pctx->pitch), color, block_size, pctx->pitch); + break; + + case 0xfd: + index = pctx->psrc++; + color = AV_RL16(&pctx->pcodebook[index]); + fill_block(point_at(pctx->pdb0, cx, cy, pctx->pitch), color, block_size, pctx->pitch); + break; + + case 0xfe: + color = AV_RL16(pctx->psrc); pctx->psrc += 2; + fill_block(point_at(pctx->pdb0, cx, cy, pctx->pitch), color, block_size, pctx->pitch); + break; + + case 0xff: + if (2 == block_size) + { + opcode_0xf8(pctx, cx, cy, block_size, pctx->pitch); + } + else + { + block_size >>= 1; + codec2level(pctx, cx , cy , block_size); + codec2level(pctx, cx + block_size, cy , block_size); + codec2level(pctx, cx , cy + block_size, block_size); + codec2level(pctx, cx + block_size, cy + block_size, block_size); + } + break; + } +} + +static void rle_decode(uint8_t pdest, const uint8_t pinput, const int out_size) +{ + int opcode, color, run_len, remaining = out_size; + + assert(out_size > 0); + + while (remaining) + { + opcode = pinput++; + run_len = (opcode >> 1) + 1; + assert(run_len <= remaining); + + if (opcode & 1) // rle strip + { + color = *pinput++; + memset(pdest, color, run_len); + } + else + { + memcpy(pdest, pinput, run_len); + pinput += run_len; + } + + pdest += run_len; + remaining -= run_len; + } +} + +static void codec5(sanm_ctx* pctx, const uint8_t* pinput, const int decoded_size) +{ + uint8_t* pdest = (uint8_t*) pctx->pdb0; + int npixels = pctx->npixels; + uint16_t pdb0 = pctx->pdb0; + + assert(!(decode_size & 1)); + + rle_decode(pdest, pinput, decoded_size); + + while (npixels--) + { + pdb0 = AV_RL16(pdb0); + ++pdb0; + } +} + +static void codec2(sanm_ctx pctx, const uint8_t* pinput) +{ + const int width = pctx->aligned_width, height = pctx->aligned_height; + int cx, cy; + + pctx->psrc = pinput; + for (cy = 0; cy != height; cy += 8) + { + for (cx = 0; cx != width; cx += 8) + { + codec2level(pctx, cx, cy, 8); + } + } +} + +static void codec8(sanm_ctx* pctx, const uint8_t* pinput, const int decoded_size) +{ + uint16_t* pdest = pctx->pdb0; + uint8_t* pindices = av_malloc(decoded_size); + uint8_t* pidx_cursor = pindices; + long npixels = pctx->npixels; + + rle_decode(pindices, pinput, decoded_size); + + while (npixels--) + { + int index = pidx_cursor++; + pdest++ = AV_RL16(&pctx->pcodebook[index]); + } + + av_free(pindices); +} + +static void copy_output(sanm_ctx pctx, sanm_frame_header pheader) +{ + uint8_t* poutput; + const uint8_t* pinput = (uint8_t*) pctx->pdb0; + + int height = pctx->height; + long inpitch = pctx->pitch * (pheader ? sizeof(pctx->pdb0[0]) : 1); + long outpitch; + + if (pctx->avctx->get_buffer(pctx->avctx, pctx->output)) + { + av_log(pctx->avctx, AV_LOG_ERROR, "sanm decoder: get_buffer() failed.\n"); + return; + } + poutput = pctx->output->data[0]; + outpitch = pctx->output->linesize[0]; + while (height--) + { + memcpy(poutput, pinput, inpitch); + pinput += inpitch; + poutput += outpitch; + } +} + +static void codec1(sanm_ctx* pctx, const uint8_t* pinput, int top, int left, int width, int height) +{ + uint8_t dst = ((uint8_t)pctx->pdb0) + left + top * pctx->pitch; + const uint8_t* end; + int i, j, len, flag, code, val, pos; + for(i = 0; i < height; i++){ + pos = 0; + len = bytestream_get_le16(&pinput); + end = pinput + len; + while(pinput < end){ + code = bytestream_get_byte(&pinput); + flag = code & 1; + code = (code >> 1) + 1; + if(flag){ + val = bytestream_get_byte(&pinput); + if(val) + memset(dst + pos, val, code); + pos += code; + }else{ + for(j = 0; j < code; j++){ + val = bytestream_get_byte(&pinput); + if(val) + dst[pos] = val; + pos++; + } + } + } + dst += pctx->pitch; + } + pctx->rotate_code = 0; +} + +static const int8_t c37_mv[] = { + 0, 0, 1, 0, 2, 0, 3, 0, 5, 0, + 8, 0, 13, 0, 21, 0, -1, 0, -2, 0, + -3, 0, -5, 0, -8, 0, -13, 0, -17, 0, + -21, 0, 0, 1, 1, 1, 2, 1, 3, 1, + 5, 1, 8, 1, 13, 1, 21, 1, -1, 1, + -2, 1, -3, 1, -5, 1, -8, 1, -13, 1, + -17, 1, -21, 1, 0, 2, 1, 2, 2, 2, + 3, 2, 5, 2, 8, 2, 13, 2, 21, 2, + -1, 2, -2, 2, -3, 2, -5, 2, -8, 2, + -13, 2, -17, 2, -21, 2, 0, 3, 1, 3, + 2, 3, 3, 3, 5, 3, 8, 3, 13, 3, + 21, 3, -1, 3, -2, 3, -3, 3, -5, 3, + -8, 3, -13, 3, -17, 3, -21, 3, 0, 5, + 1, 5, 2, 5, 3, 5, 5, 5, 8, 5, + 13, 5, 21, 5, -1, 5, -2, 5, -3, 5, + -5, 5, -8, 5, -13, 5, -17, 5, -21, 5, + 0, 8, 1, 8, 2, 8, 3, 8, 5, 8, + 8, 8, 13, 8, 21, 8, -1, 8, -2, 8, + -3, 8, -5, 8, -8, 8, -13, 8, -17, 8, + -21, 8, 0, 13, 1, 13, 2, 13, 3, 13, + 5, 13, 8, 13, 13, 13, 21, 13, -1, 13, + -2, 13, -3, 13, -5, 13, -8, 13, -13, 13, + -17, 13, -21, 13, 0, 21, 1, 21, 2, 21, + 3, 21, 5, 21, 8, 21, 13, 21, 21, 21, + -1, 21, -2, 21, -3, 21, -5, 21, -8, 21, + -13, 21, -17, 21, -21, 21, 0, -1, 1, -1, + 2, -1, 3, -1, 5, -1, 8, -1, 13, -1, + 21, -1, -1, -1, -2, -1, -3, -1, -5, -1, + -8, -1, -13, -1, -17, -1, -21, -1, 0, -2, + 1, -2, 2, -2, 3, -2, 5, -2, 8, -2, + 13, -2, 21, -2, -1, -2, -2, -2, -3, -2, + -5, -2, -8, -2, -13, -2, -17, -2, -21, -2, + 0, -3, 1, -3, 2, -3, 3, -3, 5, -3, + 8, -3, 13, -3, 21, -3, -1, -3, -2, -3, + -3, -3, -5, -3, -8, -3, -13, -3, -17, -3, + -21, -3, 0, -5, 1, -5, 2, -5, 3, -5, + 5, -5, 8, -5, 13, -5, 21, -5, -1, -5, + -2, -5, -3, -5, -5, -5, -8, -5, -13, -5, + -17, -5, -21, -5, 0, -8, 1, -8, 2, -8, + 3, -8, 5, -8, 8, -8, 13, -8, 21, -8, + -1, -8, -2, -8, -3, -8, -5, -8, -8, -8, + -13, -8, -17, -8, -21, -8, 0, -13, 1, -13, + 2, -13, 3, -13, 5, -13, 8, -13, 13, -13, + 21, -13, -1, -13, -2, -13, -3, -13, -5, -13, + -8, -13, -13, -13, -17, -13, -21, -13, 0, -17, + 1, -17, 2, -17, 3, -17, 5, -17, 8, -17, + 13, -17, 21, -17, -1, -17, -2, -17, -3, -17, + -5, -17, -8, -17, -13, -17, -17, -17, -21, -17, + 0, -21, 1, -21, 2, -21, 3, -21, 5, -21, + 8, -21, 13, -21, 21, -21, -1, -21, -2, -21, + -3, -21, -5, -21, -8, -21, -13, -21, -17, -21, + 0, 0, -8, -29, 8, -29, -18, -25, 17, -25, + 0, -23, -6, -22, 6, -22, -13, -19, 12, -19, + 0, -18, 25, -18, -25, -17, -5, -17, 5, -17, + -10, -15, 10, -15, 0, -14, -4, -13, 4, -13, + 19, -13, -19, -12, -8, -11, -2, -11, 0, -11, + 2, -11, 8, -11, -15, -10, -4, -10, 4, -10, + 15, -10, -6, -9, -1, -9, 1, -9, 6, -9, + -29, -8, -11, -8, -8, -8, -3, -8, 3, -8, + 8, -8, 11, -8, 29, -8, -5, -7, -2, -7, + 0, -7, 2, -7, 5, -7, -22, -6, -9, -6, + -6, -6, -3, -6, -1, -6, 1, -6, 3, -6, + 6, -6, 9, -6, 22, -6, -17, -5, -7, -5, + -4, -5, -2, -5, 0, -5, 2, -5, 4, -5, + 7, -5, 17, -5, -13, -4, -10, -4, -5, -4, + -3, -4, -1, -4, 0, -4, 1, -4, 3, -4, + 5, -4, 10, -4, 13, -4, -8, -3, -6, -3, + -4, -3, -3, -3, -2, -3, -1, -3, 0, -3, + 1, -3, 2, -3, 4, -3, 6, -3, 8, -3, + -11, -2, -7, -2, -5, -2, -3, -2, -2, -2, + -1, -2, 0, -2, 1, -2, 2, -2, 3, -2, + 5, -2, 7, -2, 11, -2, -9, -1, -6, -1, + -4, -1, -3, -1, -2, -1, -1, -1, 0, -1, + 1, -1, 2, -1, 3, -1, 4, -1, 6, -1, + 9, -1, -31, 0, -23, 0, -18, 0, -14, 0, + -11, 0, -7, 0, -5, 0, -4, 0, -3, 0, + -2, 0, -1, 0, 0, -31, 1, 0, 2, 0, + 3, 0, 4, 0, 5, 0, 7, 0, 11, 0, + 14, 0, 18, 0, 23, 0, 31, 0, -9, 1, + -6, 1, -4, 1, -3, 1, -2, 1, -1, 1, + 0, 1, 1, 1, 2, 1, 3, 1, 4, 1, + 6, 1, 9, 1, -11, 2, -7, 2, -5, 2, + -3, 2, -2, 2, -1, 2, 0, 2, 1, 2, + 2, 2, 3, 2, 5, 2, 7, 2, 11, 2, + -8, 3, -6, 3, -4, 3, -2, 3, -1, 3, + 0, 3, 1, 3, 2, 3, 3, 3, 4, 3, + 6, 3, 8, 3, -13, 4, -10, 4, -5, 4, + -3, 4, -1, 4, 0, 4, 1, 4, 3, 4, + 5, 4, 10, 4, 13, 4, -17, 5, -7, 5, + -4, 5, -2, 5, 0, 5, 2, 5, 4, 5, + 7, 5, 17, 5, -22, 6, -9, 6, -6, 6, + -3, 6, -1, 6, 1, 6, 3, 6, 6, 6, + 9, 6, 22, 6, -5, 7, -2, 7, 0, 7, + 2, 7, 5, 7, -29, 8, -11, 8, -8, 8, + -3, 8, 3, 8, 8, 8, 11, 8, 29, 8, + -6, 9, -1, 9, 1, 9, 6, 9, -15, 10, + -4, 10, 4, 10, 15, 10, -8, 11, -2, 11, + 0, 11, 2, 11, 8, 11, 19, 12, -19, 13, + -4, 13, 4, 13, 0, 14, -10, 15, 10, 15, + -5, 17, 5, 17, 25, 17, -25, 18, 0, 18, + -12, 19, 13, 19, -6, 22, 6, 22, 0, 23, + -17, 25, 18, 25, -8, 29, 8, 29, 0, 31, + 0, 0, -6, -22, 6, -22, -13, -19, 12, -19, + 0, -18, -5, -17, 5, -17, -10, -15, 10, -15, + 0, -14, -4, -13, 4, -13, 19, -13, -19, -12, + -8, -11, -2, -11, 0, -11, 2, -11, 8, -11, + -15, -10, -4, -10, 4, -10, 15, -10, -6, -9, + -1, -9, 1, -9, 6, -9, -11, -8, -8, -8, + -3, -8, 0, -8, 3, -8, 8, -8, 11, -8, + -5, -7, -2, -7, 0, -7, 2, -7, 5, -7, + -22, -6, -9, -6, -6, -6, -3, -6, -1, -6, + 1, -6, 3, -6, 6, -6, 9, -6, 22, -6, + -17, -5, -7, -5, -4, -5, -2, -5, -1, -5, + 0, -5, 1, -5, 2, -5, 4, -5, 7, -5, + 17, -5, -13, -4, -10, -4, -5, -4, -3, -4, + -2, -4, -1, -4, 0, -4, 1, -4, 2, -4, + 3, -4, 5, -4, 10, -4, 13, -4, -8, -3, + -6, -3, -4, -3, -3, -3, -2, -3, -1, -3, + 0, -3, 1, -3, 2, -3, 3, -3, 4, -3, + 6, -3, 8, -3, -11, -2, -7, -2, -5, -2, + -4, -2, -3, -2, -2, -2, -1, -2, 0, -2, + 1, -2, 2, -2, 3, -2, 4, -2, 5, -2, + 7, -2, 11, -2, -9, -1, -6, -1, -5, -1, + -4, -1, -3, -1, -2, -1, -1, -1, 0, -1, + 1, -1, 2, -1, 3, -1, 4, -1, 5, -1, + 6, -1, 9, -1, -23, 0, -18, 0, -14, 0, + -11, 0, -7, 0, -5, 0, -4, 0, -3, 0, + -2, 0, -1, 0, 0, -23, 1, 0, 2, 0, + 3, 0, 4, 0, 5, 0, 7, 0, 11, 0, + 14, 0, 18, 0, 23, 0, -9, 1, -6, 1, + -5, 1, -4, 1, -3, 1, -2, 1, -1, 1, + 0, 1, 1, 1, 2, 1, 3, 1, 4, 1, + 5, 1, 6, 1, 9, 1, -11, 2, -7, 2, + -5, 2, -4, 2, -3, 2, -2, 2, -1, 2, + 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, + 5, 2, 7, 2, 11, 2, -8, 3, -6, 3, + -4, 3, -3, 3, -2, 3, -1, 3, 0, 3, + 1, 3, 2, 3, 3, 3, 4, 3, 6, 3, + 8, 3, -13, 4, -10, 4, -5, 4, -3, 4, + -2, 4, -1, 4, 0, 4, 1, 4, 2, 4, + 3, 4, 5, 4, 10, 4, 13, 4, -17, 5, + -7, 5, -4, 5, -2, 5, -1, 5, 0, 5, + 1, 5, 2, 5, 4, 5, 7, 5, 17, 5, + -22, 6, -9, 6, -6, 6, -3, 6, -1, 6, + 1, 6, 3, 6, 6, 6, 9, 6, 22, 6, + -5, 7, -2, 7, 0, 7, 2, 7, 5, 7, + -11, 8, -8, 8, -3, 8, 0, 8, 3, 8, + 8, 8, 11, 8, -6, 9, -1, 9, 1, 9, + 6, 9, -15, 10, -4, 10, 4, 10, 15, 10, + -8, 11, -2, 11, 0, 11, 2, 11, 8, 11, + 19, 12, -19, 13, -4, 13, 4, 13, 0, 14, + -10, 15, 10, 15, -5, 17, 5, 17, 0, 18, + -12, 19, 13, 19, -6, 22, 6, 22, 0, 23, +}; + +static void codec37(sanm_ctx* pctx, const uint8_t* src, int top, int left, int width, int height) +{ + int stride = pctx->pitch; + uint8_t dst; + uint8_t prev; + uint8_t buf; + int compr = src[0]; + int mvoff = src[1]; + int seq = src[2]; + int i, j, k, t; + int flags = src[12]; + int skip_run = 0; + + pctx->rotate_code = 0; + if(((seq & 1) || !(flags & 1)) && (compr >= 3)) + rotate_bufs(pctx, 1); + dst = ((uint8_t)pctx->pdb0) + left + top * stride; + prev = pctx->pdb2; + src += 16; + switch(compr){ + case 0: + for(i = 0; i < height; i++){ + memcpy(dst, src, width); + src += width; + dst += stride; + } + memset(pctx->pdb1, 0, pctx->height * stride); + memset(pctx->pdb2, 0, pctx->height * stride); + break; + case 1: + av_log(NULL,0,"Codec 37 compr 1: TODO\n"); + break; + case 2: + t = width * height; + buf = av_malloc(t+16); + rle_decode(buf, src, t); + src = buf; + for(j = 0; j < height; j++){ + for(i = 0; i < width; i++) + memcpy(dst, src, width); + dst += stride; + src += width; + } + memset(pctx->pdb1, 0, pctx->height * stride); + memset(pctx->pdb2, 0, pctx->height * stride); + av_free(buf); + break; + case 3: + case 4: + if(flags & 4){ + for(j = 0; j < height; j += 4){ + for(i = 0; i < width; i += 4){ + int code; + if(skip_run){ + skip_run--; + for(k = 0; k < 4; k++) + memcpy(dst + i + k*stride, prev + i + k*stride, 4); + continue; + } + code = bytestream_get_byte(&src); + switch(code){ + case 0xFF: + for(k = 0; k < 4; k++) + bytestream_get_buffer(&src, dst + i + k*stride, 4); + break; + case 0xFE: + for(k = 0; k < 4; k++) + memset(dst + i + k*stride, bytestream_get_byte(&src), 4); + break; + case 0xFD: + t = bytestream_get_byte(&src); + for(k = 0; k < 4; k++) + memset(dst + i + k*stride, t, 4); + break; + default: + if(compr == 4 && !code){ + skip_run = bytestream_get_byte(&src) + 1; + i -= 4; + }else{ + int mx, my; + mx = c37_mv[(mvoff*255 + code)*2]; + my = c37_mv[(mvoff*255 + code)*2+1]; + for(k = 0; k < 4; k++) + memcpy(dst + i + k*stride, prev + i + mx + (k + my)*stride, 4); + } + } + } + dst += stride * 4; + prev += stride * 4; + } + }else{ + for(j = 0; j < height; j += 4){ + for(i = 0; i < width; i += 4){ + int code; + if(skip_run){ + skip_run--; + for(k = 0; k < 4; k++) + memcpy(dst + i + k*stride, prev + i + k*stride, 4); + continue; + } + code = bytestream_get_byte(&src); + if(code == 0xFF){ + for(k = 0; k < 4; k++) + bytestream_get_buffer(&src, dst + i + k*stride, 4); + }else if(compr == 4 && !code){ + skip_run = bytestream_get_byte(&src) + 1; + i -= 4; + }else{ + int mx, my; + mx = c37_mv[(mvoff*255 + code)*2]; + my = c37_mv[(mvoff*255 + code)*2+1]; + if(my + j >= 0) + for(k = 0; k < 4; k++) + memcpy(dst + i + k*stride, prev + i + mx + (k + my)*stride, 4); + } + } + dst += stride * 4; + prev += stride * 4; + } + } + break; + } +} + +static void process_block(uint8_t *dst, uint8_t *prev1, uint8_t *prev2, int stride, const uint8_t **src, const uint8_t *tbl, int size) +{ + int code; + int k, t; + + code = bytestream_get_byte(src); + if(code >= 0xF8){ + switch(code){ + case 0xFF: + if(size == 2){ + dst[0] = bytestream_get_byte(src); + dst[1] = bytestream_get_byte(src); + dst[0+stride] = bytestream_get_byte(src); + dst[1+stride] = bytestream_get_byte(src); + }else{ + size >>= 1; + process_block(dst, prev1, prev2, stride, src, tbl, size); + process_block(dst+size, prev1+size, prev2+size, stride, src, tbl, size); + dst += size * stride; + prev1 += size * stride; + prev2 += size * stride; + process_block(dst, prev1, prev2, stride, src, tbl, size); + process_block(dst+size, prev1+size, prev2+size, stride, src, tbl, size); + } + break; + case 0xFE: + t = bytestream_get_byte(src); + for(k = 0; k < size; k++) + memset(dst + k*stride, t, size); + break; + case 0xFD: + { + int code = bytestream_get_byte(src); + int8_t *pglyph = (size == 8) ? p8x8glyphs[code] : p4x4glyphs[code]; + + for(k = 0; k < size; k++) + for(t = 0; t < size; t++) + dst[t + k*stride] = src[0][!(*pglyph++)]; + *src += 2; + } + break; + case 0xFC: + for(k = 0; k < size; k++) + memcpy(dst + k*stride, prev1 + k*stride, size); + break; + default: + t = tbl[code & 7]; + for(k = 0; k < size; k++) + memset(dst + k*stride, t, size); + } + }else{ + int mx = motion_vectors[code][0]; + int my = motion_vectors[code][1]; + for(k = 0; k < size; k++) + memcpy(dst + k*stride, prev2 + mx + (my+k)*stride, size); + } +} + +static void codec47(sanm_ctx* pctx, const uint8_t* pinput, int top, int left, int width, int height) +{ + int compr = pinput[2]; + const uint8_t *src = pinput + 26; + int stride = pctx->pitch; + uint8_t dst = pctx->pdb0 + left + top * stride; + uint8_t prev1 = pctx->pdb1; + uint8_t prev2 = pctx->pdb2; + int i, j, t; + uint8_t buf; + int seq = AV_RL16(pinput); + if(pinput[4] & 1) + src += 0x8080; + if(!seq) + pctx->prev_seq = -1; + switch(compr){ + case 0: + for(j = 0; j < height; j++){ + for(i = 0; i < width; i++) + memcpy(dst, src, width); + dst += stride; + src += width; + } + break; + case 1: + av_log(NULL,0,"Codec 47 compr 1: TODO\n"); + break; + case 2: + if(seq == pctx->prev_seq + 1){ + for(j = 0; j < height; j += 8){ + for(i = 0; i < width; i += 8){ + process_block(dst + i, prev1 + i, prev2 + i, stride, &src, pinput + 8, 8); + } + dst += stride * 8; + prev1 += stride * 8; + prev2 += stride * 8; + } + } + break; + case 3: + memcpy(pctx->pdb0, pctx->pdb2, pctx->pitch * pctx->height); + break; + case 4: + memcpy(pctx->pdb0, pctx->pdb1, pctx->pitch * pctx->height); + break; + case 5: + t = AV_RL32(pinput + 14); + buf = av_malloc(t+16); + rle_decode(buf, src, t); + src = buf; + for(j = 0; j < height; j++){ + for(i = 0; i < width; i++) + memcpy(dst, src, width); + dst += stride; + src += width; + } + av_free(buf); + break; + } + if(seq == pctx->prev_seq + 1) + pctx->rotate_code = pinput[3]; + else + pctx->rotate_code = 0; + pctx->prev_seq = seq; +} + + +static void process_npal(sanm_ctx pctx, const uint8_t pinput) +{ + int i; + for(i = 0; i < 256; i++) + pctx->pal[i] = bytestream_get_be24(&pinput); +} + +static void process_xpal(sanm_ctx pctx, const uint8_t pinput, int size) +{ + int i, j; + + if(size == 6){ + for(i = 0; i < 256; i++){ + uint8_t tmp[4]; + for(j = 0; j < 3; j++){ + int t = (pctx->pal[i] >> (16 - j8)) & 0xFF; + tmp[2-j] = av_clip_uint8((t129 + pctx->delta_pal[i3+j]) / 128); + } + pctx->pal[i] = AV_RL24(tmp); + } + }else{ + pinput += 4; + for(i = 0; i < 768; i++) + pctx->delta_pal[i] = bytestream_get_le16(&pinput); + for(i = 0; i < 256; i++) + pctx->pal[i] = bytestream_get_be24(&pinput); + } +} + +static void process_fobj(sanm_ctx pctx, const uint8_t* pinput) +{ + int i; + int codec, top, left, w, h; + codec = bytestream_get_le16(&pinput); + top = bytestream_get_le16(&pinput); + left = bytestream_get_le16(&pinput); + w = bytestream_get_le16(&pinput); + h = bytestream_get_le16(&pinput); + if(pctx->width < left + w || pctx->height < top + h){ + pctx->avctx->width = left + w; + pctx->avctx->height = top + h; + init_sizes(pctx, left + w, top + h); + init_buffers(pctx); + } + pinput += 4; + switch(codec){ + case 1: + codec1(pctx, pinput, top, left, w, h); + break; + case 37: + codec37(pctx, pinput, top, left, w, h); + break; + case 47: + codec47(pctx, pinput, top, left, w, h); + break; + default: + av_log(NULL,0,"Unknown codec %d\n",codec); + } +} + +static int dispatch_codec1(sanm_ctx* pctx, const uint8_t* pinput, const int isize) +{ + const uint8_t frame_end = pinput + isize; + while(pinput < frame_end - 8){ + int sig = AV_RB32(pinput); + int size = AV_RB32(pinput+4); + pinput += 8; + if(size < 0 || pinput + size > frame_end){ + av_log(NULL,0,"Incorrect size (%d|%X, %d)\n",size,size,frame_end-pinput); + break; + } + switch(sig){ + case MKBETAG('N', 'P', 'A', 'L'): + process_npal(pctx, pinput); + break; + case MKBETAG('F', 'O', 'B', 'J'): + process_fobj(pctx, pinput); + break; + case MKBETAG('X', 'P', 'A', 'L'): + process_xpal(pctx, pinput, size); + break; + } + + pinput += size; + if(size & 1) + pinput++; + } + copy_output(pctx, NULL); + rotate_bufs(pctx, pctx->rotate_code); + memcpy(pctx->output->data[1], pctx->pal, 1024); + return 0; +} + +static int dispatch_codec(sanm_ctx pctx, const uint8_t* pinput) +{ + sanm_frame_header header; + if (read_frame_header(pctx, pinput, &header)) + { + return -1; + } + + if ((pctx->output->key_frame = !header.seq_num)) + { + pctx->output->pict_type = FF_I_TYPE; + fill_db(pctx->pdb1, pctx->npixels, header.bg_color); + fill_db(pctx->pdb2, pctx->npixels, header.bg_color); + } + else + { + pctx->output->pict_type = FF_P_TYPE; + } + + switch (header.codec) + { + case 0: + codec0(pctx, header.pvstream); + break; + + case 2: + codec2(pctx, header.pvstream); + break; + + case 3: + memcpy(pctx->pdb0, pctx->pdb2, pctx->buf_size); + break; + + case 4: + memcpy(pctx->pdb0, pctx->pdb1, pctx->buf_size); + break; + + case 5: + codec5(pctx, header.pvstream, header.rle_output_size); + break; + + case 6: + codec6(pctx, header.pvstream); + break; + + case 8: + codec8(pctx, header.pvstream, pctx->npixels); + break; + + default: + av_log(0, AV_LOG_ERROR, "sanm decoder: subcodec %u is not implemented. video may be garbled until next keyframe.\n", header.codec); + break; + } + + copy_output(pctx, &header); + rotate_bufs(pctx, header.rotate_code); + + return 0; +} + +static int decode_frame(AVCodecContext* pav_ctx, void* poutput, int* poutput_size, uint8_t* pinput, int input_size) +{ + sanm_ctx* pctx = pav_ctx->priv_data; + + if (pctx->output->data[0]) + { + pav_ctx->release_buffer(pav_ctx, pctx->output); + } + + if (!pctx->version && dispatch_codec1(pctx, pinput, input_size)) + { + return -1; + } + + if (pctx->version && dispatch_codec(pctx, pinput)) + { + return -1; + } + + poutput_size = sizeof(AVFrame); + ((AVFrame) poutput) = pctx->output; + + return input_size; +} + +AVCodec sanm_decoder = +{ + "sanm", + CODEC_TYPE_VIDEO, + CODEC_ID_SANM, + sizeof(sanm_ctx), + decode_init, + 0, + decode_end, + decode_frame +}; diff --git a/libavformat/Makefile b/libavformat/Makefile index e16a5ea..2c0696c 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -32,6 +32,7 @@ OBJS-$(CONFIG_AVM2_MUXER) += swfenc.o OBJS-$(CONFIG_AVS_DEMUXER) += avs.o vocdec.o voc.o OBJS-$(CONFIG_BETHSOFTVID_DEMUXER) += bethsoftvid.o OBJS-$(CONFIG_BFI_DEMUXER) += bfi.o +OBJS-$(CONFIG_BINK_DEMUXER) += bink.o OBJS-$(CONFIG_C93_DEMUXER) += c93.o vocdec.o voc.o OBJS-$(CONFIG_CRC_MUXER) += crcenc.o OBJS-$(CONFIG_DAUD_DEMUXER) += daud.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index a8183c2..704da60 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -163,6 +163,7 @@ void av_register_all(void) REGISTER_DEMUXER (RPL, rpl); REGISTER_MUXER (RTP, rtp); REGISTER_DEMUXER (RTSP, rtsp); + REGISTER_DEMUXER (SANM, sanm); REGISTER_DEMUXER (SDP, sdp); #if CONFIG_SDP_DEMUXER av_register_rtp_dynamic_payload_handlers(); diff --git a/libavformat/sanm.c b/libavformat/sanm.c new file mode 100644 index 0000000..5ce56dc --- /dev/null +++ b/libavformat/sanm.c @@ -0,0 +1,395 @@ +/ + * LucasArts Smush v2 demuxer + * Copyright (c) 2006 Cyril Zorin + * + * + * This library 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 of + * the License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + * + / + +/ + * @file sanm.c + * LucasArts Smush v2 demuxer + / + +/ + * Based on http://wiki.multimedia.cx/index.php?title=SANM and + * http://wiki.multimedia.cx/index.php?title=VIMA + / + +#include "avformat.h" +#include "avio.h" + +typedef struct sanm_vinfo +{ + int width, height; + + int nframes, iframe; + size_t stream_index; + int64_t pts; + int subversion; + uint32_t palette[256]; +} sanm_vinfo; + +typedef struct sanm_ainfo +{ + int freq, nchannels; + size_t stream_index; + int64_t pts; + +} sanm_ainfo; + +typedef struct sanm_ctx +{ + sanm_vinfo vinfo; + sanm_ainfo ainfo; + int version; +} sanm_ctx; + +static int sanm_probe(AVProbeData p) +{ + int score = 0; + + if (p->buf_size >= 4 + && (AV_RL32(p->buf) == MKTAG('S', 'A', 'N', 'M') + || AV_RL32(p->buf) == MKTAG('A', 'N', 'I', 'M'))) + { + score = AVPROBE_SCORE_MAX; + } + + return score; +} + +static int read_vinfo1(ByteIOContext* pbuf, sanm_vinfo* pvinfo) +{ + int64_t endpos; + uint32_t size, sig = get_be32(pbuf); + int i; + if (sig != MKBETAG('A', 'H', 'D', 'R')) + { + return -1; + } + + size = get_be32(pbuf); + endpos = url_ftell(pbuf) + size; + + pvinfo->subversion = get_le16(pbuf); + pvinfo->nframes = get_le16(pbuf); + + get_le16(pbuf); // skip pad + pvinfo->width = 0; + pvinfo->height = 0; + + for(i = 0; i < 256; i++) + pvinfo->palette[i] = get_be24(pbuf); + + url_fseek(pbuf, endpos, SEEK_SET); + + return 0; +} + +static int read_vinfo(ByteIOContext* pbuf, sanm_vinfo* pvinfo) +{ + int64_t endpos; + uint32_t size, sig = get_be32(pbuf); + if (sig != MKBETAG('S', 'H', 'D', 'R')) + { + return -1; + } + + size = get_be32(pbuf); + endpos = url_ftell(pbuf) + size; + + get_le16(pbuf); // skip version + + pvinfo->nframes = get_le32(pbuf); + + get_le16(pbuf); // skip pad + pvinfo->width = get_le16(pbuf); + pvinfo->height = get_le16(pbuf); + get_le16(pbuf); // skip pad + + url_fseek(pbuf, endpos, SEEK_SET); + + return 0; +} + +static int read_ainfo(ByteIOContext* pbuf, sanm_ainfo* painfo) +{ + uint32_t sig, size; + int64_t endpos; + int done; + + painfo->nchannels = 0; + + sig = get_be32(pbuf); + if (sig != MKBETAG('F', 'L', 'H', 'D')) + { + return -1; + } + + size = get_be32(pbuf); + endpos = url_ftell(pbuf) + size; + + done = 0; + while (!done) + { + sig = get_be32(pbuf); + size = get_be32(pbuf); + + switch (sig) + { + case MKBETAG('W', 'a', 'v', 'e'): + done = 1; + painfo->freq = get_le32(pbuf); + painfo->nchannels = get_le32(pbuf); + break; + + case MKBETAG('B', 'l', '1', '6'): + url_fseek(pbuf, size, SEEK_CUR); + break; + + default: + done = 1; + break; + } + } + + url_fseek(pbuf, endpos, SEEK_SET); + + return 0; +} + +static int init_vinfo(AVStream* pstream, sanm_ctx pctx) +{ + sanm_vinfo pvinfo = &pctx->vinfo; + + pvinfo->stream_index = pstream->index; + pstream->codec->codec_type = CODEC_TYPE_VIDEO; + pstream->codec->codec_id = CODEC_ID_SANM; + pstream->codec->codec_tag = MKBETAG('S', 'A', 'N', 'M'); + pstream->codec->width = pvinfo->width; + pstream->codec->height = pvinfo->height; + + av_set_pts_info(pstream, 64, 1, 15); + pvinfo->pts = 0; + pvinfo->iframe = 0; + + if(!pctx->version){ + int i; + pstream->codec->extradata = av_malloc(1024+2); + pstream->codec->extradata_size = 1024 + 2; + AV_WL16(pstream->codec->extradata, pvinfo->subversion); + for(i = 0; i < 256; i++) + AV_WL32(pstream->codec->extradata + 2 + i4, pvinfo->palette[i]); + } + + return 0; +} + +/ +static int init_ainfo(AVStream* pstream, sanm_ainfo* painfo) +{ + painfo->stream_index = pstream->index; + pstream->codec->codec_type = CODEC_TYPE_AUDIO; + pstream->codec->codec_id = CODEC_ID_VIMA; + pstream->codec->codec_tag = 0; + pstream->codec->channels = painfo->nchannels; + pstream->codec->sample_rate = painfo->freq; + pstream->codec->bits_per_sample = 16; + + av_set_pts_info(pstream, 64, 1, 15); + painfo->pts = 0; + + return 0; +} +/ + +static int check_preamble(ByteIOContext pbuf) +{ + uint32_t movie_sig = get_be32(pbuf); + switch(movie_sig){ + case MKBETAG('A', 'N', 'I', 'M'): + return 0; + case MKBETAG('S', 'A', 'N', 'M'): + return 1; + default: + return -1; + } +} + +static int sanm_read_header(AVFormatContext* paf_ctx, AVFormatParameters* pparams) +{ + ByteIOContext* pbuf = paf_ctx->pb; + sanm_ctx* pctx = paf_ctx->priv_data; + AVStream* pvstream; + AVStream* pastream; + + pctx->version = check_preamble(pbuf); + if (pctx->version == -1) + { + av_log(NULL,0,"Wrong magic\n"); + return -1; + } + + get_be32(pbuf); // skip movie size + + if (!pctx->version && read_vinfo1(pbuf, &pctx->vinfo)) + { + av_log(NULL,0,"wrong vinfo\n"); + return -1; + } + if (pctx->version && read_vinfo(pbuf, &pctx->vinfo)) + { + av_log(NULL,0,"wrong vinfo\n"); + return -1; + } + + pvstream = av_new_stream(paf_ctx, 0); + if (!pvstream) + { + return AVERROR_NOMEM; + } + + if (init_vinfo(pvstream, pctx)) + { + av_log(NULL,0,"error initing vinfo\n"); + return -1; + } + + if (pctx->version && read_ainfo(pbuf, &pctx->ainfo)) + { + av_log(NULL,0,"wrong ainfo\n"); + return -1; + } + + /* + if (pctx->ainfo.nchannels) + { + pastream = av_new_stream(paf_ctx, 0); + if (!pastream) + { + return AVERROR_NOMEM; + } + + if (init_ainfo(pastream, &pctx->ainfo)) + { + return -1; + } + } + / + + return 0; +} + +static int sanm_read_packet(AVFormatContext paf_ctx, AVPacket* ppacket) +{ + sanm_ctx* pctx = paf_ctx->priv_data; + ByteIOContext* pbuf = paf_ctx->pb; + uint32_t sig, size; + int64_t endpos; + int done; + + done = 0; + while (!done) + { + if (url_feof(pbuf)) + { + return pctx->vinfo.iframe == pctx->vinfo.nframes ? -EIO : AVERROR_IO; + } + + sig = get_be32(pbuf); + size = get_be32(pbuf); + + endpos = url_ftell(pbuf) + size; + + switch (sig) + { + case MKBETAG('F', 'R', 'M', 'E'): + ++pctx->vinfo.iframe; + if(!pctx->version){ + if (size != av_get_packet(pbuf, ppacket, size)) + { + return AVERROR_IO; + } + + ppacket->stream_index = pctx->vinfo.stream_index; + ppacket->pts = pctx->vinfo.pts; + ++pctx->vinfo.pts; + + done = 1; + } + break; + + case MKBETAG('B', 'l', '1', '6'): + if (size != av_get_packet(pbuf, ppacket, size)) + { + return AVERROR_IO; + } + + ppacket->stream_index = pctx->vinfo.stream_index; + ppacket->pts = pctx->vinfo.pts; + ++pctx->vinfo.pts; + + done = 1; + break; + + /* TODO: uncomment the below code when there is audio api + support for larger/dynamic frame sizes. / +/ + case MKBETAG('W', 'a', 'v', 'e'): + if (av_new_packet(ppacket, size + FF_INPUT_BUFFER_PADDING_SIZE)) + { + return AVERROR_NOMEM; + } + + if (get_buffer(pbuf, ppacket->data, size) < size) + { + return AVERROR_IO; + } + + ppacket->stream_index = pctx->ainfo.stream_index; + ppacket->pts = pctx->ainfo.pts; + ++pctx->ainfo.pts; + + done = 1; + break; +/ + + default: + url_fseek(pbuf, size, SEEK_CUR); + break; + } + } + + return 0; +} + +static int sanm_read_close(AVFormatContext paf_ctx) +{ + return 0; +} + +AVInputFormat sanm_demuxer = +{ + "sanm", + "LucasArts Smush", + sizeof(sanm_ctx), + sanm_probe, + sanm_read_header, + sanm_read_packet, + sanm_read_close +};
1.7.1
--------------090207080306080806080301--
- Previous message: [FFmpeg-devel] [PATCH] Disable symbol versioning on some BSDs
- Next message: [FFmpeg-devel] [Toy] LucasArts SMUSH demuxer and decoder
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]