Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH 00/12] avcodec/sanm: various improvements
@ 2025-03-13 11:14 Manuel Lauss
  2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 01/12] avcodec/sanm: better frame size detection for old codecs Manuel Lauss
                   ` (11 more replies)
  0 siblings, 12 replies; 13+ messages in thread
From: Manuel Lauss @ 2025-03-13 11:14 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Manuel Lauss

This patchset improves especially support for SMUSHv1 (Rebel Assault 1),
adds missing codecs and functions and improves reliability of SMUSHv1/v2.

#1 improves frame size detection.
#3 changes the left/top values to signed, as RA1 makes heavy use of
   negative offsets.
#4 adds workarounds for RA1 oddities.
#5-10 add missing codecs and fix existing ones.
#11-12 implement the store/fetch feature for RA1 codecs:
  the RA1 engine stores the raw frame object data in an aux
  buffer and replays that when necessary.  Is required for
  codecs1-34 since they only work on parts of the buffer,
  and the existing scheme breaks the visuals here.

This patchset makes almost all RA1 videos now playable with
generally correct content, although some hiccups remain.

Manuel Lauss (12):
  avcodec/sanm: better frame size detection for old codecs
  avcodec/sanm: disable left/top for fullscreen codecs
  avcodec/sanm: FOBJ left/top are signed values
  avcodec/sanm: misc fixes
  avcodec/sanm: fix codec3
  avcodec/sanm: codec2 support
  avcodec/sanm: codec23 decoder
  avcodec/sanm: codec21 decoder
  avcodec/sanm: codec4/5/33/34 decoder
  avcodec/sanm: codec37: reimplement comp4
  avcodec/sanm: change GetByteContext member to pointer
  avcodec/sanm: properly implement STOR/FTCH for ANIMv1

 libavcodec/sanm.c | 949 ++++++++++++++++++++++++++++++++++------------
 1 file changed, 717 insertions(+), 232 deletions(-)

-- 
2.48.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [FFmpeg-devel] [PATCH 01/12] avcodec/sanm: better frame size detection for old codecs
  2025-03-13 11:14 [FFmpeg-devel] [PATCH 00/12] avcodec/sanm: various improvements Manuel Lauss
@ 2025-03-13 11:14 ` Manuel Lauss
  2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 02/12] avcodec/sanm: disable left/top for fullscreen codecs Manuel Lauss
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Manuel Lauss @ 2025-03-13 11:14 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Manuel Lauss

The size of the video frame (FOBJ) of the old codecs (ANIMv0/1/2) can
very reliably be determined:
- ANIMv0/1 (=Rebel Assault 1) uses a 384x242 internal buffer for
  everything.  The codec parameters only describe the size and offset
  of the specific FOBJ on that buffer.
- ANIMv2 titles usually use one of the fullscreen codecs (37/47/48)
  as first FOBJ, and their dimensions can generally be trusted.
- RA2 uses 424x260 as internal buffer, use that if encountered.
- ignore sizes smaller than 2x2 or larger than 800x600.
- some game videos have an initial fobj with either 1x1 or -1x-1
  pixels in size, ignore them with a warning (Full Throttle
  and the Rebel Assault 2 xxRETRY.SAN videos).

Once a known/valid dimension set has been discovered, use it and
don't change it for subsequent FOBJs, rather clamp the large frame
to the determined dimensions.

Tested with RA1, RA2, Full Throttle, Dig, Outlaws, SotE and MotS
videos.

Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
 libavcodec/sanm.c | 63 ++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 49 insertions(+), 14 deletions(-)

diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index a4f0a28c7c..0795d664fa 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -264,7 +264,7 @@ typedef struct SANMVideoContext {
     AVCodecContext *avctx;
     GetByteContext gb;
 
-    int version, subversion;
+    int version, subversion, have_dimensions;
     uint32_t pal[PALETTE_SIZE];
     int16_t delta_pal[PALETTE_DELTA];
 
@@ -1243,21 +1243,56 @@ static int process_frame_obj(SANMVideoContext *ctx)
     uint16_t w     = bytestream2_get_le16u(&ctx->gb);
     uint16_t h     = bytestream2_get_le16u(&ctx->gb);
 
-    if (!w || !h) {
-        av_log(ctx->avctx, AV_LOG_ERROR, "Dimensions are invalid.\n");
-        return AVERROR_INVALIDDATA;
+    if (w < 1 || h < 1 || w > 800 || h > 600 || left > 800 || top > 600) {
+        av_log(ctx->avctx, AV_LOG_WARNING,
+               "ignoring invalid fobj dimensions: c%d %d %d @ %d %d\n",
+               codec, w, h, left, top);
+        return 0;
     }
 
-    if (ctx->width < left + w || ctx->height < top + h) {
-        int ret = ff_set_dimensions(ctx->avctx, FFMAX(left + w, ctx->width),
-                                    FFMAX(top + h, ctx->height));
-        if (ret < 0)
-            return ret;
-        init_sizes(ctx, FFMAX(left + w, ctx->width),
-                   FFMAX(top + h, ctx->height));
-        if (init_buffers(ctx)) {
-            av_log(ctx->avctx, AV_LOG_ERROR, "Error resizing buffers.\n");
-            return AVERROR(ENOMEM);
+    if (!ctx->have_dimensions) {
+        int xres, yres;
+        if (ctx->subversion < 2) {
+            /* Rebel Assault 1: 384x242 internal size */
+            xres = 384;
+            yres = 242;
+            ctx->have_dimensions = 1;
+        } else if (codec == 37 || codec == 47 || codec == 48) {
+            /* these codecs work on full frames, trust their dimensions */
+            xres = w;
+            yres = h;
+            ctx->have_dimensions = 1;
+        } else {
+            /* Rebel Assault 2: 424x260 internal size */
+            if (((left + w) == 424) && ((top + h) == 260))
+                ctx->have_dimensions = 1;
+
+            xres = FFMAX(left + w, ctx->width);
+            yres = FFMAX(top + h, ctx->height);
+        }
+
+        if (ctx->width < xres || ctx->height < yres) {
+            int ret = ff_set_dimensions(ctx->avctx, xres, yres);
+            if (ret < 0)
+                return ret;
+            init_sizes(ctx, xres, yres);
+            if (init_buffers(ctx)) {
+                av_log(ctx->avctx, AV_LOG_ERROR, "Error resizing buffers.\n");
+                return AVERROR(ENOMEM);
+            }
+        }
+    } else {
+        if ((left + w > ctx->width) || (top + h > ctx->height)) {
+            /* correct unexpected overly large frames: this happens
+             * for instance with The Dig's sq1.san video: it has a few
+             * (all black) 640x480 frames halfway in, while the rest is
+             * 320x200.
+             */
+            av_log(ctx->avctx, AV_LOG_WARNING,
+                   "resizing too large fobj: %d %d @ %d %d  c %d\n", w, h, left, top, codec);
+            left = top = 0;
+            w = ctx->width;
+            h = ctx->height;
         }
     }
     bytestream2_skip(&ctx->gb, 4);
-- 
2.48.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [FFmpeg-devel] [PATCH 02/12] avcodec/sanm: disable left/top for fullscreen codecs
  2025-03-13 11:14 [FFmpeg-devel] [PATCH 00/12] avcodec/sanm: various improvements Manuel Lauss
  2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 01/12] avcodec/sanm: better frame size detection for old codecs Manuel Lauss
@ 2025-03-13 11:14 ` Manuel Lauss
  2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 03/12] avcodec/sanm: FOBJ left/top are signed values Manuel Lauss
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Manuel Lauss @ 2025-03-13 11:14 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Manuel Lauss

The block-based codecs 37/47/48 work on the full frame, and there's no
existing LucasArts game video that uses left/top offsets for these,
as it doesn't make sense. Ignore the left/top parameters for these codecs.

Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
 libavcodec/sanm.c | 36 +++++++++++++++++-------------------
 1 file changed, 17 insertions(+), 19 deletions(-)

diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index 0795d664fa..63956c01d6 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -621,8 +621,7 @@ static inline void codec37_mv(uint8_t *dst, const uint8_t *src,
     }
 }
 
-static int old_codec37(SANMVideoContext *ctx, int top,
-                       int left, int width, int height)
+static int old_codec37(SANMVideoContext *ctx, int width, int height)
 {
     int i, j, k, l, t, run, len, code, skip, mx, my;
     ptrdiff_t stride = ctx->pitch;
@@ -638,8 +637,8 @@ static int old_codec37(SANMVideoContext *ctx, int top,
     flags = bytestream2_get_byte(&ctx->gb);
     bytestream2_skip(&ctx->gb, 3);
 
-    if (decoded_size > ctx->height * stride - left - top * stride) {
-        decoded_size = ctx->height * stride - left - top * stride;
+    if (decoded_size > ctx->height * stride) {
+        decoded_size = ctx->height * stride;
         av_log(ctx->avctx, AV_LOG_WARNING, "Decoded size is too large.\n");
     }
 
@@ -649,8 +648,8 @@ static int old_codec37(SANMVideoContext *ctx, int top,
         FFSWAP(uint16_t*, ctx->frm1, ctx->frm2);
     }
 
-    dst  = ((uint8_t*)ctx->frm1) + left + top * stride;
-    prev = ((uint8_t*)ctx->frm2) + left + top * stride;
+    dst  = ((uint8_t*)ctx->frm1);
+    prev = ((uint8_t*)ctx->frm2);
 
     if (mvoff > 2) {
         av_log(ctx->avctx, AV_LOG_ERROR, "Invalid motion base value %d.\n", mvoff);
@@ -925,15 +924,14 @@ static void codec47_comp1(SANMVideoContext *ctx, uint8_t *dst_in, int width,
     }
 }
 
-static int old_codec47(SANMVideoContext *ctx, int top,
-                       int left, int width, int height)
+static int old_codec47(SANMVideoContext *ctx, int width, int height)
 {
     uint32_t decoded_size;
     int i, j;
     ptrdiff_t stride = ctx->pitch;
-    uint8_t *dst   = (uint8_t *)ctx->frm0 + left + top * stride;
-    uint8_t *prev1 = (uint8_t *)ctx->frm1 + left + top * stride;
-    uint8_t *prev2 = (uint8_t *)ctx->frm2 + left + top * stride;
+    uint8_t *dst   = (uint8_t *)ctx->frm0;
+    uint8_t *prev1 = (uint8_t *)ctx->frm1;
+    uint8_t *prev2 = (uint8_t *)ctx->frm2;
     uint8_t auxcol[2];
     int tbl_pos = bytestream2_tell(&ctx->gb);
     int seq     = bytestream2_get_le16(&ctx->gb);
@@ -947,8 +945,8 @@ static int old_codec47(SANMVideoContext *ctx, int top,
     decoded_size = bytestream2_get_le32(&ctx->gb);
     bytestream2_skip(&ctx->gb, 8);
 
-    if (decoded_size > ctx->height * stride - left - top * stride) {
-        decoded_size = ctx->height * stride - left - top * stride;
+    if (decoded_size > ctx->height * stride) {
+        decoded_size = ctx->height * stride;
         av_log(ctx->avctx, AV_LOG_WARNING, "Decoded size is too large.\n");
     }
 
@@ -959,8 +957,8 @@ static int old_codec47(SANMVideoContext *ctx, int top,
     }
     if (!seq) {
         ctx->prev_seq = -1;
-        memset(prev1, auxcol[0], (ctx->height - top) * stride);
-        memset(prev2, auxcol[1], (ctx->height - top) * stride);
+        memset(prev1, auxcol[0], ctx->height * stride);
+        memset(prev2, auxcol[1], ctx->height * stride);
     }
 
     switch (compr) {
@@ -1282,7 +1280,8 @@ static int process_frame_obj(SANMVideoContext *ctx)
             }
         }
     } else {
-        if ((left + w > ctx->width) || (top + h > ctx->height)) {
+        if (((left + w > ctx->width) || (top + h > ctx->height))
+            && codec >= 37) {
             /* correct unexpected overly large frames: this happens
              * for instance with The Dig's sq1.san video: it has a few
              * (all black) 640x480 frames halfway in, while the rest is
@@ -1290,7 +1289,6 @@ static int process_frame_obj(SANMVideoContext *ctx)
              */
             av_log(ctx->avctx, AV_LOG_WARNING,
                    "resizing too large fobj: %d %d @ %d %d  c %d\n", w, h, left, top, codec);
-            left = top = 0;
             w = ctx->width;
             h = ctx->height;
         }
@@ -1302,9 +1300,9 @@ static int process_frame_obj(SANMVideoContext *ctx)
     case 3:
         return old_codec1(ctx, top, left, w, h);
     case 37:
-        return old_codec37(ctx, top, left, w, h);
+        return old_codec37(ctx, w, h);
     case 47:
-        return old_codec47(ctx, top, left, w, h);
+        return old_codec47(ctx, w, h);
     case 48:
         return old_codec48(ctx, w, h);
     default:
-- 
2.48.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [FFmpeg-devel] [PATCH 03/12] avcodec/sanm: FOBJ left/top are signed values
  2025-03-13 11:14 [FFmpeg-devel] [PATCH 00/12] avcodec/sanm: various improvements Manuel Lauss
  2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 01/12] avcodec/sanm: better frame size detection for old codecs Manuel Lauss
  2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 02/12] avcodec/sanm: disable left/top for fullscreen codecs Manuel Lauss
@ 2025-03-13 11:14 ` Manuel Lauss
  2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 04/12] avcodec/sanm: misc fixes Manuel Lauss
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Manuel Lauss @ 2025-03-13 11:14 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Manuel Lauss

The left/top parameters of a FOBJ are signed values.  Adjust
codec1 code accordingly to not draw outside the buffer area.
Rebel Assault 1 makes heavy use of this.

Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
 libavcodec/sanm.c | 33 ++++++++++++++++++---------------
 1 file changed, 18 insertions(+), 15 deletions(-)

diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index 63956c01d6..9f656e6ba8 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -558,18 +558,18 @@ static int rle_decode(SANMVideoContext *ctx, uint8_t *dst, const int out_size)
 static int old_codec1(SANMVideoContext *ctx, int top,
                       int left, int width, int height)
 {
-    uint8_t *dst = ((uint8_t *)ctx->frm0) + left + top * ctx->pitch;
-    int i, j, len, flag, code, val, pos, end;
+    int i, j, len, flag, code, val, end, pxoff;
+    const int maxpxo = ctx->height * ctx->pitch;
+    uint8_t *dst = (uint8_t *)ctx->frm0;
 
     for (i = 0; i < height; i++) {
-        pos = 0;
-
         if (bytestream2_get_bytes_left(&ctx->gb) < 2)
             return AVERROR_INVALIDDATA;
 
         len = bytestream2_get_le16u(&ctx->gb);
         end = bytestream2_tell(&ctx->gb) + len;
 
+        pxoff = left + ((top + i) * ctx->pitch);
         while (bytestream2_tell(&ctx->gb) < end) {
             if (bytestream2_get_bytes_left(&ctx->gb) < 2)
                 return AVERROR_INVALIDDATA;
@@ -577,25 +577,28 @@ static int old_codec1(SANMVideoContext *ctx, int top,
             code = bytestream2_get_byteu(&ctx->gb);
             flag = code & 1;
             code = (code >> 1) + 1;
-            if (pos + code > width)
-                return AVERROR_INVALIDDATA;
             if (flag) {
                 val = bytestream2_get_byteu(&ctx->gb);
-                if (val)
-                    memset(dst + pos, val, code);
-                pos += code;
+                if (val) {
+                    for (j = 0; j < code; j++) {
+                        if (pxoff >= 0 && pxoff < maxpxo)
+                            *(dst + pxoff) = val;
+                        pxoff++;
+                    }
+                } else {
+                    pxoff += code;
+                }
             } else {
                 if (bytestream2_get_bytes_left(&ctx->gb) < code)
                     return AVERROR_INVALIDDATA;
                 for (j = 0; j < code; j++) {
                     val = bytestream2_get_byteu(&ctx->gb);
-                    if (val)
-                        dst[pos] = val;
-                    pos++;
+                    if ((pxoff >= 0) && (pxoff < maxpxo) && val)
+                        *(dst + pxoff) = val;
+                    pxoff++;
                 }
             }
         }
-        dst += ctx->pitch;
     }
     ctx->rotate_code = 0;
 
@@ -1236,8 +1239,8 @@ static int old_codec48(SANMVideoContext *ctx, int width, int height)
 static int process_frame_obj(SANMVideoContext *ctx)
 {
     uint16_t codec = bytestream2_get_le16u(&ctx->gb);
-    uint16_t left  = bytestream2_get_le16u(&ctx->gb);
-    uint16_t top   = bytestream2_get_le16u(&ctx->gb);
+    int16_t  left  = bytestream2_get_le16u(&ctx->gb);
+    int16_t  top   = bytestream2_get_le16u(&ctx->gb);
     uint16_t w     = bytestream2_get_le16u(&ctx->gb);
     uint16_t h     = bytestream2_get_le16u(&ctx->gb);
 
-- 
2.48.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [FFmpeg-devel] [PATCH 04/12] avcodec/sanm: misc fixes
  2025-03-13 11:14 [FFmpeg-devel] [PATCH 00/12] avcodec/sanm: various improvements Manuel Lauss
                   ` (2 preceding siblings ...)
  2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 03/12] avcodec/sanm: FOBJ left/top are signed values Manuel Lauss
@ 2025-03-13 11:14 ` Manuel Lauss
  2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 05/12] avcodec/sanm: fix codec3 Manuel Lauss
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Manuel Lauss @ 2025-03-13 11:14 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Manuel Lauss

- clear the front buffer with color 0 when processing data.
  Fixes a lot of Rebel Assault 1 videos and Rebel Assault 2 space
  scenes (e.g. 08PLAY.SAN which consists only of codec1/2/21 objects
  which only ever touch parts of the buffer).
- for ANIMv1 (Rebel Assault 1): set palette index 0 to all zeroes.
  This fixes a lot of stray colors in e.g L1HANGAR.ANM, L2INTRO.ANM,
  space scenes.
- Esp in RA1, there are a lot of FRME objects which don't contain
  any video data (prebuffering some audio only). Account for that.

Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
 libavcodec/sanm.c | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index 9f656e6ba8..fbb6e7231a 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -515,6 +515,8 @@ static av_cold int decode_init(AVCodecContext *avctx)
         ctx->subversion = AV_RL16(avctx->extradata);
         for (i = 0; i < PALETTE_SIZE; i++)
             ctx->pal[i] = 0xFFU << 24 | AV_RL32(avctx->extradata + 2 + i * 4);
+        if (ctx->subversion < 2)
+            ctx->pal[0] = 0xFFU << 24;
     }
 
     return 0;
@@ -1349,6 +1351,8 @@ static int process_xpal(SANMVideoContext *ctx, int size)
         if (size >= PALETTE_DELTA * 2 + 4 + PALETTE_SIZE * 3) {
             for (i = 0; i < PALETTE_SIZE; i++)
                 ctx->pal[i] = 0xFFU << 24 | bytestream2_get_be24u(&ctx->gb);
+            if (ctx->subversion < 2)
+                ctx->pal[0] = 0xFFU << 24;
         }
     }
     return 0;
@@ -1762,7 +1766,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
     bytestream2_init(&ctx->gb, pkt->data, pkt->size);
 
     if (!ctx->version) {
-        int to_store = 0;
+        int to_store = 0, have_pic = 0;
+
+        /* clear the front buffer */
+        if (ctx->frm0_size)
+            memset(ctx->frm0, 0, ctx->frm0_size);
 
         while (bytestream2_get_bytes_left(&ctx->gb) >= 8) {
             uint32_t sig, size;
@@ -1785,12 +1793,15 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
                 }
                 for (i = 0; i < PALETTE_SIZE; i++)
                     ctx->pal[i] = 0xFFU << 24 | bytestream2_get_be24u(&ctx->gb);
+                if (ctx->subversion < 2)
+                    ctx->pal[0] = 0xFFU << 24;
                 break;
             case MKBETAG('F', 'O', 'B', 'J'):
                 if (size < 16)
                     return AVERROR_INVALIDDATA;
                 if (ret = process_frame_obj(ctx))
                     return ret;
+                have_pic = 1;
                 break;
             case MKBETAG('X', 'P', 'A', 'L'):
                 if (ret = process_xpal(ctx, size))
@@ -1801,6 +1812,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
                 break;
             case MKBETAG('F', 'T', 'C', 'H'):
                 memcpy(ctx->frm0, ctx->stored_frame, ctx->buf_size);
+                have_pic = 1;
                 break;
             default:
                 bytestream2_skip(&ctx->gb, size);
@@ -1815,9 +1827,13 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
         }
         if (to_store)
             memcpy(ctx->stored_frame, ctx->frm0, ctx->buf_size);
-        if ((ret = copy_output(ctx, NULL)))
-            return ret;
-        memcpy(ctx->frame->data[1], ctx->pal, 1024);
+
+        if (have_pic && ctx->have_dimensions) {
+            if ((ret = copy_output(ctx, NULL)))
+                return ret;
+            memcpy(ctx->frame->data[1], ctx->pal, 1024);
+            *got_frame_ptr = 1;
+        }
     } else {
         SANMFrameHeader header;
 
@@ -1848,12 +1864,13 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
 
         if ((ret = copy_output(ctx, &header)))
             return ret;
+
+        *got_frame_ptr = 1;
+
     }
     if (ctx->rotate_code)
         rotate_bufs(ctx, ctx->rotate_code);
 
-    *got_frame_ptr = 1;
-
     return pkt->size;
 }
 
