[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