1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-08-04 22:03:09 +02:00

avcodec/sanm: implement STOR/FTCH for ANIMv1

Handle STOR/FTCH the same way the RA1 game engine does:
On STOR, save the next following FOBJ (not the decoded image)
in a buffer; decode it on FTCH.
The RA1 codecs and the fobj handler now take an explicit
GetByteContext in order to be able to replay stored data.

Used extensively by Rebel Assault 1 for e.g. backgrounds and
the cockpit overlay.

For ANIMv2 keep the current system to store the decoded image, as
replaying a FOBJ would not work with codecs37/47/48 due to sequence
violations.

Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
This commit is contained in:
Manuel Lauss
2025-03-14 22:17:21 +01:00
parent 8a04526080
commit b48bd23321

View File

@ -598,19 +598,17 @@ static void codec4_gen_tiles(SANMVideoContext *ctx, uint16_t param1)
} }
static int codec4_load_tiles(SANMVideoContext *ctx, uint16_t param2, uint8_t clr) static int codec4_load_tiles(SANMVideoContext *ctx, GetByteContext *gb,
uint16_t param2, uint8_t clr)
{ {
uint8_t c, *dst = (uint8_t *)&(ctx->c4tbl[1][0][0]); uint8_t c, *dst = (uint8_t *)&(ctx->c4tbl[1][0][0]);
uint32_t loop = param2 * 8; uint32_t loop = param2 * 8;
if (param2 > 256) if ((param2 > 256) || (bytestream2_get_bytes_left(gb) < loop))
return AVERROR_INVALIDDATA;
if (bytestream2_get_bytes_left(&ctx->gb) < loop)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
while (loop--) { while (loop--) {
c = bytestream2_get_byteu(&ctx->gb); c = bytestream2_get_byteu(gb);
*dst++ = (c >> 4) + clr; *dst++ = (c >> 4) + clr;
*dst++ = (c & 0xf) + clr; *dst++ = (c & 0xf) + clr;
} }
@ -671,8 +669,8 @@ static av_cold int decode_end(AVCodecContext *avctx)
return 0; return 0;
} }
static int old_codec4(SANMVideoContext *ctx, int top, int left, int w, int h, static int old_codec4(SANMVideoContext *ctx, GetByteContext *gb, int top, int left,
uint8_t param, uint16_t param2, int codec) int w, int h, uint8_t param, uint16_t param2, int codec)
{ {
const uint16_t p = ctx->pitch; const uint16_t p = ctx->pitch;
const uint32_t maxpxo = ctx->height * p; const uint32_t maxpxo = ctx->height * p;
@ -688,7 +686,7 @@ static int old_codec4(SANMVideoContext *ctx, int top, int left, int w, int h,
ctx->c4param = param; ctx->c4param = param;
} }
if (param2 > 0) { if (param2 > 0) {
ret = codec4_load_tiles(ctx, param2, param); ret = codec4_load_tiles(ctx, gb, param2, param);
if (ret) if (ret)
return ret; return ret;
} }
@ -702,9 +700,9 @@ static int old_codec4(SANMVideoContext *ctx, int top, int left, int w, int h,
pxoff = j + left + ((top + i) * p); pxoff = j + left + ((top + i) * p);
if (param2 > 0) { if (param2 > 0) {
if (bits == 0) { if (bits == 0) {
if (bytestream2_get_bytes_left(&ctx->gb) < 1) if (bytestream2_get_bytes_left(gb) < 1)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
mask = bytestream2_get_byteu(&ctx->gb); mask = bytestream2_get_byteu(gb);
bits = 8; bits = 8;
} }
bit = !!(mask & 0x80); bit = !!(mask & 0x80);
@ -714,9 +712,9 @@ static int old_codec4(SANMVideoContext *ctx, int top, int left, int w, int h,
bit = 0; bit = 0;
} }
if (bytestream2_get_bytes_left(&ctx->gb) < 1) if (bytestream2_get_bytes_left(gb) < 1)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
idx = bytestream2_get_byteu(&ctx->gb); idx = bytestream2_get_byteu(gb);
if ((bit == 0) && (idx == 0x80) && (codec != 5)) if ((bit == 0) && (idx == 0x80) && (codec != 5))
continue; continue;
@ -756,23 +754,23 @@ static int old_codec4(SANMVideoContext *ctx, int top, int left, int w, int h,
return 0; return 0;
} }
static int rle_decode(SANMVideoContext *ctx, uint8_t *dst, const int out_size) static int rle_decode(SANMVideoContext *ctx, GetByteContext *gb, uint8_t *dst, const int out_size)
{ {
int opcode, color, run_len, left = out_size; int opcode, color, run_len, left = out_size;
while (left > 0) { while (left > 0) {
opcode = bytestream2_get_byte(&ctx->gb); opcode = bytestream2_get_byte(gb);
run_len = (opcode >> 1) + 1; run_len = (opcode >> 1) + 1;
if (run_len > left || bytestream2_get_bytes_left(&ctx->gb) <= 0) if (run_len > left || bytestream2_get_bytes_left(gb) <= 0)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
if (opcode & 1) { if (opcode & 1) {
color = bytestream2_get_byte(&ctx->gb); color = bytestream2_get_byte(gb);
memset(dst, color, run_len); memset(dst, color, run_len);
} else { } else {
if (bytestream2_get_bytes_left(&ctx->gb) < run_len) if (bytestream2_get_bytes_left(gb) < run_len)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
bytestream2_get_bufferu(&ctx->gb, dst, run_len); bytestream2_get_bufferu(gb, dst, run_len);
} }
dst += run_len; dst += run_len;
@ -782,8 +780,8 @@ static int rle_decode(SANMVideoContext *ctx, uint8_t *dst, const int out_size)
return 0; return 0;
} }
static int old_codec23(SANMVideoContext *ctx, int top, int left, int width, static int old_codec23(SANMVideoContext *ctx, GetByteContext *gb, int top, int left,
int height, uint8_t param, uint16_t param2) int width, int height, uint8_t param, uint16_t param2)
{ {
const uint32_t maxpxo = ctx->height * ctx->pitch; const uint32_t maxpxo = ctx->height * ctx->pitch;
uint8_t *dst, lut[256], c; uint8_t *dst, lut[256], c;
@ -795,30 +793,30 @@ static int old_codec23(SANMVideoContext *ctx, int top, int left, int width,
for (i = 0; i < 256; i++) for (i = 0; i < 256; i++)
lut[i] = (i + param + 0xd0) & 0xff; lut[i] = (i + param + 0xd0) & 0xff;
} else if (param2 == 256) { } else if (param2 == 256) {
if (bytestream2_get_bytes_left(&ctx->gb) < 256) if (bytestream2_get_bytes_left(gb) < 256)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
bytestream2_get_bufferu(&ctx->gb, ctx->c23lut, 256); bytestream2_get_bufferu(gb, ctx->c23lut, 256);
} else if (param2 < 256) { } else if (param2 < 256) {
for (i = 0; i < 256; i++) for (i = 0; i < 256; i++)
lut[i] = (i + param2) & 0xff; lut[i] = (i + param2) & 0xff;
} else { } else {
memcpy(lut, ctx->c23lut, 256); memcpy(lut, ctx->c23lut, 256);
} }
if (bytestream2_get_bytes_left(&ctx->gb) < 1) if (bytestream2_get_bytes_left(gb) < 1)
return 0; /* some c23 frames just set up the LUT */ return 0; /* some c23 frames just set up the LUT */
dst = (uint8_t *)ctx->frm0; dst = (uint8_t *)ctx->frm0;
for (i = 0; i < height; i++) { for (i = 0; i < height; i++) {
if (bytestream2_get_bytes_left(&ctx->gb) < 2) if (bytestream2_get_bytes_left(gb) < 2)
return 0; return 0;
pxoff = left + ((top + i) * ctx->pitch); pxoff = left + ((top + i) * ctx->pitch);
k = bytestream2_get_le16u(&ctx->gb); k = bytestream2_get_le16u(gb);
sk = 1; sk = 1;
pc = 0; pc = 0;
while (k > 0 && pc <= width) { while (k > 0 && pc <= width) {
if (bytestream2_get_bytes_left(&ctx->gb) < 1) if (bytestream2_get_bytes_left(gb) < 1)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
j = bytestream2_get_byteu(&ctx->gb); j = bytestream2_get_byteu(gb);
if (sk) { if (sk) {
pxoff += j; pxoff += j;
pc += j; pc += j;
@ -838,8 +836,8 @@ static int old_codec23(SANMVideoContext *ctx, int top, int left, int width,
return 0; return 0;
} }
static int old_codec21(SANMVideoContext *ctx, int top, int left, int width, static int old_codec21(SANMVideoContext *ctx, GetByteContext *gb, int top, int left,
int height) int width, int height)
{ {
const uint32_t maxpxo = ctx->height * ctx->pitch; const uint32_t maxpxo = ctx->height * ctx->pitch;
uint8_t *dst = (uint8_t *)ctx->frm0, c; uint8_t *dst = (uint8_t *)ctx->frm0, c;
@ -847,25 +845,25 @@ static int old_codec21(SANMVideoContext *ctx, int top, int left, int width,
dst = (uint8_t *)ctx->frm0; dst = (uint8_t *)ctx->frm0;
for (i = 0; i < height; i++) { for (i = 0; i < height; i++) {
if (bytestream2_get_bytes_left(&ctx->gb) < 2) if (bytestream2_get_bytes_left(gb) < 2)
return 0; return 0;
pxoff = left + ((top + i) * ctx->pitch); pxoff = left + ((top + i) * ctx->pitch);
k = bytestream2_get_le16u(&ctx->gb); k = bytestream2_get_le16u(gb);
sk = 1; sk = 1;
pc = 0; pc = 0;
while (k > 0 && pc <= width) { while (k > 0 && pc <= width) {
if (bytestream2_get_bytes_left(&ctx->gb) < 2) if (bytestream2_get_bytes_left(gb) < 2)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
j = bytestream2_get_le16u(&ctx->gb); j = bytestream2_get_le16u(gb);
k -= 2; k -= 2;
if (sk) { if (sk) {
pxoff += j; pxoff += j;
pc += j; pc += j;
} else { } else {
if (bytestream2_get_bytes_left(&ctx->gb) < (j + 1)) if (bytestream2_get_bytes_left(gb) < (j + 1))
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
do { do {
c = bytestream2_get_byteu(&ctx->gb); c = bytestream2_get_byteu(gb);
if (pxoff >=0 && pxoff < maxpxo) { if (pxoff >=0 && pxoff < maxpxo) {
*(dst + pxoff) = c; *(dst + pxoff) = c;
} }
@ -881,7 +879,7 @@ static int old_codec21(SANMVideoContext *ctx, int top, int left, int width,
return 0; return 0;
} }
static int old_codec1(SANMVideoContext *ctx, int top, static int old_codec1(SANMVideoContext *ctx, GetByteContext *gb, int top,
int left, int width, int height, int opaque) int left, int width, int height, int opaque)
{ {
int i, j, len, flag, code, val, end, pxoff; int i, j, len, flag, code, val, end, pxoff;
@ -889,22 +887,22 @@ static int old_codec1(SANMVideoContext *ctx, int top,
uint8_t *dst = (uint8_t *)ctx->frm0; uint8_t *dst = (uint8_t *)ctx->frm0;
for (i = 0; i < height; i++) { for (i = 0; i < height; i++) {
if (bytestream2_get_bytes_left(&ctx->gb) < 2) if (bytestream2_get_bytes_left(gb) < 2)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
len = bytestream2_get_le16u(&ctx->gb); len = bytestream2_get_le16u(gb);
end = bytestream2_tell(&ctx->gb) + len; end = bytestream2_tell(gb) + len;
pxoff = left + ((top + i) * ctx->pitch); pxoff = left + ((top + i) * ctx->pitch);
while (bytestream2_tell(&ctx->gb) < end) { while (bytestream2_tell(gb) < end) {
if (bytestream2_get_bytes_left(&ctx->gb) < 2) if (bytestream2_get_bytes_left(gb) < 2)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
code = bytestream2_get_byteu(&ctx->gb); code = bytestream2_get_byteu(gb);
flag = code & 1; flag = code & 1;
code = (code >> 1) + 1; code = (code >> 1) + 1;
if (flag) { if (flag) {
val = bytestream2_get_byteu(&ctx->gb); val = bytestream2_get_byteu(gb);
if (val || opaque) { if (val || opaque) {
for (j = 0; j < code; j++) { for (j = 0; j < code; j++) {
if (pxoff >= 0 && pxoff < maxpxo) if (pxoff >= 0 && pxoff < maxpxo)
@ -915,10 +913,10 @@ static int old_codec1(SANMVideoContext *ctx, int top,
pxoff += code; pxoff += code;
} }
} else { } else {
if (bytestream2_get_bytes_left(&ctx->gb) < code) if (bytestream2_get_bytes_left(gb) < code)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
for (j = 0; j < code; j++) { for (j = 0; j < code; j++) {
val = bytestream2_get_byteu(&ctx->gb); val = bytestream2_get_byteu(gb);
if ((pxoff >= 0) && (pxoff < maxpxo) && (val || opaque)) if ((pxoff >= 0) && (pxoff < maxpxo) && (val || opaque))
*(dst + pxoff) = val; *(dst + pxoff) = val;
pxoff++; pxoff++;
@ -931,16 +929,16 @@ static int old_codec1(SANMVideoContext *ctx, int top,
return 0; return 0;
} }
static int old_codec2(SANMVideoContext *ctx, int top, static int old_codec2(SANMVideoContext *ctx, GetByteContext *gb, int top,
int left, int width, int height) int left, int width, int height)
{ {
uint8_t *dst = (uint8_t *)ctx->frm0, col; uint8_t *dst = (uint8_t *)ctx->frm0, col;
int16_t xpos = left, ypos = top; int16_t xpos = left, ypos = top;
while (bytestream2_get_bytes_left(&ctx->gb) > 3) { while (bytestream2_get_bytes_left(gb) > 3) {
xpos += bytestream2_get_le16u(&ctx->gb); xpos += bytestream2_get_le16u(gb);
ypos += bytestream2_get_byteu(&ctx->gb); ypos += bytestream2_get_byteu(gb);
col = bytestream2_get_byteu(&ctx->gb); col = bytestream2_get_byteu(gb);
if (xpos >= 0 && ypos >= 0 && if (xpos >= 0 && ypos >= 0 &&
xpos < ctx->width && ypos < ctx->height) { xpos < ctx->width && ypos < ctx->height) {
*(dst + xpos + ypos * ctx->pitch) = col; *(dst + xpos + ypos * ctx->pitch) = col;
@ -1073,7 +1071,7 @@ static int old_codec37(SANMVideoContext *ctx, int width, int height)
} }
break; break;
case 2: case 2:
if (rle_decode(ctx, dst, decoded_size)) if (rle_decode(ctx, &ctx->gb, dst, decoded_size))
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
memset(ctx->frm2, 0, ctx->frm2_size); memset(ctx->frm2, 0, ctx->frm2_size);
break; break;
@ -1343,7 +1341,7 @@ static int old_codec47(SANMVideoContext *ctx, int width, int height)
memcpy(ctx->frm0, ctx->frm1, ctx->pitch * ctx->height); memcpy(ctx->frm0, ctx->frm1, ctx->pitch * ctx->height);
break; break;
case 5: case 5:
if (rle_decode(ctx, dst, decoded_size)) if (rle_decode(ctx, &ctx->gb, dst, decoded_size))
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
break; break;
default: default:
@ -1550,7 +1548,7 @@ static int old_codec48(SANMVideoContext *ctx, int width, int height)
} }
break; break;
case 2: case 2:
if (rle_decode(ctx, dst, decoded_size)) if (rle_decode(ctx, &ctx->gb, dst, decoded_size))
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
break; break;
case 3: case 3:
@ -1581,17 +1579,20 @@ static int old_codec48(SANMVideoContext *ctx, int width, int height)
return 0; return 0;
} }
static int process_frame_obj(SANMVideoContext *ctx) static int process_frame_obj(SANMVideoContext *ctx, GetByteContext *gb)
{ {
uint16_t parm2; uint16_t w, h, parm2;
uint8_t codec = bytestream2_get_byteu(&ctx->gb); uint8_t codec, param;
uint8_t param = bytestream2_get_byteu(&ctx->gb); int16_t left, top;
int16_t left = bytestream2_get_le16u(&ctx->gb);
int16_t top = bytestream2_get_le16u(&ctx->gb); codec = bytestream2_get_byteu(gb);
uint16_t w = bytestream2_get_le16u(&ctx->gb); param = bytestream2_get_byteu(gb);
uint16_t h = bytestream2_get_le16u(&ctx->gb); left = bytestream2_get_le16u(gb);
bytestream2_skip(&ctx->gb, 2); top = bytestream2_get_le16u(gb);
parm2 = bytestream2_get_le16u(&ctx->gb); w = bytestream2_get_le16u(gb);
h = bytestream2_get_le16u(gb);
bytestream2_skip(gb, 2);
parm2 = bytestream2_get_le16u(gb);
if (w < 1 || h < 1 || w > 800 || h > 600 || left > 800 || top > 600) { if (w < 1 || h < 1 || w > 800 || h > 600 || left > 800 || top > 600) {
av_log(ctx->avctx, AV_LOG_WARNING, av_log(ctx->avctx, AV_LOG_WARNING,
@ -1658,18 +1659,18 @@ static int process_frame_obj(SANMVideoContext *ctx)
switch (codec) { switch (codec) {
case 1: case 1:
case 3: case 3:
return old_codec1(ctx, top, left, w, h, codec == 3); return old_codec1(ctx, gb, top, left, w, h, codec == 3);
case 2: case 2:
return old_codec2(ctx, top, left, w, h); return old_codec2(ctx, gb, top, left, w, h);
case 4: case 4:
case 5: case 5:
case 33: case 33:
case 34: case 34:
return old_codec4(ctx, top, left, w, h, param, parm2, codec); return old_codec4(ctx, gb, top, left, w, h, param, parm2, codec);
case 21: case 21:
return old_codec21(ctx, top, left, w, h); return old_codec21(ctx, gb, top, left, w, h);
case 23: case 23:
return old_codec23(ctx, top, left, w, h, param, parm2); return old_codec23(ctx, gb, top, left, w, h, param, parm2);
case 37: case 37:
return old_codec37(ctx, w, h); return old_codec37(ctx, w, h);
case 47: case 47:
@ -1683,6 +1684,55 @@ static int process_frame_obj(SANMVideoContext *ctx)
} }
} }
static int process_ftch(SANMVideoContext *ctx, int size)
{
uint8_t *sf = ctx->stored_frame;
int xoff, yoff, left, top, ret;
GetByteContext gb;
uint32_t sz;
/* FTCH defines additional x/y offsets */
if (size != 12) {
if (bytestream2_get_bytes_left(&ctx->gb) < 6)
return AVERROR_INVALIDDATA;
bytestream2_skip(&ctx->gb, 2);
xoff = bytestream2_get_le16u(&ctx->gb);
yoff = bytestream2_get_le16u(&ctx->gb);
} else {
if (bytestream2_get_bytes_left(&ctx->gb) < 12)
return AVERROR_INVALIDDATA;
bytestream2_skip(&ctx->gb, 4);
xoff = bytestream2_get_be32u(&ctx->gb);
yoff = bytestream2_get_be32u(&ctx->gb);
}
sz = *(uint32_t *)(sf + 0);
if ((sz > 0) && (sz <= ctx->stored_frame_size - 4)) {
/* add the FTCH offsets to the left/top values of the stored FOBJ */
left = av_le2ne16(*(int16_t *)(sf + 4 + 2));
top = av_le2ne16(*(int16_t *)(sf + 4 + 4));
*(int16_t *)(sf + 4 + 2) = av_le2ne16(left + xoff);
*(int16_t *)(sf + 4 + 4) = av_le2ne16(top + yoff);
/* decode the stored FOBJ */
bytestream2_init(&gb, sf + 4, sz);
ret = process_frame_obj(ctx, &gb);
/* now restore the original left/top values again */
*(int16_t *)(sf + 4 + 2) = av_le2ne16(left);
*(int16_t *)(sf + 4 + 4) = av_le2ne16(top);
} else {
/* this happens a lot in RA1: The individual files are meant to
* be played in sequence, with some referencing objects STORed
* by previous files, e.g. the cockpit codec21 object in RA1 LVL8.
* But spamming the log with errors is also not helpful, so
* here we simply ignore this case.
*/
ret = 0;
}
return ret;
}
static int process_xpal(SANMVideoContext *ctx, int size) static int process_xpal(SANMVideoContext *ctx, int size)
{ {
int16_t *dp = ctx->delta_pal; int16_t *dp = ctx->delta_pal;
@ -1993,7 +2043,7 @@ static int decode_5(SANMVideoContext *ctx)
#endif #endif
uint8_t *dst = (uint8_t*)ctx->frm0; uint8_t *dst = (uint8_t*)ctx->frm0;
if (rle_decode(ctx, dst, ctx->buf_size)) if (rle_decode(ctx, &ctx->gb, dst, ctx->buf_size))
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
#if HAVE_BIGENDIAN #if HAVE_BIGENDIAN
@ -2036,7 +2086,7 @@ static int decode_8(SANMVideoContext *ctx)
} }
rsrc = ctx->rle_buf; rsrc = ctx->rle_buf;
if (rle_decode(ctx, rsrc, npixels)) if (rle_decode(ctx, &ctx->gb, rsrc, npixels))
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
while (npixels--) while (npixels--)
@ -2163,9 +2213,32 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
case MKBETAG('F', 'O', 'B', 'J'): case MKBETAG('F', 'O', 'B', 'J'):
if (size < 16) if (size < 16)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
if (ret = process_frame_obj(ctx)) if (ret = process_frame_obj(ctx, &ctx->gb))
return ret; return ret;
have_img = 1; have_img = 1;
/* STOR: for ANIMv0/1 store the whole FOBJ datablock, as it
* needs to be replayed on FTCH, since none of the codecs
* it uses work on the full buffer.
* For ANIMv2, it's enough to store the current framebuffer.
*/
if (to_store) {
to_store = 0;
if (ctx->subversion < 2) {
if (size + 4 <= ctx->stored_frame_size) {
int pos2 = bytestream2_tell(&ctx->gb);
bytestream2_seek(&ctx->gb, pos, SEEK_SET);
*(uint32_t *)(ctx->stored_frame) = size;
bytestream2_get_bufferu(&ctx->gb, ctx->stored_frame + 4, size);
bytestream2_seek(&ctx->gb, pos2, SEEK_SET);
} else {
av_log(avctx, AV_LOG_ERROR, "FOBJ too large for STOR\n");
ret = AVERROR(ENOMEM);
}
} else {
memcpy(ctx->stored_frame, ctx->frm0, ctx->buf_size);
}
}
break; break;
case MKBETAG('X', 'P', 'A', 'L'): case MKBETAG('X', 'P', 'A', 'L'):
if (ret = process_xpal(ctx, size)) if (ret = process_xpal(ctx, size))
@ -2175,7 +2248,12 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
to_store = 1; to_store = 1;
break; break;
case MKBETAG('F', 'T', 'C', 'H'): case MKBETAG('F', 'T', 'C', 'H'):
memcpy(ctx->frm0, ctx->stored_frame, ctx->buf_size); if (ctx->subversion < 2) {
if (ret = process_ftch(ctx, size))
return ret;
} else {
memcpy(ctx->frm0, ctx->stored_frame, ctx->buf_size);
}
have_img = 1; have_img = 1;
break; break;
default: default:
@ -2196,8 +2274,6 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
bytestream2_seek(&ctx->gb, pos + size, SEEK_SET); bytestream2_seek(&ctx->gb, pos + size, SEEK_SET);
} }
} }
if (to_store)
memcpy(ctx->stored_frame, ctx->frm0, ctx->buf_size);
if (have_img) { if (have_img) {
if ((ret = copy_output(ctx, NULL))) if ((ret = copy_output(ctx, NULL)))