[FFmpeg-devel] [PATCH] sanm.c: Add partial support for JediKnight .san video.

Reimar Döffinger Reimar.Doeffinger at gmx.de
Sat Apr 2 11:20:37 CEST 2016


I don't have the binary decoder, and this is as far as I got
with pure guessing and only 2 samples.
Intra frames seem to work, and full-block inter updates work
as well. Partial/special coded blocks I could not figure out.
Samples:
http://samples.mplayerhq.hu/game-formats/la-san/jediknight-sith/
---
 libavcodec/sanm.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 155 insertions(+), 1 deletion(-)

diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index 1aa002b..2eb7e44 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -947,6 +947,158 @@ static int old_codec47(SANMVideoContext *ctx, int top,
     return 0;
 }
 
+// Currently mostly a copy of old_codec47, but there might
+// be further differences to be discovered.
+static int old_codec48(SANMVideoContext *ctx, int top,
+                       int left, int width, int height)
+{
+    uint32_t decoded_size;
+    int i, j;
+    int stride     = ctx->pitch;
+    uint8_t *dst   = (uint8_t *)ctx->frm0 + left + top * stride;
+    uint8_t *prev1 = (uint8_t *)ctx->frm1;
+    uint8_t *prev2 = (uint8_t *)ctx->frm2;
+    int tbl_pos = bytestream2_tell(&ctx->gb);
+    int skip;
+    int compr   = bytestream2_get_byte(&ctx->gb);
+    int new_rot = bytestream2_get_byte(&ctx->gb);
+    int seq     = bytestream2_get_le16(&ctx->gb);
+    // Not sure which is which...
+    decoded_size = bytestream2_get_le32(&ctx->gb);
+    decoded_size = bytestream2_get_le32(&ctx->gb);
+    skip = bytestream2_get_byte(&ctx->gb) >> 3;
+    bytestream2_get_le24(&ctx->gb); // unknown
+
+    if (decoded_size > ctx->height * stride - left - top * stride) {
+        decoded_size = ctx->height * stride - left - top * stride;
+        av_log(ctx->avctx, AV_LOG_WARNING, "Decoded size is too large.\n");
+    }
+
+    if (skip & 1)
+        bytestream2_skip(&ctx->gb, 0x8080);
+    if (!seq) {
+        ctx->prev_seq = -1;
+        memset(prev1, 0, ctx->height * stride);
+        memset(prev2, 0, ctx->height * stride);
+    }
+
+    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 += stride;
+        }
+        break;
+    case 1:
+        if (bytestream2_get_bytes_left(&ctx->gb) < ((width + 1) >> 1) * ((height + 1) >> 1))
+            return AVERROR_INVALIDDATA;
+        for (j = 0; j < height; j += 2) {
+            for (i = 0; i < width; i += 2) {
+                dst[i] =
+                dst[i + 1] =
+                dst[stride + i] =
+                dst[stride + i + 1] = bytestream2_get_byteu(&ctx->gb);
+            }
+            dst += stride * 2;
+        }
+        break;
+    case 2:
+        if (seq == ctx->prev_seq + 1) {
+            for (j = 0; j < height; j += 8) {
+                for (i = 0; i < width; i += 8)
+                    if (process_block(ctx, dst + i, prev1 + i, prev2 + i, stride,
+                                      tbl_pos + 8, 8))
+                        return AVERROR_INVALIDDATA;
+                dst   += stride * 8;
+                prev1 += stride * 8;
+                prev2 += stride * 8;
+            }
+        }
+        break;
+    case 3:
+        memcpy(ctx->frm0, ctx->frm2, ctx->pitch * ctx->height);
+        avpriv_report_missing_feature(ctx->avctx,
+                                      "Subcodec 48 compression 3 only partially supported");
+        for (j = 0; j < height; j += 8) {
+            for (i = 0; i < width; i += 8) {
+                int k;
+                int b = bytestream2_get_byte(&ctx->gb);
+                if (b == 0)
+                    continue;
+                // TODO: only 0xf7 decodes correctly, all others
+                // only have the correct input length
+                // 0xf9 and 0xf8 give approximately visually correct output
+                // for s4l3ocs.san but are absolutely not correct.
+                switch (b) {
+                case 0xfe:
+                    bytestream2_get_buffer(&ctx->gb, dst + i, 2);
+                    break;
+                case 0xfd:
+                    bytestream2_get_buffer(&ctx->gb, dst + i, 4);
+                    break;
+                case 0xfc:
+                    bytestream2_get_buffer(&ctx->gb, dst + i, 4);
+                    break;
+                case 0xfb:
+                    bytestream2_get_buffer(&ctx->gb, dst + i, 8);
+                    break;
+                case 0xf9:
+                    for (k = 0; k < 8; k += 2)
+                    {
+                        uint8_t *d = dst + i + k * stride;
+                        int l;
+                        for (l = 0; l < 8; l += 2) {
+                            uint8_t in = bytestream2_get_byte(&ctx->gb);
+                            if (in) { d[l] = d[l + 1] = d[l + stride] = d[l + stride + 1] = 0x32; }
+                        }
+                    }
+                    break;
+                case 0xf8:
+                    for (k = 0; k < 8; k += 2)
+                    {
+                        uint8_t *d = dst + i + k * stride;
+                        int l;
+                        for (l = 0; l < 8; l++) {
+                            uint8_t in = bytestream2_get_byte(&ctx->gb);
+                            if (in >> 4) d[l] = 0x4b;
+                            if (in & 0xf) d[l + stride] = 0x4b;
+                        }
+                    }
+                    break;
+                case 0xf7:
+                    for (k = 0; k < 8; k++)
+                        bytestream2_get_buffer(&ctx->gb, dst + i + k * stride, 8);
+                    break;
+                default:
+                    break;
+                }
+            }
+            dst += stride * 8;
+        }
+        break;
+    case 4:
+        memcpy(ctx->frm0, ctx->frm1, ctx->pitch * ctx->height);
+        break;
+    case 5:
+        if (rle_decode(ctx, dst, decoded_size))
+            return AVERROR_INVALIDDATA;
+        break;
+    default:
+        avpriv_report_missing_feature(ctx->avctx,
+                                      "Subcodec 48 compression %d", compr);
+        return AVERROR_PATCHWELCOME;
+    }
+    if (seq == ctx->prev_seq + 1)
+        ctx->rotate_code = new_rot;
+    else
+        ctx->rotate_code = 0;
+    ctx->prev_seq = seq;
+
+    return 0;
+}
+
 static int process_frame_obj(SANMVideoContext *ctx)
 {
     uint16_t codec = bytestream2_get_le16u(&ctx->gb);
@@ -985,8 +1137,10 @@ static int process_frame_obj(SANMVideoContext *ctx)
     case 47:
         return old_codec47(ctx, top, left, w, h);
         break;
+    case 48:
+        return old_codec48(ctx, top, left, w, h);
     default:
-        avpriv_request_sample(ctx->avctx, "Subcodec %d", codec);
+        avpriv_request_sample(ctx->avctx, "Subcodec %d fobj", codec);
         return AVERROR_PATCHWELCOME;
     }
 }
-- 
2.7.0



More information about the ffmpeg-devel mailing list