On Thu, 2 Jan 2025, Manuel Lauss wrote:

Hello,

Ping?   With this patch the 2 sample files at
http://samples.mplayerhq.hu/game-formats/la-san/jediknight-sith/  play
just fine.

Will apply.

Thanks,
Marton


Thanks,
     Manuel

On Mon, Dec 16, 2024 at 5:08 PM Manuel Lauss <manuel.la...@gmail.com> wrote:

Adds a decoder for the SMUSH codec48 video encoding, as is used by
the LucasArts game "Mysteries of the Sith".

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

diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index 8bcffb1e90..a278ef24b1 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -995,6 +995,227 @@ static int old_codec47(SANMVideoContext *ctx, int top,
     return 0;
 }

+// scale 4x4 input block to an 8x8 output block
+static void c48_4to8(uint8_t *dst, const uint8_t *src, const uint16_t w)
+{
+    uint16_t p;
+    // dst is always at least 16bit aligned
+    for (int i = 0; i < 4; i++) {
+        for (int j = 0; j < 8; j += 2) {
+            p = *src++;
+            p = (p << 8) | p;
+            *((uint16_t *)(dst + w * 0 + j)) = p;
+            *((uint16_t *)(dst + w * 1 + j)) = p;
+        }
+        dst += w * 2;
+    }
+}
+
+static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
+                         const uint16_t w)
+{
+    uint8_t opc, sb[16];
+    int i, j, k, l;
+    int16_t mvofs;
+    uint32_t ofs;
+
+    if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+        return 1;
+
+    opc = bytestream2_get_byteu(&ctx->gb);
+    switch (opc) {
+    case 0xFF:    // 1x1 -> 8x8 block scale
+        if (bytestream2_get_bytes_left(&ctx->gb) < 1)
+            return 1;
+
+        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)
+            return 1;
+        mvofs =  bytestream2_get_le16(&ctx->gb);
+        for (i = 0; i < 8; i++) {
+            ofs = w * i;
+            for (k = 0; k < 8; k++)
+                *(dst + ofs + k) = *(db + ofs + k + mvofs);
+        }
+        break;
+    case 0xFD:    // 2x2 -> 8x8 block scale
+        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[0] = sb[1] = sb[4] = sb[5];
+        sb[2] = sb[3] = sb[6] = sb[7];
+        sb[8] = sb[9] = sb[12] = sb[13];
+        sb[10] = sb[11] = sb[14] = sb[15];
+        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)
+            return 1;
+        for (i = 0; i < 8; i += 4) {
+            for (k = 0; k < 8; k += 4) {
+                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;
+                    for (l = 0; l < 4; l++)
+                        *(dst + ofs + l) = *(db + ofs + l + mvofs);
+                }
+            }
+        }
+        break;
+    case 0xFB:    // Copy 4x 4x4 blocks, per-block mv from source
+        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);
+                for (j = 0; j < 4; j++) {
+                    ofs = (w * (j + i)) + k;
+                    for (l = 0; l < 4; l++)
+                        *(dst + ofs + l) = *(db + ofs + l + mvofs);
+                }
+            }
+        }
+        break;
+    case 0xFA:    // scale 4x4 input block to 8x8 dest block
+        if (bytestream2_get_bytes_left(&ctx->gb) < 16)
+            return 1;
+        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)
+            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);
+                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);
+                    *(dst + ofs + l + w) = *(db + ofs + l + w + mvofs);
+                }
+            }
+        }
+        break;
+    case 0xF8:    // 16x 2x2 blocks copy, 16bit mv from source
+        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);
+                for (l = 0; l < 2; l++) {
+                    *(dst + ofs + l + 0) = *(db + ofs + l + 0 + mvofs);
+                    *(dst + ofs + l + w) = *(db + ofs + l + w + mvofs);
+                }
+            }
+        }
+        break;
+    case 0xF7:    // copy 8x8 block from src to dest
+        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);
+        }
+        break;
+    default:    // copy 8x8 block from prev, c37_mv from source
+        mvofs = c37_mv[opc * 2] + (c37_mv[opc * 2 + 1] * w);
+        for (i = 0; i < 8; i++) {
+            ofs = i * w;
+            for (l = 0; l < 8; l++)
+                *(dst + ofs + l) = *(db + ofs + l + mvofs);
+        }
+        break;
+    }
+    return 0;
+}
+
+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 i, j, flags;
+
+    // all codec48 videos use 1, but just to be safe...
+    if (mvidx != 1) {
+        av_log(ctx->avctx, AV_LOG_ERROR, "Invalid motion base value %d.\n", 
mvidx);
+        return AVERROR_INVALIDDATA;
+    }
+
+    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)
+            return AVERROR_INVALIDDATA;
+        codec47_read_interptable(ctx);
+    }
+
+    dst  = (uint8_t*)ctx->frm0;
+    prev = (uint8_t*)ctx->frm2;
+
+    if (!seq) {
+        ctx->prev_seq = -1;
+        memset(prev, 0, ctx->aligned_height * width);
+    }
+
+    switch (compr) {
+    case 0:
+        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);
+            dst += width;
+        }
+        break;
+    case 2:
+        if (rle_decode(ctx, dst, decoded_size))
+            return AVERROR_INVALIDDATA;
+        break;
+    case 3:
+        if (seq == ctx->prev_seq + 1) {
+            for (j = 0; j < height; j += 8) {
+                for (i = 0; i < width; i += 8) {
+                    if (codec48_block(ctx, dst + i, prev + i, width))
+                        return AVERROR_INVALIDDATA;
+                }
+                dst += width * 8;
+                prev += width * 8;
+            }
+        }
+        break;
+    case 5:
+        if (bytestream2_get_bytes_left(&ctx->gb) < ((width + 1) >> 1) * ((height + 
1) >> 1))
+            return AVERROR_INVALIDDATA;
+        codec47_comp1(ctx, dst, width, height, width);
+        break;
+
+    default:
+        avpriv_report_missing_feature(ctx->avctx,
+                                      "Subcodec 48 compression %d", compr);
+        return AVERROR_PATCHWELCOME;
+    }
+    ctx->rotate_code = 1;    // swap frm[0] and frm[2]
+    ctx->prev_seq = seq;
+    return 0;
+}
+
 static int process_frame_obj(SANMVideoContext *ctx)
 {
     uint16_t codec = bytestream2_get_le16u(&ctx->gb);
@@ -1030,6 +1251,8 @@ static int process_frame_obj(SANMVideoContext *ctx)
         return old_codec37(ctx, top, left, w, h);
     case 47:
         return old_codec47(ctx, top, left, w, h);
+    case 48:
+        return old_codec48(ctx, w, h);
     default:
         avpriv_request_sample(ctx->avctx, "Subcodec %d", codec);
         return AVERROR_PATCHWELCOME;
--
2.47.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-requ...@ffmpeg.org with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

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

Reply via email to