-- 
2.48.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [FFmpeg-devel] [PATCH 05/12] avcodec/sanm: fix codec3
  2025-03-13 11:14 [FFmpeg-devel] [PATCH 00/12] avcodec/sanm: various improvements Manuel Lauss
                   ` (3 preceding siblings ...)
  2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 04/12] avcodec/sanm: misc fixes Manuel Lauss
@ 2025-03-13 11:14 ` Manuel Lauss
  2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 06/12] avcodec/sanm: codec2 support Manuel Lauss
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Manuel Lauss @ 2025-03-13 11:14 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Manuel Lauss

codec3 is codec1 which writes zero values instead of skipping them.
This fixes a lot of RA1 videos.

Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
 libavcodec/sanm.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index fbb6e7231a..d4a84febc7 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -558,7 +558,7 @@ static int rle_decode(SANMVideoContext *ctx, uint8_t *dst, const int out_size)
 }
 
 static int old_codec1(SANMVideoContext *ctx, int top,
-                      int left, int width, int height)
+                      int left, int width, int height, int opaque)
 {
     int i, j, len, flag, code, val, end, pxoff;
     const int maxpxo = ctx->height * ctx->pitch;
@@ -581,7 +581,7 @@ static int old_codec1(SANMVideoContext *ctx, int top,
             code = (code >> 1) + 1;
             if (flag) {
                 val = bytestream2_get_byteu(&ctx->gb);
-                if (val) {
+                if (val || opaque) {
                     for (j = 0; j < code; j++) {
                         if (pxoff >= 0 && pxoff < maxpxo)
                             *(dst + pxoff) = val;
@@ -595,7 +595,7 @@ static int old_codec1(SANMVideoContext *ctx, int top,
                     return AVERROR_INVALIDDATA;
                 for (j = 0; j < code; j++) {
                     val = bytestream2_get_byteu(&ctx->gb);
-                    if ((pxoff >= 0) && (pxoff < maxpxo) && val)
+                    if ((pxoff >= 0) && (pxoff < maxpxo) && (val || opaque))
                         *(dst + pxoff) = val;
                     pxoff++;
                 }
@@ -1303,7 +1303,7 @@ static int process_frame_obj(SANMVideoContext *ctx)
     switch (codec) {
     case 1:
     case 3:
-        return old_codec1(ctx, top, left, w, h);
+        return old_codec1(ctx, top, left, w, h, codec == 3);
     case 37:
         return old_codec37(ctx, w, h);
     case 47:
-- 
2.48.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [FFmpeg-devel] [PATCH 06/12] avcodec/sanm: codec2 support
  2025-03-13 11:14 [FFmpeg-devel] [PATCH 00/12] avcodec/sanm: various improvements Manuel Lauss
                   ` (4 preceding siblings ...)
  2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 05/12] avcodec/sanm: fix codec3 Manuel Lauss
@ 2025-03-13 11:14 ` Manuel Lauss
  2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 07/12] avcodec/sanm: codec23 decoder Manuel Lauss
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Manuel Lauss @ 2025-03-13 11:14 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Manuel Lauss

this codec consists of 4 byte packets: 2bytes delta-x, 1 byte delta-y
and 1 byte color to put at that spot.
Used in Rebel Assault 1 only.

Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
 libavcodec/sanm.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index d4a84febc7..b88756d2c6 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -607,6 +607,24 @@ static int old_codec1(SANMVideoContext *ctx, int top,
     return 0;
 }
 
+static int old_codec2(SANMVideoContext *ctx, int top,
+                      int left, int width, int height)
+{
+    uint8_t *dst = (uint8_t *)ctx->frm0, col;
+    int16_t xpos = left, ypos = top;
+
+    while (bytestream2_get_bytes_left(&ctx->gb) > 3) {
+        xpos += bytestream2_get_le16u(&ctx->gb);
+        ypos += bytestream2_get_byteu(&ctx->gb);
+        col = bytestream2_get_byteu(&ctx->gb);
+        if (xpos >= 0 && ypos >= 0 &&
+            xpos < ctx->width && ypos < ctx->height) {
+                *(dst + xpos + ypos * ctx->pitch) = col;
+        }
+    }
+    return 0;
+}
+
 static inline void codec37_mv(uint8_t *dst, const uint8_t *src,
                               int height, int stride, int x, int y)
 {
@@ -1304,6 +1322,8 @@ static int process_frame_obj(SANMVideoContext *ctx)
     case 1:
     case 3:
         return old_codec1(ctx, top, left, w, h, codec == 3);
+    case 2:
+        return old_codec2(ctx, top, left, w, h);
     case 37:
         return old_codec37(ctx, w, h);
     case 47:
-- 
2.48.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [FFmpeg-devel] [PATCH 07/12] avcodec/sanm: codec23 decoder
  2025-03-13 11:14 [FFmpeg-devel] [PATCH 00/12] avcodec/sanm: various improvements Manuel Lauss
                   ` (5 preceding siblings ...)
  2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 06/12] avcodec/sanm: codec2 support Manuel Lauss
@ 2025-03-13 11:15 ` Manuel Lauss
  2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 08/12] avcodec/sanm: codec21 decoder Manuel Lauss
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Manuel Lauss @ 2025-03-13 11:15 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Manuel Lauss

This codec alternatingly skips and changes existing pixels.
A second 16bit parameter in the FOBJ header indicates how to do
the pixel changes: either by specifying a LUT in the codec datastream
or by adding a constant value to the pixel.

Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
 libavcodec/sanm.c | 66 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 64 insertions(+), 2 deletions(-)

diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index b88756d2c6..67963bb5d3 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -292,6 +292,7 @@ typedef struct SANMVideoContext {
     int8_t p4x4glyphs[NGLYPHS][16];
     int8_t p8x8glyphs[NGLYPHS][64];
     uint8_t c47itbl[0x10000];
+    uint8_t c23lut[256];
 } SANMVideoContext;
 
 typedef struct SANMFrameHeader {
@@ -557,6 +558,62 @@ static int rle_decode(SANMVideoContext *ctx, uint8_t *dst, const int out_size)
     return 0;
 }
 
+static int old_codec23(SANMVideoContext *ctx, int top, int left, int width,
+                       int height, uint8_t param, uint16_t param2)
+{
+    const uint32_t maxpxo = ctx->height * ctx->pitch;
+    uint8_t *dst, lut[256], c;
+    int i, j, k, pc, sk;
+    int32_t pxoff;
+
+    if (ctx->subversion < 2) {
+        /* Rebel Assault 1: constant offset + 0xd0 */
+        for (i = 0; i < 256; i++)
+            lut[i] = (i + param + 0xd0) & 0xff;
+    } else if (param2 == 256) {
+        if (bytestream2_get_bytes_left(&ctx->gb) < 256)
+            return AVERROR_INVALIDDATA;
+        bytestream2_get_bufferu(&ctx->gb, ctx->c23lut, 256);
+    } else if (param2 < 256) {
+        for (i = 0; i < 256; i++)
+            lut[i] = (i + param2) & 0xff;
+    } else {
+        memcpy(lut, ctx->c23lut, 256);
+    }
+    if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+        return 0;  /* some c23 frames just set up the LUT */
+
+    dst = (uint8_t *)ctx->frm0;
+    for (i = 0; i < height; i++) {
+        if (bytestream2_get_bytes_left(&ctx->gb) < 2)
+            return 0;
+        pxoff = left + ((top + i) * ctx->pitch);
+        k = bytestream2_get_le16u(&ctx->gb);
+        sk = 1;
+        pc = 0;
+        while (k > 0 && pc <= width) {
+            if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+                return AVERROR_INVALIDDATA;
+            j = bytestream2_get_byteu(&ctx->gb);
+            if (sk) {
+                pxoff += j;
+                pc += j;
+            } else {
+                while (j--) {
+                    if (pxoff >=0 && pxoff < maxpxo) {
+                        c = *(dst + pxoff);
+                        *(dst + pxoff) = lut[c];
+                    }
+                    pxoff++;
+                    pc++;
+                }
+            }
+            sk ^= 1;
+        }
+    }
+    return 0;
+}
+
 static int old_codec1(SANMVideoContext *ctx, int top,
                       int left, int width, int height, int opaque)
 {
@@ -1258,11 +1315,15 @@ static int old_codec48(SANMVideoContext *ctx, int width, int height)
 
 static int process_frame_obj(SANMVideoContext *ctx)
 {
-    uint16_t codec = bytestream2_get_le16u(&ctx->gb);
+    uint16_t parm2;
+    uint8_t  codec = bytestream2_get_byteu(&ctx->gb);
+    uint8_t  param = bytestream2_get_byteu(&ctx->gb);
     int16_t  left  = bytestream2_get_le16u(&ctx->gb);
     int16_t  top   = bytestream2_get_le16u(&ctx->gb);
     uint16_t w     = bytestream2_get_le16u(&ctx->gb);
     uint16_t h     = bytestream2_get_le16u(&ctx->gb);
+    bytestream2_skip(&ctx->gb, 2);
+    parm2 = bytestream2_get_le16u(&ctx->gb);
 
     if (w < 1 || h < 1 || w > 800 || h > 600 || left > 800 || top > 600) {
         av_log(ctx->avctx, AV_LOG_WARNING,
@@ -1316,7 +1377,6 @@ static int process_frame_obj(SANMVideoContext *ctx)
             h = ctx->height;
         }
     }
-    bytestream2_skip(&ctx->gb, 4);
 
     switch (codec) {
     case 1:
@@ -1324,6 +1384,8 @@ static int process_frame_obj(SANMVideoContext *ctx)
         return old_codec1(ctx, top, left, w, h, codec == 3);
     case 2:
         return old_codec2(ctx, top, left, w, h);
+    case 23:
+        return old_codec23(ctx, top, left, w, h, param, parm2);
     case 37:
         return old_codec37(ctx, w, h);
     case 47:
-- 
2.48.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [FFmpeg-devel] [PATCH 08/12] avcodec/sanm: codec21 decoder
  2025-03-13 11:14 [FFmpeg-devel] [PATCH 00/12] avcodec/sanm: various improvements Manuel Lauss
                   ` (6 preceding siblings ...)
  2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 07/12] avcodec/sanm: codec23 decoder Manuel Lauss
@ 2025-03-13 11:15 ` Manuel Lauss
  2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 09/12] avcodec/sanm: codec4/5/33/34 decoder Manuel Lauss
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Manuel Lauss @ 2025-03-13 11:15 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Manuel Lauss

similar to codec23, this one alternatingly skips and writes bytes.

Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
 libavcodec/sanm.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index 67963bb5d3..bf2ec40df4 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -614,6 +614,49 @@ static int old_codec23(SANMVideoContext *ctx, int top, int left, int width,
     return 0;
 }
 
+static int old_codec21(SANMVideoContext *ctx, int top, int left, int width,
+                       int height)
+{
+    const uint32_t maxpxo = ctx->height * ctx->pitch;
+    uint8_t *dst = (uint8_t *)ctx->frm0, c;
+    int i, j, k, pc, sk, pxoff;
+
+    dst = (uint8_t *)ctx->frm0;
+    for (i = 0; i < height; i++) {
+        if (bytestream2_get_bytes_left(&ctx->gb) < 2)
+            return 0;
+        pxoff = left + ((top + i) * ctx->pitch);
+        k = bytestream2_get_le16u(&ctx->gb);
+        sk = 1;
+        pc = 0;
+        while (k > 0 && pc <= width) {
+            if (bytestream2_get_bytes_left(&ctx->gb) < 2)
+                return AVERROR_INVALIDDATA;
+            j = bytestream2_get_le16u(&ctx->gb);
+            k -= 2;
+            if (sk) {
+                pxoff += j;
+                pc += j;
+            } else {
+                if (bytestream2_get_bytes_left(&ctx->gb) < (j + 1))
+                    return AVERROR_INVALIDDATA;
+                do {
+                    c = bytestream2_get_byteu(&ctx->gb);
+                    if (pxoff >=0 && pxoff < maxpxo) {
+                        *(dst + pxoff) = c;
+                    }
+                    pxoff++;
+                    pc++;
+                    j--;
+                    k--;
+                } while (j > -1);
+            }
+            sk ^= 1;
+        }
+    }
+    return 0;
+}
+
 static int old_codec1(SANMVideoContext *ctx, int top,
                       int left, int width, int height, int opaque)
 {
@@ -1384,6 +1427,8 @@ static int process_frame_obj(SANMVideoContext *ctx)
         return old_codec1(ctx, top, left, w, h, codec == 3);
     case 2:
         return old_codec2(ctx, top, left, w, h);
+    case 21:
+        return old_codec21(ctx, top, left, w, h);
     case 23:
         return old_codec23(ctx, top, left, w, h, param, parm2);
     case 37:
-- 
2.48.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [FFmpeg-devel] [PATCH 09/12] avcodec/sanm: codec4/5/33/34 decoder
  2025-03-13 11:14 [FFmpeg-devel] [PATCH 00/12] avcodec/sanm: various improvements Manuel Lauss
                   ` (7 preceding siblings ...)
  2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 08/12] avcodec/sanm: codec21 decoder Manuel Lauss
@ 2025-03-13 11:15 ` Manuel Lauss
  2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 10/12] avcodec/sanm: codec37: reimplement comp4 Manuel Lauss
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Manuel Lauss @ 2025-03-13 11:15 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Manuel Lauss

This codec works on 4x4 pixel tiles, which can be generated and read
from the datastream.  They're both identical, codec5 misses a tile
index to skip the current tile.
Codecs33/34 are the same as 4/5 but with a different tileset generator.
Used only in Rebel Assault 1.

Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
 libavcodec/sanm.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 229 insertions(+)

diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index bf2ec40df4..91c8ba658b 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -293,6 +293,8 @@ typedef struct SANMVideoContext {
     int8_t p8x8glyphs[NGLYPHS][64];
     uint8_t c47itbl[0x10000];
     uint8_t c23lut[256];
+    uint8_t c4tbl[2][256][16];
+    uint16_t c4param;
 } SANMVideoContext;
 
 typedef struct SANMFrameHeader {
@@ -480,6 +482,142 @@ static av_cold int init_buffers(SANMVideoContext *ctx)
     return 0;
 }
 
+static void codec33_gen_tiles(SANMVideoContext *ctx, int8_t param1)
+{
+    uint8_t *dst = &(ctx->c4tbl[0][0][0]);
+    int i, j, k, l, m, n, o, p;
+
+    for (i = 0; i < 8; i++) {
+        for (k = 0; k < 8; k++) {
+            j = i + param1;
+            l = k + param1;
+            p = (j + k) / 2;
+            j = (j + p) / 2;
+            m = l / 2;
+            n = (i + param1);
+            o = (k + param1);
+
+            *dst++ = p; *dst++ = p; *dst++ = j; *dst++ = n;
+            *dst++ = p; *dst++ = p; *dst++ = j; *dst++ = i;
+            *dst++ = m; *dst++ = m; *dst++ = p; *dst++ = j;
+            *dst++ = l; *dst++ = l; *dst++ = m; *dst++ = p;
+        }
+    }
+
+    for (i = 0; i < 8; i++) {
+        for (k = 0; k < 8; k++) {
+            j = i + param1;
+            l = k + param1;
+            n = ((j + l) / 2);
+            m = ((l + n) / 2);
+
+            *dst++ = j; *dst++ = j; *dst++ = j; *dst++ = j;
+            *dst++ = n; *dst++ = n; *dst++ = n; *dst++ = n;
+            *dst++ = m; *dst++ = m; *dst++ = m; *dst++ = m;
+            *dst++ = l; *dst++ = l; *dst++ = l; *dst++ = l;
+        }
+    }
+
+    for (i = 0; i < 8; i++) {
+        for (k = 0; k < 8; k++) {
+            j = i + param1;
+            l = k + param1;
+            m = (j + l) / 2;
+            n = (j + m) / 2;
+            o = m / 2;
+            p = j & 0xff;
+
+            *dst++ = p; *dst++ = p; *dst++ = n; *dst++ = m;
+            *dst++ = p; *dst++ = p; *dst++ = n; *dst++ = m;
+            *dst++ = n; *dst++ = n; *dst++ = m; *dst++ = o;
+            *dst++ = m; *dst++ = m; *dst++ = o; *dst++ = l;
+        }
+    }
+
+    for (i = 0; i < 8; i++) {
+        for (k = 0; k < 8; k++) {
+            j = i + param1;
+            l = k + param1;
+            m = (j + l) / 2;
+            n = m / 2;
+
+            *dst++ = j; *dst++ = m; *dst++ = n; *dst++ = l;
+            *dst++ = j; *dst++ = m; *dst++ = n; *dst++ = l;
+            *dst++ = j; *dst++ = m; *dst++ = n; *dst++ = l;
+            *dst++ = j; *dst++ = m; *dst++ = n; *dst++ = l;
+        }
+    }
+}
+
+static void codec4_gen_tiles(SANMVideoContext *ctx, uint16_t param1)
+{
+    uint8_t *dst = &(ctx->c4tbl[0][0][0]);
+    int i, j, k, l, m, n, o;
+
+    for (i = 1; i < 16; i += 2) {
+        for (k = 0; k < 16; k++) {
+            j = i + param1;
+            l = k + param1;
+            m = (j + l) / 2;
+            n = (j + m) / 2;
+            o = (l + m) / 2;
+            if (j == m || l == m) {
+                *dst++ = l; *dst++ = j; *dst++ = l; *dst++ = j;
+                *dst++ = j; *dst++ = l; *dst++ = j; *dst++ = j;
+                *dst++ = l; *dst++ = j; *dst++ = l; *dst++ = j;
+                *dst++ = l; *dst++ = l; *dst++ = j; *dst++ = l;
+            } else {
+                *dst++ = m; *dst++ = m; *dst++ = n; *dst++ = j;
+                *dst++ = m; *dst++ = m; *dst++ = n; *dst++ = j;
+                *dst++ = o; *dst++ = o; *dst++ = m; *dst++ = n;
+                *dst++ = l; *dst++ = l; *dst++ = o; *dst++ = m;
+            }
+        }
+    }
+
+    for (i = 0; i < 16; i += 2) {
+        for (k = 0; k < 16; k++) {
+            j = i + param1;
+            l = k + param1;
+            m = (j + l) / 2;
+            n = (j + m) / 2;
+            o = (l + m) / 2;
+            if (m == j || m == l) {
+                *dst++ = j; *dst++ = j; *dst++ = l; *dst++ = j;
+                *dst++ = j; *dst++ = j; *dst++ = j; *dst++ = l;
+                *dst++ = l; *dst++ = j; *dst++ = l; *dst++ = l;
+                *dst++ = j; *dst++ = l; *dst++ = j; *dst++ = l;
+            } else {
+                *dst++ = j; *dst++ = j; *dst++ = n; *dst++ = m;
+                *dst++ = j; *dst++ = j; *dst++ = n; *dst++ = m;
+                *dst++ = n; *dst++ = n; *dst++ = m; *dst++ = o;
+                *dst++ = m; *dst++ = m; *dst++ = o; *dst++ = l;
+            }
+        }
+    }
+}
+
+
+static int codec4_load_tiles(SANMVideoContext *ctx, uint16_t param2, uint8_t clr)
+{
+    uint8_t c, *dst = (uint8_t *)&(ctx->c4tbl[1][0][0]);
+    uint32_t loop = param2 * 8;
+
+    if (param2 > 256)
+        return AVERROR_INVALIDDATA;
+
+    if (bytestream2_get_bytes_left(&ctx->gb) < loop)
+        return AVERROR_INVALIDDATA;
+
+    while (loop--) {
+        c = bytestream2_get_byteu(&ctx->gb);
+        *dst++ = (c >> 4) + clr;
+        *dst++ = (c & 0xf) + clr;
+    }
+
+    return 0;
+}
+
 static void rotate_bufs(SANMVideoContext *ctx, int rotate_code)
 {
     if (rotate_code == 2)
@@ -519,6 +657,7 @@ static av_cold int decode_init(AVCodecContext *avctx)
         if (ctx->subversion < 2)
             ctx->pal[0] = 0xFFU << 24;
     }
+    ctx->c4param = 0xffff;
 
     return 0;
 }
@@ -532,6 +671,91 @@ static av_cold int decode_end(AVCodecContext *avctx)
     return 0;
 }
 
+static int old_codec4(SANMVideoContext *ctx, int left, int top, int w, int h,
+                      uint8_t param, uint16_t param2, int codec)
+{
+    const uint16_t p = ctx->pitch;
+    const uint32_t maxpxo = ctx->height * p;
+    uint8_t mask, bits, idx, *gs, *dst = (uint8_t *)ctx->frm0;
+    int i, j, k, l, bit, ret;
+    int32_t pxoff, pxo2;
+
+    if (ctx->c4param != param) {
+        if (codec > 32)
+            codec33_gen_tiles(ctx, param);
+        else
+            codec4_gen_tiles(ctx, param);
+        ctx->c4param = param;
+    }
+    if (param2 > 0) {
+        ret = codec4_load_tiles(ctx, param2, param);
+        if (ret)
+            return ret;
+    }
+
+    if (codec > 32)
+        codec -= 29;
+
+    for (j = 0; j < w; j += 4) {
+        mask = bits = 0;
+        for (i = 0; i < h; i += 4) {
+            pxoff = j + left + ((top + i) * p);
+            if (param2 > 0) {
+                if (bits == 0) {
+                    if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+                        return AVERROR_INVALIDDATA;
+                    mask = bytestream2_get_byteu(&ctx->gb);
+                    bits = 8;
+                }
+                bit = !!(mask & 0x80);
+                mask <<= 1;
+                bits--;
+            } else {
+                bit = 0;
+            }
+
+            if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+                return AVERROR_INVALIDDATA;
+            idx = bytestream2_get_byteu(&ctx->gb);
+            if ((bit == 0) && (idx == 0x80) && (codec != 5))
+                continue;
+
+            gs = &(ctx->c4tbl[bit][idx][0]);
+            pxo2 = pxoff;
+            for (k = 0; k < 4; k++) {
+                for (l = 0; l < 4; l++) {
+                    if (pxo2 >= 0 && pxo2 < maxpxo) {
+                        *(dst + pxo2) = *gs;
+                    }
+                    gs++;
+                    pxo2++;
+                }
+                pxo2 = pxo2 - 4 + p;
+            }
+
+            /* smooth top and left block borders with neighbours */
+            if (((pxoff - p + k) < 0) || ((pxoff - p + k) >= maxpxo)
+                || ((pxoff + 3 * p) < 0) || ((pxoff + 3 * p) >= maxpxo)
+                || (i == 0) || (j == 0))
+                continue;
+            if (param & 0x80) {
+                for (k = 0; k < 4; k++)
+                    *(dst + pxoff + k) = ((*(dst + pxoff + k) + *(dst + pxoff - p + k)) >> 1) | 0x80;
+                *(dst + pxoff + 1 * p) = (*(dst + pxoff + 1 * p) + *(dst + pxoff + 1 * p - 1)) >> 1 | 0x80;
+                *(dst + pxoff + 2 * p) = (*(dst + pxoff + 2 * p) + *(dst + pxoff + 2 * p - 1)) >> 1 | 0x80;
+                *(dst + pxoff + 3 * p) = (*(dst + pxoff + 3 * p) + *(dst + pxoff + 3 * p - 1)) >> 1 | 0x80;
+            } else {
+                for (k = 0; k < 4; k++)
+                    *(dst + pxoff + k) = ((*(dst + pxoff + k) + *(dst + pxoff - p + k)) >> 1) & 0x7f;
+                *(dst + pxoff + 1 * p) = (*(dst + pxoff + 1 * p) + *(dst + pxoff + 1 * p - 1)) >> 1;
+                *(dst + pxoff + 2 * p) = (*(dst + pxoff + 2 * p) + *(dst + pxoff + 2 * p - 1)) >> 1;
+                *(dst + pxoff + 3 * p) = (*(dst + pxoff + 3 * p) + *(dst + pxoff + 3 * p - 1)) >> 1;
+            }
+        }
+    }
+    return 0;
+}
+
 static int rle_decode(SANMVideoContext *ctx, uint8_t *dst, const int out_size)
 {
     int opcode, color, run_len, left = out_size;
@@ -1427,6 +1651,11 @@ static int process_frame_obj(SANMVideoContext *ctx)
         return old_codec1(ctx, top, left, w, h, codec == 3);
     case 2:
         return old_codec2(ctx, top, left, w, h);
+    case 4:
+    case 5:
+    case 33:
+    case 34:
+        return old_codec4(ctx, top, left, w, h, param, parm2, codec);
     case 21:
         return old_codec21(ctx, top, left, w, h);
     case 23:
-- 
2.48.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [FFmpeg-devel] [PATCH 10/12] avcodec/sanm: codec37: reimplement comp4
  2025-03-13 11:14 [FFmpeg-devel] [PATCH 00/12] avcodec/sanm: various improvements Manuel Lauss
                   ` (8 preceding siblings ...)
  2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 09/12] avcodec/sanm: codec4/5/33/34 decoder Manuel Lauss
@ 2025-03-13 11:15 ` Manuel Lauss
  2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 11/12] avcodec/sanm: change GetByteContext member to pointer Manuel Lauss
  2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 12/12] avcodec/sanm: properly implement STOR/FTCH for ANIMv1 Manuel Lauss
  11 siblings, 0 replies; 13+ messages in thread
From: Manuel Lauss @ 2025-03-13 11:15 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Manuel Lauss

Compression 4 code 0 means copy from delta buffer without mv,
AND start of a skip run.  This gets rid of the extra case and column
index manipulation and implements this as it is implemented in the
original game exe, i.e. as a special case for after mv copy.

Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
 libavcodec/sanm.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index 91c8ba658b..a8a3e04156 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -1106,16 +1106,14 @@ static int old_codec37(SANMVideoContext *ctx, int width, int height)
                     t = bytestream2_get_byteu(&ctx->gb);
                     for (k = 0; k < 4; k++)
                         memset(dst + i + k * stride, t, 4);
-               } else if ((compr == 4) && (code == 0)) {
-                    if (bytestream2_get_bytes_left(&ctx->gb) < 1)
-                        return AVERROR_INVALIDDATA;
-                    skip_run = bytestream2_get_byteu(&ctx->gb) + 1;
-                    i -= 4;
                } else {
                     mx = c37_mv[(mvoff * 255 + code) * 2];
                     my = c37_mv[(mvoff * 255 + code) * 2 + 1];
                     codec37_mv(dst + i, prev + i + mx + my * stride,
                                ctx->height, stride, i + mx, j + my);
+
+                    if ((compr == 4) && (code == 0))
+                        skip_run = bytestream2_get_byteu(&ctx->gb);
                 }
             }
             dst  += stride * 4;
-- 
2.48.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [FFmpeg-devel] [PATCH 11/12] avcodec/sanm: change GetByteContext member to pointer
  2025-03-13 11:14 [FFmpeg-devel] [PATCH 00/12] avcodec/sanm: various improvements Manuel Lauss
                   ` (9 preceding siblings ...)
  2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 10/12] avcodec/sanm: codec37: reimplement comp4 Manuel Lauss
@ 2025-03-13 11:15 ` Manuel Lauss
  2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 12/12] avcodec/sanm: properly implement STOR/FTCH for ANIMv1 Manuel Lauss
  11 siblings, 0 replies; 13+ messages in thread
From: Manuel Lauss @ 2025-03-13 11:15 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Manuel Lauss

In order do properly support the ANIM STOR/FTCH system, the FTCH
must replay a stored FOBJ and change the SANMContext's "GetByteContext"
member temporarily.

Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
 libavcodec/sanm.c | 394 +++++++++++++++++++++++-----------------------
 1 file changed, 198 insertions(+), 196 deletions(-)

diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index a8a3e04156..207db4a8fb 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -262,7 +262,7 @@ static const int8_t c37_mv[] = {
 
 typedef struct SANMVideoContext {
     AVCodecContext *avctx;
-    GetByteContext gb;
+    GetByteContext *gb;
 
     int version, subversion, have_dimensions;
     uint32_t pal[PALETTE_SIZE];
@@ -606,11 +606,11 @@ static int codec4_load_tiles(SANMVideoContext *ctx, uint16_t param2, uint8_t clr
     if (param2 > 256)
         return AVERROR_INVALIDDATA;
 
-    if (bytestream2_get_bytes_left(&ctx->gb) < loop)
+    if (bytestream2_get_bytes_left(ctx->gb) < loop)
         return AVERROR_INVALIDDATA;
 
     while (loop--) {
-        c = bytestream2_get_byteu(&ctx->gb);
+        c = bytestream2_get_byteu(ctx->gb);
         *dst++ = (c >> 4) + clr;
         *dst++ = (c & 0xf) + clr;
     }
@@ -702,9 +702,9 @@ static int old_codec4(SANMVideoContext *ctx, int left, int top, int w, int h,
             pxoff = j + left + ((top + i) * p);
             if (param2 > 0) {
                 if (bits == 0) {
-                    if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+                    if (bytestream2_get_bytes_left(ctx->gb) < 1)
                         return AVERROR_INVALIDDATA;
-                    mask = bytestream2_get_byteu(&ctx->gb);
+                    mask = bytestream2_get_byteu(ctx->gb);
                     bits = 8;
                 }
                 bit = !!(mask & 0x80);
@@ -714,9 +714,9 @@ static int old_codec4(SANMVideoContext *ctx, int left, int top, int w, int h,
                 bit = 0;
             }
 
-            if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+            if (bytestream2_get_bytes_left(ctx->gb) < 1)
                 return AVERROR_INVALIDDATA;
-            idx = bytestream2_get_byteu(&ctx->gb);
+            idx = bytestream2_get_byteu(ctx->gb);
             if ((bit == 0) && (idx == 0x80) && (codec != 5))
                 continue;
 
@@ -761,18 +761,18 @@ static int rle_decode(SANMVideoContext *ctx, uint8_t *dst, const int out_size)
     int opcode, color, run_len, left = out_size;
 
     while (left > 0) {
-        opcode = bytestream2_get_byte(&ctx->gb);
+        opcode = bytestream2_get_byte(ctx->gb);
         run_len = (opcode >> 1) + 1;
-        if (run_len > left || bytestream2_get_bytes_left(&ctx->gb) <= 0)
+        if (run_len > left || bytestream2_get_bytes_left(ctx->gb) <= 0)
             return AVERROR_INVALIDDATA;
 
         if (opcode & 1) {
-            color = bytestream2_get_byte(&ctx->gb);
+            color = bytestream2_get_byte(ctx->gb);
             memset(dst, color, run_len);
         } else {
-            if (bytestream2_get_bytes_left(&ctx->gb) < run_len)
+            if (bytestream2_get_bytes_left(ctx->gb) < run_len)
                 return AVERROR_INVALIDDATA;
-            bytestream2_get_bufferu(&ctx->gb, dst, run_len);
+            bytestream2_get_bufferu(ctx->gb, dst, run_len);
         }
 
         dst  += run_len;
@@ -795,30 +795,30 @@ static int old_codec23(SANMVideoContext *ctx, int top, int left, int width,
         for (i = 0; i < 256; i++)
             lut[i] = (i + param + 0xd0) & 0xff;
     } else if (param2 == 256) {
-        if (bytestream2_get_bytes_left(&ctx->gb) < 256)
+        if (bytestream2_get_bytes_left(ctx->gb) < 256)
             return AVERROR_INVALIDDATA;
-        bytestream2_get_bufferu(&ctx->gb, ctx->c23lut, 256);
+        bytestream2_get_bufferu(ctx->gb, ctx->c23lut, 256);
     } else if (param2 < 256) {
         for (i = 0; i < 256; i++)
             lut[i] = (i + param2) & 0xff;
     } else {
         memcpy(lut, ctx->c23lut, 256);
     }
-    if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+    if (bytestream2_get_bytes_left(ctx->gb) < 1)
         return 0;  /* some c23 frames just set up the LUT */
 
     dst = (uint8_t *)ctx->frm0;
     for (i = 0; i < height; i++) {
-        if (bytestream2_get_bytes_left(&ctx->gb) < 2)
+        if (bytestream2_get_bytes_left(ctx->gb) < 2)
             return 0;
         pxoff = left + ((top + i) * ctx->pitch);
-        k = bytestream2_get_le16u(&ctx->gb);
+        k = bytestream2_get_le16u(ctx->gb);
         sk = 1;
         pc = 0;
         while (k > 0 && pc <= width) {
-            if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+            if (bytestream2_get_bytes_left(ctx->gb) < 1)
                 return AVERROR_INVALIDDATA;
-            j = bytestream2_get_byteu(&ctx->gb);
+            j = bytestream2_get_byteu(ctx->gb);
             if (sk) {
                 pxoff += j;
                 pc += j;
@@ -847,25 +847,25 @@ static int old_codec21(SANMVideoContext *ctx, int top, int left, int width,
 
     dst = (uint8_t *)ctx->frm0;
     for (i = 0; i < height; i++) {
-        if (bytestream2_get_bytes_left(&ctx->gb) < 2)
+        if (bytestream2_get_bytes_left(ctx->gb) < 2)
             return 0;
         pxoff = left + ((top + i) * ctx->pitch);
-        k = bytestream2_get_le16u(&ctx->gb);
+        k = bytestream2_get_le16u(ctx->gb);
         sk = 1;
         pc = 0;
         while (k > 0 && pc <= width) {
-            if (bytestream2_get_bytes_left(&ctx->gb) < 2)
+            if (bytestream2_get_bytes_left(ctx->gb) < 2)
                 return AVERROR_INVALIDDATA;
-            j = bytestream2_get_le16u(&ctx->gb);
+            j = bytestream2_get_le16u(ctx->gb);
             k -= 2;
             if (sk) {
                 pxoff += j;
                 pc += j;
             } else {
-                if (bytestream2_get_bytes_left(&ctx->gb) < (j + 1))
+                if (bytestream2_get_bytes_left(ctx->gb) < (j + 1))
                     return AVERROR_INVALIDDATA;
                 do {
-                    c = bytestream2_get_byteu(&ctx->gb);
+                    c = bytestream2_get_byteu(ctx->gb);
                     if (pxoff >=0 && pxoff < maxpxo) {
                         *(dst + pxoff) = c;
                     }
@@ -889,22 +889,22 @@ static int old_codec1(SANMVideoContext *ctx, int top,
     uint8_t *dst = (uint8_t *)ctx->frm0;
 
     for (i = 0; i < height; i++) {
-        if (bytestream2_get_bytes_left(&ctx->gb) < 2)
+        if (bytestream2_get_bytes_left(ctx->gb) < 2)
             return AVERROR_INVALIDDATA;
 
-        len = bytestream2_get_le16u(&ctx->gb);
-        end = bytestream2_tell(&ctx->gb) + len;
+        len = bytestream2_get_le16u(ctx->gb);
+        end = bytestream2_tell(ctx->gb) + len;
 
         pxoff = left + ((top + i) * ctx->pitch);
-        while (bytestream2_tell(&ctx->gb) < end) {
-            if (bytestream2_get_bytes_left(&ctx->gb) < 2)
+        while (bytestream2_tell(ctx->gb) < end) {
+            if (bytestream2_get_bytes_left(ctx->gb) < 2)
                 return AVERROR_INVALIDDATA;
 
-            code = bytestream2_get_byteu(&ctx->gb);
+            code = bytestream2_get_byteu(ctx->gb);
             flag = code & 1;
             code = (code >> 1) + 1;
             if (flag) {
-                val = bytestream2_get_byteu(&ctx->gb);
+                val = bytestream2_get_byteu(ctx->gb);
                 if (val || opaque) {
                     for (j = 0; j < code; j++) {
                         if (pxoff >= 0 && pxoff < maxpxo)
@@ -915,10 +915,10 @@ static int old_codec1(SANMVideoContext *ctx, int top,
                     pxoff += code;
                 }
             } else {
-                if (bytestream2_get_bytes_left(&ctx->gb) < code)
+                if (bytestream2_get_bytes_left(ctx->gb) < code)
                     return AVERROR_INVALIDDATA;
                 for (j = 0; j < code; j++) {
-                    val = bytestream2_get_byteu(&ctx->gb);
+                    val = bytestream2_get_byteu(ctx->gb);
                     if ((pxoff >= 0) && (pxoff < maxpxo) && (val || opaque))
                         *(dst + pxoff) = val;
                     pxoff++;
@@ -937,10 +937,10 @@ static int old_codec2(SANMVideoContext *ctx, int top,
     uint8_t *dst = (uint8_t *)ctx->frm0, col;
     int16_t xpos = left, ypos = top;
 
-    while (bytestream2_get_bytes_left(&ctx->gb) > 3) {
-        xpos += bytestream2_get_le16u(&ctx->gb);
-        ypos += bytestream2_get_byteu(&ctx->gb);
-        col = bytestream2_get_byteu(&ctx->gb);
+    while (bytestream2_get_bytes_left(ctx->gb) > 3) {
+        xpos += bytestream2_get_le16u(ctx->gb);
+        ypos += bytestream2_get_byteu(ctx->gb);
+        col = bytestream2_get_byteu(ctx->gb);
         if (xpos >= 0 && ypos >= 0 &&
             xpos < ctx->width && ypos < ctx->height) {
                 *(dst + xpos + ypos * ctx->pitch) = col;
@@ -974,15 +974,15 @@ static int old_codec37(SANMVideoContext *ctx, int width, int height)
     ptrdiff_t stride = ctx->pitch;
     uint8_t *dst, *prev;
     int skip_run = 0;
-    int compr = bytestream2_get_byte(&ctx->gb);
-    int mvoff = bytestream2_get_byte(&ctx->gb);
-    int seq   = bytestream2_get_le16(&ctx->gb);
-    uint32_t decoded_size = bytestream2_get_le32(&ctx->gb);
+    int compr = bytestream2_get_byte(ctx->gb);
+    int mvoff = bytestream2_get_byte(ctx->gb);
+    int seq   = bytestream2_get_le16(ctx->gb);
+    uint32_t decoded_size = bytestream2_get_le32(ctx->gb);
     int flags;
 
-    bytestream2_skip(&ctx->gb, 4);
-    flags = bytestream2_get_byte(&ctx->gb);
-    bytestream2_skip(&ctx->gb, 3);
+    bytestream2_skip(ctx->gb, 4);
+    flags = bytestream2_get_byte(ctx->gb);
+    bytestream2_skip(ctx->gb, 3);
 
     if (decoded_size > ctx->height * stride) {
         decoded_size = ctx->height * stride;
@@ -1006,7 +1006,7 @@ static int old_codec37(SANMVideoContext *ctx, int width, int height)
     switch (compr) {
     case 0:
         for (i = 0; i < height; i++) {
-            bytestream2_get_buffer(&ctx->gb, dst, width);
+            bytestream2_get_buffer(ctx->gb, dst, width);
             dst += stride;
         }
         memset(ctx->frm2, 0, ctx->height * stride);
@@ -1019,9 +1019,9 @@ static int old_codec37(SANMVideoContext *ctx, int width, int height)
         for (j = 0; j < height; j += 4) {
             for (i = 0; i < width; i += 4) {
                 if (len < 0) {
-                    if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+                    if (bytestream2_get_bytes_left(ctx->gb) < 1)
                         return AVERROR_INVALIDDATA;
-                    code = bytestream2_get_byte(&ctx->gb);
+                    code = bytestream2_get_byte(ctx->gb);
                     len = code >> 1;
                     run = code & 1;
                     skip = 0;
@@ -1030,29 +1030,29 @@ static int old_codec37(SANMVideoContext *ctx, int width, int height)
                 }
 
                 if (!skip) {
-                    if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+                    if (bytestream2_get_bytes_left(ctx->gb) < 1)
                         return AVERROR_INVALIDDATA;
-                    code = bytestream2_get_byte(&ctx->gb);
+                    code = bytestream2_get_byte(ctx->gb);
                     if (code == 0xff) {
                         len--;
                         for (k = 0; k < 4; k++) {
                             for (l = 0; l < 4; l++) {
                                 if (len < 0) {
-                                    if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+                                    if (bytestream2_get_bytes_left(ctx->gb) < 1)
                                         return AVERROR_INVALIDDATA;
-                                    code = bytestream2_get_byte(&ctx->gb);
+                                    code = bytestream2_get_byte(ctx->gb);
                                     len = code >> 1;
                                     run = code & 1;
                                     if (run) {
-                                        if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+                                        if (bytestream2_get_bytes_left(ctx->gb) < 1)
                                             return AVERROR_INVALIDDATA;
-                                        code = bytestream2_get_byte(&ctx->gb);
+                                        code = bytestream2_get_byte(ctx->gb);
                                     }
                                 }
                                 if (!run) {
-                                    if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+                                    if (bytestream2_get_bytes_left(ctx->gb) < 1)
                                             return AVERROR_INVALIDDATA;
-                                        code = bytestream2_get_byte(&ctx->gb);
+                                        code = bytestream2_get_byte(ctx->gb);
                                 }
                                 *(dst + i + (k * stride) + l) = code;
                                 len--;
@@ -1087,23 +1087,23 @@ static int old_codec37(SANMVideoContext *ctx, int width, int height)
                     copy_block4(dst + i, prev + i, stride, stride, 4);
                     continue;
                 }
-                if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+                if (bytestream2_get_bytes_left(ctx->gb) < 1)
                     return AVERROR_INVALIDDATA;
-                code = bytestream2_get_byteu(&ctx->gb);
+                code = bytestream2_get_byteu(ctx->gb);
                 if (code == 0xFF) {
-                    if (bytestream2_get_bytes_left(&ctx->gb) < 16)
+                    if (bytestream2_get_bytes_left(ctx->gb) < 16)
                         return AVERROR_INVALIDDATA;
                     for (k = 0; k < 4; k++)
-                        bytestream2_get_bufferu(&ctx->gb, dst + i + k * stride, 4);
+                        bytestream2_get_bufferu(ctx->gb, dst + i + k * stride, 4);
                 } else if ((flags & 4) && (code == 0xFE)) {
-                    if (bytestream2_get_bytes_left(&ctx->gb) < 4)
+                    if (bytestream2_get_bytes_left(ctx->gb) < 4)
                        return AVERROR_INVALIDDATA;
                    for (k = 0; k < 4; k++)
-                       memset(dst + i + k * stride, bytestream2_get_byteu(&ctx->gb), 4);
+                       memset(dst + i + k * stride, bytestream2_get_byteu(ctx->gb), 4);
                 } else if ((flags & 4) && (code == 0xFD)) {
-                    if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+                    if (bytestream2_get_bytes_left(ctx->gb) < 1)
                         return AVERROR_INVALIDDATA;
-                    t = bytestream2_get_byteu(&ctx->gb);
+                    t = bytestream2_get_byteu(ctx->gb);
                     for (k = 0; k < 4; k++)
                         memset(dst + i + k * stride, t, 4);
                } else {
@@ -1113,7 +1113,7 @@ static int old_codec37(SANMVideoContext *ctx, int width, int height)
                                ctx->height, stride, i + mx, j + my);
 
                     if ((compr == 4) && (code == 0))
-                        skip_run = bytestream2_get_byteu(&ctx->gb);
+                        skip_run = bytestream2_get_byteu(ctx->gb);
                 }
             }
             dst  += stride * 4;
@@ -1137,20 +1137,20 @@ static int process_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *prev1,
     uint8_t colors[2];
     int8_t *pglyph;
 
-    if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+    if (bytestream2_get_bytes_left(ctx->gb) < 1)
         return AVERROR_INVALIDDATA;
 
-    code = bytestream2_get_byteu(&ctx->gb);
+    code = bytestream2_get_byteu(ctx->gb);
     if (code >= 0xF8) {
         switch (code) {
         case 0xFF:
             if (size == 2) {
-                if (bytestream2_get_bytes_left(&ctx->gb) < 4)
+                if (bytestream2_get_bytes_left(ctx->gb) < 4)
                     return AVERROR_INVALIDDATA;
-                dst[0]          = bytestream2_get_byteu(&ctx->gb);
-                dst[1]          = bytestream2_get_byteu(&ctx->gb);
-                dst[0 + stride] = bytestream2_get_byteu(&ctx->gb);
-                dst[1 + stride] = bytestream2_get_byteu(&ctx->gb);
+                dst[0]          = bytestream2_get_byteu(ctx->gb);
+                dst[1]          = bytestream2_get_byteu(ctx->gb);
+                dst[0 + stride] = bytestream2_get_byteu(ctx->gb);
+                dst[1 + stride] = bytestream2_get_byteu(ctx->gb);
             } else {
                 size >>= 1;
                 if (process_block(ctx, dst, prev1, prev2, stride, tbl, size))
@@ -1169,20 +1169,20 @@ static int process_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *prev1,
             }
             break;
         case 0xFE:
-            if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+            if (bytestream2_get_bytes_left(ctx->gb) < 1)
                 return AVERROR_INVALIDDATA;
 
-            t = bytestream2_get_byteu(&ctx->gb);
+            t = bytestream2_get_byteu(ctx->gb);
             for (k = 0; k < size; k++)
                 memset(dst + k * stride, t, size);
             break;
         case 0xFD:
-            if (bytestream2_get_bytes_left(&ctx->gb) < 3)
+            if (bytestream2_get_bytes_left(ctx->gb) < 3)
                 return AVERROR_INVALIDDATA;
 
-            code = bytestream2_get_byteu(&ctx->gb);
+            code = bytestream2_get_byteu(ctx->gb);
             pglyph = (size == 8) ? ctx->p8x8glyphs[code] : ctx->p4x4glyphs[code];
-            bytestream2_get_bufferu(&ctx->gb, colors, 2);
+            bytestream2_get_bufferu(ctx->gb, colors, 2);
 
             for (k = 0; k < size; k++)
                 for (t = 0; t < size; t++)
@@ -1193,10 +1193,10 @@ static int process_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *prev1,
                 memcpy(dst + k * stride, prev1 + k * stride, size);
             break;
         default:
-            k = bytestream2_tell(&ctx->gb);
-            bytestream2_seek(&ctx->gb, tbl + (code & 7), SEEK_SET);
-            t = bytestream2_get_byte(&ctx->gb);
-            bytestream2_seek(&ctx->gb, k, SEEK_SET);
+            k = bytestream2_tell(ctx->gb);
+            bytestream2_seek(ctx->gb, tbl + (code & 7), SEEK_SET);
+            t = bytestream2_get_byte(ctx->gb);
+            bytestream2_seek(ctx->gb, k, SEEK_SET);
             for (k = 0; k < size; k++)
                 memset(dst + k * stride, t, size);
         }
@@ -1228,7 +1228,7 @@ static void codec47_read_interptable(SANMVideoContext *ctx)
     for (i = 0; i < 256; i++) {
         p1 = p2 = itbl + i;
         for (j = 256 - i; j; j--) {
-            *p1 = *p2 = bytestream2_get_byte(&ctx->gb);
+            *p1 = *p2 = bytestream2_get_byte(ctx->gb);
             p1 += 1;
             p2 += 256;
         }
@@ -1245,12 +1245,12 @@ static void codec47_comp1(SANMVideoContext *ctx, uint8_t *dst_in, int width,
 
     dst = dst_in + stride;
     for (i = 0; i < height; i += 2) {
-        p1 = bytestream2_get_byte(&ctx->gb);
+        p1 = bytestream2_get_byte(ctx->gb);
         *dst++ = p1;
         *dst++ = p1;
         px = p1;
         for (j = 2; j < width; j += 2) {
-            p1 = bytestream2_get_byte(&ctx->gb);
+            p1 = bytestream2_get_byte(ctx->gb);
             px = (px << 8) | p1;
             *dst++ = itbl[px];
             *dst++ = p1;
@@ -1278,17 +1278,17 @@ static int old_codec47(SANMVideoContext *ctx, int width, int height)
     uint8_t *prev1 = (uint8_t *)ctx->frm1;
     uint8_t *prev2 = (uint8_t *)ctx->frm2;
     uint8_t auxcol[2];
-    int tbl_pos = bytestream2_tell(&ctx->gb);
-    int seq     = bytestream2_get_le16(&ctx->gb);
-    int compr   = bytestream2_get_byte(&ctx->gb);
-    int new_rot = bytestream2_get_byte(&ctx->gb);
-    int skip    = bytestream2_get_byte(&ctx->gb);
-
-    bytestream2_skip(&ctx->gb, 7);
-    auxcol[0] = bytestream2_get_byteu(&ctx->gb);
-    auxcol[1] = bytestream2_get_byteu(&ctx->gb);
-    decoded_size = bytestream2_get_le32(&ctx->gb);
-    bytestream2_skip(&ctx->gb, 8);
+    int tbl_pos = bytestream2_tell(ctx->gb);
+    int seq     = bytestream2_get_le16(ctx->gb);
+    int compr   = bytestream2_get_byte(ctx->gb);
+    int new_rot = bytestream2_get_byte(ctx->gb);
+    int skip    = bytestream2_get_byte(ctx->gb);
+
+    bytestream2_skip(ctx->gb, 7);
+    auxcol[0] = bytestream2_get_byteu(ctx->gb);
+    auxcol[1] = bytestream2_get_byteu(ctx->gb);
+    decoded_size = bytestream2_get_le32(ctx->gb);
+    bytestream2_skip(ctx->gb, 8);
 
     if (decoded_size > ctx->height * stride) {
         decoded_size = ctx->height * stride;
@@ -1296,7 +1296,7 @@ static int old_codec47(SANMVideoContext *ctx, int width, int height)
     }
 
     if (skip & 1) {
-        if (bytestream2_get_bytes_left(&ctx->gb) < 0x8080)
+        if (bytestream2_get_bytes_left(ctx->gb) < 0x8080)
             return AVERROR_INVALIDDATA;
         codec47_read_interptable(ctx);
     }
@@ -1308,15 +1308,15 @@ static int old_codec47(SANMVideoContext *ctx, int width, int height)
 
     switch (compr) {
     case 0:
-        if (bytestream2_get_bytes_left(&ctx->gb) < width * height)
+        if (bytestream2_get_bytes_left(ctx->gb) < width * height)
             return AVERROR_INVALIDDATA;
         for (j = 0; j < height; j++) {
-            bytestream2_get_bufferu(&ctx->gb, dst, width);
+            bytestream2_get_bufferu(ctx->gb, dst, width);
             dst += stride;
         }
         break;
     case 1:
-        if (bytestream2_get_bytes_left(&ctx->gb) < ((width + 1) >> 1) * ((height + 1) >> 1))
+        if (bytestream2_get_bytes_left(ctx->gb) < ((width + 1) >> 1) * ((height + 1) >> 1))
             return AVERROR_INVALIDDATA;
         codec47_comp1(ctx, dst, width, height, stride);
         break;
@@ -1381,24 +1381,24 @@ static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
     int16_t mvofs;
     uint32_t ofs;
 
-    if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+    if (bytestream2_get_bytes_left(ctx->gb) < 1)
         return 1;
 
-    opc = bytestream2_get_byteu(&ctx->gb);
+    opc = bytestream2_get_byteu(ctx->gb);
     switch (opc) {
     case 0xFF:    // 1x1 -> 8x8 block scale
-        if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+        if (bytestream2_get_bytes_left(ctx->gb) < 1)
             return 1;
 
-        opc = bytestream2_get_byteu(&ctx->gb);
+        opc = bytestream2_get_byteu(ctx->gb);
         for (i = 0; i < 16; i++)
             sb[i] = opc;
         c48_4to8(dst, sb, w);
         break;
     case 0xFE:    // 1x 8x8 copy from deltabuf, 16bit mv from source
-        if (bytestream2_get_bytes_left(&ctx->gb) < 2)
+        if (bytestream2_get_bytes_left(ctx->gb) < 2)
             return 1;
-        mvofs =  bytestream2_get_le16(&ctx->gb);
+        mvofs =  bytestream2_get_le16(ctx->gb);
         for (i = 0; i < 8; i++) {
             ofs = w * i;
             for (k = 0; k < 8; k++)
@@ -1406,12 +1406,12 @@ static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
         }
         break;
     case 0xFD:    // 2x2 -> 8x8 block scale
-        if (bytestream2_get_bytes_left(&ctx->gb) < 4)
+        if (bytestream2_get_bytes_left(ctx->gb) < 4)
             return 1;
-        sb[ 5] =  bytestream2_get_byteu(&ctx->gb);
-        sb[ 7] =  bytestream2_get_byteu(&ctx->gb);
-        sb[13] =  bytestream2_get_byteu(&ctx->gb);
-        sb[15] =  bytestream2_get_byteu(&ctx->gb);
+        sb[ 5] =  bytestream2_get_byteu(ctx->gb);
+        sb[ 7] =  bytestream2_get_byteu(ctx->gb);
+        sb[13] =  bytestream2_get_byteu(ctx->gb);
+        sb[15] =  bytestream2_get_byteu(ctx->gb);
 
         sb[0] = sb[1] = sb[4] = sb[5];
         sb[2] = sb[3] = sb[6] = sb[7];
@@ -1420,11 +1420,11 @@ static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
         c48_4to8(dst, sb, w);
         break;
     case 0xFC:    // 4x copy 4x4 block, per-block c37_mv from source
-        if (bytestream2_get_bytes_left(&ctx->gb) < 4)
+        if (bytestream2_get_bytes_left(ctx->gb) < 4)
             return 1;
         for (i = 0; i < 8; i += 4) {
             for (k = 0; k < 8; k += 4) {
-                opc =  bytestream2_get_byteu(&ctx->gb);
+                opc =  bytestream2_get_byteu(ctx->gb);
                 mvofs = c37_mv[opc * 2] + (c37_mv[opc * 2 + 1] * w);
                 for (j = 0; j < 4; j++) {
                     ofs = (w * (j + i)) + k;
@@ -1435,11 +1435,11 @@ static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
         }
         break;
     case 0xFB:    // Copy 4x 4x4 blocks, per-block mv from source
-        if (bytestream2_get_bytes_left(&ctx->gb) < 8)
+        if (bytestream2_get_bytes_left(ctx->gb) < 8)
             return 1;
         for (i = 0; i < 8; i += 4) {
             for (k = 0; k < 8; k += 4) {
-                mvofs = bytestream2_get_le16(&ctx->gb);
+                mvofs = bytestream2_get_le16(ctx->gb);
                 for (j = 0; j < 4; j++) {
                     ofs = (w * (j + i)) + k;
                     for (l = 0; l < 4; l++)
@@ -1449,18 +1449,18 @@ static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
         }
         break;
     case 0xFA:    // scale 4x4 input block to 8x8 dest block
-        if (bytestream2_get_bytes_left(&ctx->gb) < 16)
+        if (bytestream2_get_bytes_left(ctx->gb) < 16)
             return 1;
-        bytestream2_get_bufferu(&ctx->gb, sb, 16);
+        bytestream2_get_bufferu(ctx->gb, sb, 16);
         c48_4to8(dst, sb, w);
         break;
     case 0xF9:    // 16x 2x2 copy from delta, per-block c37_mv from source
-        if (bytestream2_get_bytes_left(&ctx->gb) < 16)
+        if (bytestream2_get_bytes_left(ctx->gb) < 16)
             return 1;
         for (i = 0; i < 8; i += 2) {
             for (j = 0; j < 8; j += 2) {
                 ofs = (w * i) + j;
-                opc = bytestream2_get_byteu(&ctx->gb);
+                opc = bytestream2_get_byteu(ctx->gb);
                 mvofs = c37_mv[opc * 2] + (c37_mv[opc * 2 + 1] * w);
                 for (l = 0; l < 2; l++) {
                     *(dst + ofs + l + 0) = *(db + ofs + l + 0 + mvofs);
@@ -1470,12 +1470,12 @@ static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
         }
         break;
     case 0xF8:    // 16x 2x2 blocks copy, 16bit mv from source
-        if (bytestream2_get_bytes_left(&ctx->gb) < 32)
+        if (bytestream2_get_bytes_left(ctx->gb) < 32)
             return 1;
         for (i = 0; i < 8; i += 2) {
             for (j = 0; j < 8; j += 2) {
                 ofs = w * i + j;
-                mvofs = bytestream2_get_le16(&ctx->gb);
+                mvofs = bytestream2_get_le16(ctx->gb);
                 for (l = 0; l < 2; l++) {
                     *(dst + ofs + l + 0) = *(db + ofs + l + 0 + mvofs);
                     *(dst + ofs + l + w) = *(db + ofs + l + w + mvofs);
@@ -1484,12 +1484,12 @@ static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
         }
         break;
     case 0xF7:    // copy 8x8 block from src to dest
-        if (bytestream2_get_bytes_left(&ctx->gb) < 64)
+        if (bytestream2_get_bytes_left(ctx->gb) < 64)
             return 1;
         for (i = 0; i < 8; i++) {
             ofs = i * w;
             for (l = 0; l < 8; l++)
-                *(dst + ofs + l) = bytestream2_get_byteu(&ctx->gb);
+                *(dst + ofs + l) = bytestream2_get_byteu(ctx->gb);
         }
         break;
     default:    // copy 8x8 block from prev, c37_mv from source
@@ -1507,10 +1507,10 @@ static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
 static int old_codec48(SANMVideoContext *ctx, int width, int height)
 {
     uint8_t *dst, *prev;
-    int compr = bytestream2_get_byte(&ctx->gb);
-    int mvidx = bytestream2_get_byte(&ctx->gb);
-    int seq   = bytestream2_get_le16(&ctx->gb);
-    uint32_t decoded_size = bytestream2_get_le32(&ctx->gb);
+    int compr = bytestream2_get_byte(ctx->gb);
+    int mvidx = bytestream2_get_byte(ctx->gb);
+    int seq   = bytestream2_get_le16(ctx->gb);
+    uint32_t decoded_size = bytestream2_get_le32(ctx->gb);
     int i, j, flags;
 
     // all codec48 videos use 1, but just to be safe...
@@ -1519,12 +1519,12 @@ static int old_codec48(SANMVideoContext *ctx, int width, int height)
         return AVERROR_INVALIDDATA;
     }
 
-    bytestream2_skip(&ctx->gb, 4);
-    flags = bytestream2_get_byte(&ctx->gb);
-    bytestream2_skip(&ctx->gb, 3);
+    bytestream2_skip(ctx->gb, 4);
+    flags = bytestream2_get_byte(ctx->gb);
+    bytestream2_skip(ctx->gb, 3);
 
     if (flags & 8) {
-        if (bytestream2_get_bytes_left(&ctx->gb) < 0x8080)
+        if (bytestream2_get_bytes_left(ctx->gb) < 0x8080)
             return AVERROR_INVALIDDATA;
         codec47_read_interptable(ctx);
     }
@@ -1539,10 +1539,10 @@ static int old_codec48(SANMVideoContext *ctx, int width, int height)
 
     switch (compr) {
     case 0:
-        if (bytestream2_get_bytes_left(&ctx->gb) < width * height)
+        if (bytestream2_get_bytes_left(ctx->gb) < width * height)
             return AVERROR_INVALIDDATA;
         for (j = 0; j < height; j++) {
-            bytestream2_get_bufferu(&ctx->gb, dst, width);
+            bytestream2_get_bufferu(ctx->gb, dst, width);
             dst += width;
         }
         break;
@@ -1563,7 +1563,7 @@ static int old_codec48(SANMVideoContext *ctx, int width, int height)
         }
         break;
     case 5:
-        if (bytestream2_get_bytes_left(&ctx->gb) < ((width + 1) >> 1) * ((height + 1) >> 1))
+        if (bytestream2_get_bytes_left(ctx->gb) < ((width + 1) >> 1) * ((height + 1) >> 1))
             return AVERROR_INVALIDDATA;
         codec47_comp1(ctx, dst, width, height, width);
         break;
@@ -1581,14 +1581,14 @@ static int old_codec48(SANMVideoContext *ctx, int width, int height)
 static int process_frame_obj(SANMVideoContext *ctx)
 {
     uint16_t parm2;
-    uint8_t  codec = bytestream2_get_byteu(&ctx->gb);
-    uint8_t  param = bytestream2_get_byteu(&ctx->gb);
-    int16_t  left  = bytestream2_get_le16u(&ctx->gb);
-    int16_t  top   = bytestream2_get_le16u(&ctx->gb);
-    uint16_t w     = bytestream2_get_le16u(&ctx->gb);
-    uint16_t h     = bytestream2_get_le16u(&ctx->gb);
-    bytestream2_skip(&ctx->gb, 2);
-    parm2 = bytestream2_get_le16u(&ctx->gb);
+    uint8_t  codec = bytestream2_get_byteu(ctx->gb);
+    uint8_t  param = bytestream2_get_byteu(ctx->gb);
+    int16_t  left  = bytestream2_get_le16u(ctx->gb);
+    int16_t  top   = bytestream2_get_le16u(ctx->gb);
+    uint16_t w     = bytestream2_get_le16u(ctx->gb);
+    uint16_t h     = bytestream2_get_le16u(ctx->gb);
+    bytestream2_skip(ctx->gb, 2);
+    parm2 = bytestream2_get_le16u(ctx->gb);
 
     if (w < 1 || h < 1 || w > 800 || h > 600 || left > 800 || top > 600) {
         av_log(ctx->avctx, AV_LOG_WARNING,
@@ -1679,8 +1679,8 @@ static int process_xpal(SANMVideoContext *ctx, int size)
     uint8_t c[3];
     int i, j;
 
-    bytestream2_skip(&ctx->gb, 2);
-    cmd = bytestream2_get_be16(&ctx->gb);
+    bytestream2_skip(ctx->gb, 2);
+    cmd = bytestream2_get_be16(ctx->gb);
 
     if (cmd == 1) {
         for (i = 0; i < PALETTE_DELTA; i += 3) {
@@ -1700,11 +1700,11 @@ static int process_xpal(SANMVideoContext *ctx, int size)
             return AVERROR_INVALIDDATA;
         }
         for (i = 0; i < PALETTE_DELTA; i++)
-            dp[i] = bytestream2_get_le16u(&ctx->gb);
+            dp[i] = bytestream2_get_le16u(ctx->gb);
 
         if (size >= PALETTE_DELTA * 2 + 4 + PALETTE_SIZE * 3) {
             for (i = 0; i < PALETTE_SIZE; i++)
-                ctx->pal[i] = 0xFFU << 24 | bytestream2_get_be24u(&ctx->gb);
+                ctx->pal[i] = 0xFFU << 24 | bytestream2_get_be24u(ctx->gb);
             if (ctx->subversion < 2)
                 ctx->pal[0] = 0xFFU << 24;
         }
@@ -1717,13 +1717,13 @@ static int decode_0(SANMVideoContext *ctx)
     uint16_t *frm = ctx->frm0;
     int x, y;
 
-    if (bytestream2_get_bytes_left(&ctx->gb) < ctx->width * ctx->height * 2) {
+    if (bytestream2_get_bytes_left(ctx->gb) < ctx->width * ctx->height * 2) {
         av_log(ctx->avctx, AV_LOG_ERROR, "Insufficient data for raw frame.\n");
         return AVERROR_INVALIDDATA;
     }
     for (y = 0; y < ctx->height; y++) {
         for (x = 0; x < ctx->width; x++)
-            frm[x] = bytestream2_get_le16u(&ctx->gb);
+            frm[x] = bytestream2_get_le16u(ctx->gb);
         frm += ctx->pitch;
     }
     return 0;
@@ -1793,10 +1793,10 @@ static int opcode_0xf7(SANMVideoContext *ctx, int cx, int cy, int block_size, pt
     if (block_size == 2) {
         uint32_t indices;
 
-        if (bytestream2_get_bytes_left(&ctx->gb) < 4)
+        if (bytestream2_get_bytes_left(ctx->gb) < 4)
             return AVERROR_INVALIDDATA;
 
-        indices        = bytestream2_get_le32u(&ctx->gb);
+        indices        = bytestream2_get_le32u(ctx->gb);
         dst[0]         = ctx->codebook[indices & 0xFF];
         indices      >>= 8;
         dst[1]         = ctx->codebook[indices & 0xFF];
@@ -1808,12 +1808,12 @@ static int opcode_0xf7(SANMVideoContext *ctx, int cx, int cy, int block_size, pt
         uint16_t fgcolor, bgcolor;
         int glyph;
 
-        if (bytestream2_get_bytes_left(&ctx->gb) < 3)
+        if (bytestream2_get_bytes_left(ctx->gb) < 3)
             return AVERROR_INVALIDDATA;
 
-        glyph   = bytestream2_get_byteu(&ctx->gb);
-        bgcolor = ctx->codebook[bytestream2_get_byteu(&ctx->gb)];
-        fgcolor = ctx->codebook[bytestream2_get_byteu(&ctx->gb)];
+        glyph   = bytestream2_get_byteu(ctx->gb);
+        bgcolor = ctx->codebook[bytestream2_get_byteu(ctx->gb)];
+        fgcolor = ctx->codebook[bytestream2_get_byteu(ctx->gb)];
 
         draw_glyph(ctx, dst, glyph, fgcolor, bgcolor, block_size, pitch);
     }
@@ -1825,23 +1825,23 @@ static int opcode_0xf8(SANMVideoContext *ctx, int cx, int cy, int block_size, pt
     uint16_t *dst = ctx->frm0 + cx + cy * ctx->pitch;
 
     if (block_size == 2) {
-        if (bytestream2_get_bytes_left(&ctx->gb) < 8)
+        if (bytestream2_get_bytes_left(ctx->gb) < 8)
             return AVERROR_INVALIDDATA;
 
-        dst[0]         = bytestream2_get_le16u(&ctx->gb);
-        dst[1]         = bytestream2_get_le16u(&ctx->gb);
-        dst[pitch]     = bytestream2_get_le16u(&ctx->gb);
-        dst[pitch + 1] = bytestream2_get_le16u(&ctx->gb);
+        dst[0]         = bytestream2_get_le16u(ctx->gb);
+        dst[1]         = bytestream2_get_le16u(ctx->gb);
+        dst[pitch]     = bytestream2_get_le16u(ctx->gb);
+        dst[pitch + 1] = bytestream2_get_le16u(ctx->gb);
     } else {
         uint16_t fgcolor, bgcolor;
         int glyph;
 
-        if (bytestream2_get_bytes_left(&ctx->gb) < 5)
+        if (bytestream2_get_bytes_left(ctx->gb) < 5)
             return AVERROR_INVALIDDATA;
 
-        glyph   = bytestream2_get_byteu(&ctx->gb);
-        bgcolor = bytestream2_get_le16u(&ctx->gb);
-        fgcolor = bytestream2_get_le16u(&ctx->gb);
+        glyph   = bytestream2_get_byteu(ctx->gb);
+        bgcolor = bytestream2_get_le16u(ctx->gb);
+        fgcolor = bytestream2_get_le16u(ctx->gb);
 
         draw_glyph(ctx, dst, glyph, fgcolor, bgcolor, block_size, pitch);
     }
@@ -1869,10 +1869,10 @@ static int codec2subblock(SANMVideoContext *ctx, int cx, int cy, int blk_size)
     int16_t mx, my, index;
     int opcode;
 
-    if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+    if (bytestream2_get_bytes_left(ctx->gb) < 1)
         return AVERROR_INVALIDDATA;
 
-    opcode = bytestream2_get_byteu(&ctx->gb);
+    opcode = bytestream2_get_byteu(ctx->gb);
 
     switch (opcode) {
     default:
@@ -1886,9 +1886,9 @@ static int codec2subblock(SANMVideoContext *ctx, int cx, int cy, int blk_size)
         }
         break;
     case 0xF5:
-        if (bytestream2_get_bytes_left(&ctx->gb) < 2)
+        if (bytestream2_get_bytes_left(ctx->gb) < 2)
             return AVERROR_INVALIDDATA;
-        index = bytestream2_get_le16u(&ctx->gb);
+        index = bytestream2_get_le16u(ctx->gb);
 
         mx = index % ctx->width;
         my = index / ctx->width;
@@ -1919,16 +1919,16 @@ static int codec2subblock(SANMVideoContext *ctx, int cx, int cy, int blk_size)
                    ctx->small_codebook[opcode - 0xf9], blk_size, ctx->pitch);
         break;
     case 0xFD:
-        if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+        if (bytestream2_get_bytes_left(ctx->gb) < 1)
             return AVERROR_INVALIDDATA;
         fill_block(ctx->frm0 + cx + cy * ctx->pitch,
-                   ctx->codebook[bytestream2_get_byteu(&ctx->gb)], blk_size, ctx->pitch);
+                   ctx->codebook[bytestream2_get_byteu(ctx->gb)], blk_size, ctx->pitch);
         break;
     case 0xFE:
-        if (bytestream2_get_bytes_left(&ctx->gb) < 2)
+        if (bytestream2_get_bytes_left(ctx->gb) < 2)
             return AVERROR_INVALIDDATA;
         fill_block(ctx->frm0 + cx + cy * ctx->pitch,
-                   bytestream2_get_le16u(&ctx->gb), blk_size, ctx->pitch);
+                   bytestream2_get_le16u(ctx->gb), blk_size, ctx->pitch);
         break;
     case 0xFF:
         if (blk_size == 2) {
@@ -2001,12 +2001,12 @@ static int decode_6(SANMVideoContext *ctx)
     int npixels = ctx->npixels;
     uint16_t *frm = ctx->frm0;
 
-    if (bytestream2_get_bytes_left(&ctx->gb) < npixels) {
+    if (bytestream2_get_bytes_left(ctx->gb) < npixels) {
         av_log(ctx->avctx, AV_LOG_ERROR, "Insufficient data for frame.\n");
         return AVERROR_INVALIDDATA;
     }
     while (npixels--)
-        *frm++ = ctx->codebook[bytestream2_get_byteu(&ctx->gb)];
+        *frm++ = ctx->codebook[bytestream2_get_byteu(ctx->gb)];
 
     return 0;
 }
@@ -2044,38 +2044,38 @@ static int read_frame_header(SANMVideoContext *ctx, SANMFrameHeader *hdr)
 {
     int i, ret;
 
-    if ((ret = bytestream2_get_bytes_left(&ctx->gb)) < 560) {
+    if ((ret = bytestream2_get_bytes_left(ctx->gb)) < 560) {
         av_log(ctx->avctx, AV_LOG_ERROR, "Input frame too short (%d bytes).\n",
                ret);
         return AVERROR_INVALIDDATA;
     }
-    bytestream2_skip(&ctx->gb, 8); // skip pad
+    bytestream2_skip(ctx->gb, 8); // skip pad
 
-    hdr->width  = bytestream2_get_le32u(&ctx->gb);
-    hdr->height = bytestream2_get_le32u(&ctx->gb);
+    hdr->width  = bytestream2_get_le32u(ctx->gb);
+    hdr->height = bytestream2_get_le32u(ctx->gb);
 
     if (hdr->width != ctx->width || hdr->height != ctx->height) {
         avpriv_report_missing_feature(ctx->avctx, "Variable size frames");
         return AVERROR_PATCHWELCOME;
     }
 
-    hdr->seq_num     = bytestream2_get_le16u(&ctx->gb);
-    hdr->codec       = bytestream2_get_byteu(&ctx->gb);
-    hdr->rotate_code = bytestream2_get_byteu(&ctx->gb);
+    hdr->seq_num     = bytestream2_get_le16u(ctx->gb);
+    hdr->codec       = bytestream2_get_byteu(ctx->gb);
+    hdr->rotate_code = bytestream2_get_byteu(ctx->gb);
 
-    bytestream2_skip(&ctx->gb, 4); // skip pad
+    bytestream2_skip(ctx->gb, 4); // skip pad
 
     for (i = 0; i < 4; i++)
-        ctx->small_codebook[i] = bytestream2_get_le16u(&ctx->gb);
-    hdr->bg_color = bytestream2_get_le16u(&ctx->gb);
+        ctx->small_codebook[i] = bytestream2_get_le16u(ctx->gb);
+    hdr->bg_color = bytestream2_get_le16u(ctx->gb);
 
-    bytestream2_skip(&ctx->gb, 2); // skip pad
+    bytestream2_skip(ctx->gb, 2); // skip pad
 
-    hdr->rle_output_size = bytestream2_get_le32u(&ctx->gb);
+    hdr->rle_output_size = bytestream2_get_le32u(ctx->gb);
     for (i = 0; i < 256; i++)
-        ctx->codebook[i] = bytestream2_get_le16u(&ctx->gb);
+        ctx->codebook[i] = bytestream2_get_le16u(ctx->gb);
 
-    bytestream2_skip(&ctx->gb, 8); // skip pad
+    bytestream2_skip(ctx->gb, 8); // skip pad
 
     return 0;
 }
@@ -2114,10 +2114,12 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
                         int *got_frame_ptr, AVPacket *pkt)
 {
     SANMVideoContext *ctx = avctx->priv_data;
+    GetByteContext gb;
     int i, ret;
 
     ctx->frame = frame;
-    bytestream2_init(&ctx->gb, pkt->data, pkt->size);
+    bytestream2_init(&gb, pkt->data, pkt->size);
+    ctx->gb = &gb;
 
     if (!ctx->version) {
         int to_store = 0, have_pic = 0;
@@ -2126,15 +2128,15 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
         if (ctx->frm0_size)
             memset(ctx->frm0, 0, ctx->frm0_size);
 
-        while (bytestream2_get_bytes_left(&ctx->gb) >= 8) {
+        while (bytestream2_get_bytes_left(ctx->gb) >= 8) {
             uint32_t sig, size;
             int pos;
 
-            sig  = bytestream2_get_be32u(&ctx->gb);
-            size = bytestream2_get_be32u(&ctx->gb);
-            pos  = bytestream2_tell(&ctx->gb);
+            sig  = bytestream2_get_be32u(ctx->gb);
+            size = bytestream2_get_be32u(ctx->gb);
+            pos  = bytestream2_tell(ctx->gb);
 
-            if (bytestream2_get_bytes_left(&ctx->gb) < size) {
+            if (bytestream2_get_bytes_left(ctx->gb) < size) {
                 av_log(avctx, AV_LOG_ERROR, "Incorrect chunk size %"PRIu32".\n", size);
                 break;
             }
@@ -2146,7 +2148,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
                     return AVERROR_INVALIDDATA;
                 }
                 for (i = 0; i < PALETTE_SIZE; i++)
-                    ctx->pal[i] = 0xFFU << 24 | bytestream2_get_be24u(&ctx->gb);
+                    ctx->pal[i] = 0xFFU << 24 | bytestream2_get_be24u(ctx->gb);
                 if (ctx->subversion < 2)
                     ctx->pal[0] = 0xFFU << 24;
                 break;
@@ -2169,15 +2171,15 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
                 have_pic = 1;
                 break;
             default:
-                bytestream2_skip(&ctx->gb, size);
+                bytestream2_skip(ctx->gb, size);
                 av_log(avctx, AV_LOG_DEBUG,
                        "Unknown/unsupported chunk %"PRIx32".\n", sig);
                 break;
             }
 
-            bytestream2_seek(&ctx->gb, pos + size, SEEK_SET);
+            bytestream2_seek(ctx->gb, pos + size, SEEK_SET);
             if (size & 1)
-                bytestream2_skip(&ctx->gb, 1);
+                bytestream2_skip(ctx->gb, 1);
         }
         if (to_store)
             memcpy(ctx->stored_frame, ctx->frm0, ctx->buf_size);
-- 
2.48.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [FFmpeg-devel] [PATCH 12/12] avcodec/sanm: properly implement STOR/FTCH for ANIMv1
  2025-03-13 11:14 [FFmpeg-devel] [PATCH 00/12] avcodec/sanm: various improvements Manuel Lauss
                   ` (10 preceding siblings ...)
  2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 11/12] avcodec/sanm: change GetByteContext member to pointer Manuel Lauss
@ 2025-03-13 11:15 ` Manuel Lauss
  11 siblings, 0 replies; 13+ messages in thread
From: Manuel Lauss @ 2025-03-13 11:15 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Manuel Lauss

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.

Used extensively by Rebel Assault 1 for e.g. the cockpit: A STOR-ed
codec21 object which only covers parts of the buffer is FTCHed
as last object in the stack.

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>
---
 libavcodec/sanm.c | 84 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 80 insertions(+), 4 deletions(-)

diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index 207db4a8fb..da7fb4a9e0 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -1671,6 +1671,57 @@ static int process_frame_obj(SANMVideoContext *ctx)
     }
 }
 
+static int process_ftch(SANMVideoContext *ctx, int size)
+{
+    GetByteContext gb, *old_gb = ctx->gb;
+    uint8_t *sf = ctx->stored_frame;
+    int xoff, yoff, left, top, ret;
+    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);
+        ctx->gb = &gb;
+        ret = process_frame_obj(ctx);
+        ctx->gb = old_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)
 {
     int16_t *dp = ctx->delta_pal;
@@ -2158,6 +2209,29 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
                 if (ret = process_frame_obj(ctx))
                     return ret;
                 have_pic = 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;
             case MKBETAG('X', 'P', 'A', 'L'):
                 if (ret = process_xpal(ctx, size))
@@ -2167,8 +2241,12 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
                 to_store = 1;
                 break;
             case MKBETAG('F', 'T', 'C', 'H'):
-                memcpy(ctx->frm0, ctx->stored_frame, ctx->buf_size);
-                have_pic = 1;
+                if (ctx->subversion < 2) {
+                    if (ret = process_ftch(ctx, size))
+                        return ret;
+                } else {
+                    memcpy(ctx->frm0, ctx->stored_frame, ctx->buf_size);
+                }
                 break;
             default:
                 bytestream2_skip(ctx->gb, size);
@@ -2181,8 +2259,6 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
             if (size & 1)
                 bytestream2_skip(ctx->gb, 1);
         }
-        if (to_store)
-            memcpy(ctx->stored_frame, ctx->frm0, ctx->buf_size);
 
         if (have_pic && ctx->have_dimensions) {
             if ((ret = copy_output(ctx, NULL)))
-- 
2.48.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2025-03-13 11:18 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-03-13 11:14 [FFmpeg-devel] [PATCH 00/12] avcodec/sanm: various improvements Manuel Lauss
2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 01/12] avcodec/sanm: better frame size detection for old codecs Manuel Lauss
2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 02/12] avcodec/sanm: disable left/top for fullscreen codecs Manuel Lauss
2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 03/12] avcodec/sanm: FOBJ left/top are signed values Manuel Lauss
2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 04/12] avcodec/sanm: misc fixes Manuel Lauss
2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 05/12] avcodec/sanm: fix codec3 Manuel Lauss
2025-03-13 11:14 ` [FFmpeg-devel] [PATCH 06/12] avcodec/sanm: codec2 support Manuel Lauss
2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 07/12] avcodec/sanm: codec23 decoder Manuel Lauss
2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 08/12] avcodec/sanm: codec21 decoder Manuel Lauss
2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 09/12] avcodec/sanm: codec4/5/33/34 decoder Manuel Lauss
2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 10/12] avcodec/sanm: codec37: reimplement comp4 Manuel Lauss
2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 11/12] avcodec/sanm: change GetByteContext member to pointer Manuel Lauss
2025-03-13 11:15 ` [FFmpeg-devel] [PATCH 12/12] avcodec/sanm: properly implement STOR/FTCH for ANIMv1 Manuel Lauss

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git