Hello, Ping? With this patch the 2 sample files at http://samples.mplayerhq.hu/game-formats/la-san/jediknight-sith/ play just fine.
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".