* [FFmpeg-devel] [PATCH 0/3] avcodec/sanm: Fixes for "StarWars - Making Magic" @ 2025-05-01 9:57 Manuel Lauss 2025-05-01 9:57 ` [FFmpeg-devel] [PATCH 1/3] avcodec/sanm: ignore codec48 compression type 6 Manuel Lauss ` (2 more replies) 0 siblings, 3 replies; 4+ messages in thread From: Manuel Lauss @ 2025-05-01 9:57 UTC (permalink / raw) To: ffmpeg-devel; +Cc: Manuel Lauss This patchset extends the SANM codec handler to support video of the 1996 CD-ROM Title "StarWars - Making Magic". The videos all consist of 640x480 codec3 backgrounds, with 320x240 codec48 videos put on top at random x/y offsets. #1: some video file advertise codec48 compression type 6. The actual data is for these stops after the codec headers, and the dos exe as well as the codec48 decoder in the MotS game exe don't know of it and simply ignore it. #2: change the FOBJ frame size determination to a whitelist. With the scheme employed by Making Magic, the codec48 dimensions can no longer be blindly trusted. #3: support video of Making Magic. This patch also brings the fobj handling more in line with what the game engines actually do. Tested with RA1, RA2 (c37), Outlaws (c47), MotS (c48) See https://ibb.co/73Pt803 for post/pre fix screenshots. Manuel Lauss (3): avcodec/sanm: ignore codec48 compression type 6 avcodec/sanm: add a whitelist for known FOBJ sizes avcodec/sanm: support "StarWars - Making Magic" video libavcodec/sanm.c | 98 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 28 deletions(-) -- 2.49.0 _______________________________________________ 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] 4+ messages in thread
* [FFmpeg-devel] [PATCH 1/3] avcodec/sanm: ignore codec48 compression type 6 2025-05-01 9:57 [FFmpeg-devel] [PATCH 0/3] avcodec/sanm: Fixes for "StarWars - Making Magic" Manuel Lauss @ 2025-05-01 9:57 ` Manuel Lauss 2025-05-01 9:58 ` [FFmpeg-devel] [PATCH 2/3] avcodec/sanm: add a whitelist for known FOBJ sizes Manuel Lauss 2025-05-01 9:58 ` [FFmpeg-devel] [PATCH 3/3] avcodec/sanm: support "StarWars - Making Magic" video Manuel Lauss 2 siblings, 0 replies; 4+ messages in thread From: Manuel Lauss @ 2025-05-01 9:57 UTC (permalink / raw) To: ffmpeg-devel; +Cc: Manuel Lauss Some videos of "StarWars - Making Magic" have this subcompression type: data just consists of the 16 byte codec48 header; the DOS player simply treats it like nothing to do. Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com> --- libavcodec/sanm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c index 6b1da2e30c..e436b5ab8e 100644 --- a/libavcodec/sanm.c +++ b/libavcodec/sanm.c @@ -1586,7 +1586,8 @@ static int old_codec48(SANMVideoContext *ctx, int width, int height) return AVERROR_INVALIDDATA; codec47_comp1(ctx, dst, width, height, width); break; - + case 6: /* in some videos, but does nothing */ + break; default: avpriv_report_missing_feature(ctx->avctx, "Subcodec 48 compression %d", compr); -- 2.49.0 _______________________________________________ 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] 4+ messages in thread
* [FFmpeg-devel] [PATCH 2/3] avcodec/sanm: add a whitelist for known FOBJ sizes 2025-05-01 9:57 [FFmpeg-devel] [PATCH 0/3] avcodec/sanm: Fixes for "StarWars - Making Magic" Manuel Lauss 2025-05-01 9:57 ` [FFmpeg-devel] [PATCH 1/3] avcodec/sanm: ignore codec48 compression type 6 Manuel Lauss @ 2025-05-01 9:58 ` Manuel Lauss 2025-05-01 9:58 ` [FFmpeg-devel] [PATCH 3/3] avcodec/sanm: support "StarWars - Making Magic" video Manuel Lauss 2 siblings, 0 replies; 4+ messages in thread From: Manuel Lauss @ 2025-05-01 9:58 UTC (permalink / raw) To: ffmpeg-devel; +Cc: Manuel Lauss Change the size detection a bit to recognize common video sizes, as the FOBJ codecs>=37 cannot always be trusted. Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com> --- libavcodec/sanm.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c index e436b5ab8e..63f5c2cc7f 100644 --- a/libavcodec/sanm.c +++ b/libavcodec/sanm.c @@ -1603,6 +1603,7 @@ static int process_frame_obj(SANMVideoContext *ctx, GetByteContext *gb) uint16_t w, h, parm2; uint8_t codec, param; int16_t left, top; + int fsc, sote; codec = bytestream2_get_byteu(gb); param = bytestream2_get_byteu(gb); @@ -1620,6 +1621,14 @@ static int process_frame_obj(SANMVideoContext *ctx, GetByteContext *gb) return 0; } + /* codecs with their own buffers */ + fsc = (codec == 37 || codec == 47 || codec == 48); + + /* special case for "Shadows of the Empire" videos */ + sote = ((w == 640) && (h == 272) && (codec == 47)); + if (sote) + left = top = 0; + if (!ctx->have_dimensions) { int xres, yres; if (ctx->subversion < 2) { @@ -1633,12 +1642,24 @@ static int process_frame_obj(SANMVideoContext *ctx, GetByteContext *gb) yres = h; ctx->have_dimensions = 1; } else { - /* Rebel Assault 2: 424x260 internal size */ - if (((left + w) == 424) && ((top + h) == 260)) + /* detect common sizes */ + xres = w + left; + yres = h + top; + if (sote) { + /* SotE: has top=60 at all times to center video + * inside the 640x480 game window + */ + xres = w; + yres = h; ctx->have_dimensions = 1; + } else if (((xres == 424) && (yres == 260)) || /* RA1 */ + ((xres == 320) && (yres == 200)) || /* ft/dig/... */ + ((xres == 640) && (yres == 480))) { /* ol/comi/mots... */ + ctx->have_dimensions = 1; + } - xres = FFMAX(left + w, ctx->width); - yres = FFMAX(top + h, ctx->height); + xres = FFMAX(xres, ctx->width); + yres = FFMAX(yres, ctx->height); } if (ctx->width < xres || ctx->height < yres) { @@ -1652,8 +1673,7 @@ static int process_frame_obj(SANMVideoContext *ctx, GetByteContext *gb) } } } else { - if (((left + w > ctx->width) || (top + h > ctx->height)) - && (codec >= 37)) { + if (((left + w > ctx->width) || (top + h > ctx->height)) && fsc) { /* 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 @@ -1671,7 +1691,7 @@ static int process_frame_obj(SANMVideoContext *ctx, GetByteContext *gb) */ if (ctx->first_fob) { ctx->first_fob = 0; - if (codec < 37) + if (!fsc) memset(ctx->frm0, 0, ctx->frm0_size); } -- 2.49.0 _______________________________________________ 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] 4+ messages in thread
* [FFmpeg-devel] [PATCH 3/3] avcodec/sanm: support "StarWars - Making Magic" video 2025-05-01 9:57 [FFmpeg-devel] [PATCH 0/3] avcodec/sanm: Fixes for "StarWars - Making Magic" Manuel Lauss 2025-05-01 9:57 ` [FFmpeg-devel] [PATCH 1/3] avcodec/sanm: ignore codec48 compression type 6 Manuel Lauss 2025-05-01 9:58 ` [FFmpeg-devel] [PATCH 2/3] avcodec/sanm: add a whitelist for known FOBJ sizes Manuel Lauss @ 2025-05-01 9:58 ` Manuel Lauss 2 siblings, 0 replies; 4+ messages in thread From: Manuel Lauss @ 2025-05-01 9:58 UTC (permalink / raw) To: ffmpeg-devel; +Cc: Manuel Lauss The title "StarWars - Making Magic" consists of 640x480 codec3 frames, with a 320x240 codec48 video put on top of that at random x/y offsets. To support this, a new default buffer "fbuf", which holds the final image to be presented, is added, since codec37/47/48 need their buffers need to be private to themselves. The decoded result is then copied to the fbuf, honoring the x/y offsets if required. Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com> --- libavcodec/sanm.c | 63 +++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c index 63f5c2cc7f..0af0527ce6 100644 --- a/libavcodec/sanm.c +++ b/libavcodec/sanm.c @@ -274,9 +274,9 @@ typedef struct SANMVideoContext { int prev_seq; AVFrame *frame; - uint16_t *frm0, *frm1, *frm2; + uint16_t *fbuf, *frm0, *frm1, *frm2; uint8_t *stored_frame; - uint32_t frm0_size, frm1_size, frm2_size; + uint32_t fbuf_size, frm0_size, frm1_size, frm2_size; uint32_t stored_frame_size; uint8_t *rle_buf; @@ -453,6 +453,7 @@ static void init_sizes(SANMVideoContext *ctx, int width, int height) static void destroy_buffers(SANMVideoContext *ctx) { + av_freep(&ctx->fbuf); av_freep(&ctx->frm0); av_freep(&ctx->frm1); av_freep(&ctx->frm2); @@ -466,6 +467,7 @@ static void destroy_buffers(SANMVideoContext *ctx) static av_cold int init_buffers(SANMVideoContext *ctx) { + av_fast_padded_mallocz(&ctx->fbuf, &ctx->fbuf_size, ctx->buf_size); av_fast_padded_mallocz(&ctx->frm0, &ctx->frm0_size, ctx->buf_size); av_fast_padded_mallocz(&ctx->frm1, &ctx->frm1_size, ctx->buf_size); av_fast_padded_mallocz(&ctx->frm2, &ctx->frm2_size, ctx->buf_size); @@ -674,7 +676,7 @@ static int old_codec4(SANMVideoContext *ctx, GetByteContext *gb, int top, int le { const uint16_t p = ctx->pitch; const uint32_t maxpxo = ctx->height * p; - uint8_t mask, bits, idx, *gs, *dst = (uint8_t *)ctx->frm0; + uint8_t mask, bits, idx, *gs, *dst = (uint8_t *)ctx->fbuf; int i, j, k, l, bit, ret; int32_t pxoff, pxo2; @@ -805,7 +807,7 @@ static int old_codec23(SANMVideoContext *ctx, GetByteContext *gb, int top, int l if (bytestream2_get_bytes_left(gb) < 1) return 0; /* some c23 frames just set up the LUT */ - dst = (uint8_t *)ctx->frm0; + dst = (uint8_t *)ctx->fbuf; for (i = 0; i < height; i++) { if (bytestream2_get_bytes_left(gb) < 2) return 0; @@ -840,10 +842,10 @@ static int old_codec21(SANMVideoContext *ctx, GetByteContext *gb, int top, int l int width, int height) { const uint32_t maxpxo = ctx->height * ctx->pitch; - uint8_t *dst = (uint8_t *)ctx->frm0, c; + uint8_t *dst = (uint8_t *)ctx->fbuf, c; int i, j, k, pc, sk, pxoff; - dst = (uint8_t *)ctx->frm0; + dst = (uint8_t *)ctx->fbuf; for (i = 0; i < height; i++) { if (bytestream2_get_bytes_left(gb) < 2) return 0; @@ -884,7 +886,7 @@ static int old_codec1(SANMVideoContext *ctx, GetByteContext *gb, int top, { int i, j, len, flag, code, val, end, pxoff; const int maxpxo = ctx->height * ctx->pitch; - uint8_t *dst = (uint8_t *)ctx->frm0; + uint8_t *dst = (uint8_t *)ctx->fbuf; for (i = 0; i < height; i++) { if (bytestream2_get_bytes_left(gb) < 2) @@ -932,7 +934,7 @@ static int old_codec1(SANMVideoContext *ctx, GetByteContext *gb, int top, static int old_codec2(SANMVideoContext *ctx, GetByteContext *gb, int top, int left, int width, int height) { - uint8_t *dst = (uint8_t *)ctx->frm0, col; + uint8_t *dst = (uint8_t *)ctx->fbuf, col; int16_t xpos = left, ypos = top; while (bytestream2_get_bytes_left(gb) > 3) { @@ -949,7 +951,7 @@ static int old_codec2(SANMVideoContext *ctx, GetByteContext *gb, int top, static int old_codec20(SANMVideoContext *ctx, int w, int h) { - uint8_t *dst = (uint8_t *)ctx->frm0; + uint8_t *dst = (uint8_t *)ctx->fbuf; if (bytestream2_get_bytes_left(&ctx->gb) < w * h) return AVERROR_INVALIDDATA; @@ -1008,10 +1010,10 @@ static int old_codec37(SANMVideoContext *ctx, int width, int height) ctx->rotate_code = 0; if (((seq & 1) || !(flags & 1)) && (compr && compr != 2)) { - FFSWAP(uint16_t*, ctx->frm1, ctx->frm2); + FFSWAP(uint16_t*, ctx->frm0, ctx->frm2); } - dst = ((uint8_t*)ctx->frm1); + dst = ((uint8_t*)ctx->frm0); prev = ((uint8_t*)ctx->frm2); if (mvoff > 2) { @@ -1145,7 +1147,6 @@ static int old_codec37(SANMVideoContext *ctx, int width, int height) return AVERROR_PATCHWELCOME; } - memcpy(ctx->frm0, ctx->frm1, ctx->buf_size); return 0; } @@ -1603,7 +1604,7 @@ static int process_frame_obj(SANMVideoContext *ctx, GetByteContext *gb) uint16_t w, h, parm2; uint8_t codec, param; int16_t left, top; - int fsc, sote; + int fsc, sote, ret; codec = bytestream2_get_byteu(gb); param = bytestream2_get_byteu(gb); @@ -1687,12 +1688,12 @@ static int process_frame_obj(SANMVideoContext *ctx, GetByteContext *gb) } /* on first FOBJ, when the codec is not one of the - * full-buffer codecs (37/47/48), frm0 needs to be cleared. + * full-buffer codecs (37/47/48), fbuf needs to be cleared. */ if (ctx->first_fob) { ctx->first_fob = 0; if (!fsc) - memset(ctx->frm0, 0, ctx->frm0_size); + memset(ctx->fbuf, 0, ctx->frm0_size); } switch (codec) { @@ -1713,18 +1714,38 @@ static int process_frame_obj(SANMVideoContext *ctx, GetByteContext *gb) case 23: return old_codec23(ctx, gb, top, left, w, h, param, parm2); case 37: - return old_codec37(ctx, w, h); + ret = old_codec37(ctx, w, h); break; case 45: return 0; case 47: - return old_codec47(ctx, w, h); + ret = old_codec47(ctx, w, h); break; case 48: - return old_codec48(ctx, w, h); + ret = old_codec48(ctx, w, h); break; default: avpriv_request_sample(ctx->avctx, "Subcodec %d", codec); ctx->frame->flags |= AV_FRAME_FLAG_CORRUPT; return 0; } + if (ret) + return ret; + + /* copy the codec37/47/48 result to main buffer */ + if ((w == ctx->width) && (h == ctx->height)) { + memcpy(ctx->fbuf, ctx->frm0, ctx->fbuf_size); + } else { + uint8_t *dst = (uint8_t *)ctx->fbuf + left + top * ctx->pitch; + const uint8_t *src = (uint8_t *)ctx->frm0; + int cw = FFMIN(w, ctx->width - left); + int ch = FFMIN(h, ctx->height - top); + if ((cw > 0) && (ch > 0) && (left > 0) && (top > 0)) { + for (int i = 0; i < ch; i++) { + memcpy(dst, src, cw); + dst += ctx->pitch; + src += w; + } + } + } + return 0; } static int process_ftch(SANMVideoContext *ctx, int size) @@ -2196,7 +2217,7 @@ static void fill_frame(uint16_t *pbuf, int buf_size, uint16_t color) static int copy_output(SANMVideoContext *ctx, SANMFrameHeader *hdr) { uint8_t *dst; - const uint8_t *src = (uint8_t*) ctx->frm0; + const uint8_t *src = hdr ? (uint8_t *)ctx->frm0 : (uint8_t *)ctx->fbuf; int ret, height = ctx->height; ptrdiff_t dstpitch, srcpitch = ctx->pitch * (hdr ? sizeof(ctx->frm0[0]) : 1); @@ -2279,7 +2300,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, ret = AVERROR(ENOMEM); } } else { - memcpy(ctx->stored_frame, ctx->frm0, ctx->buf_size); + memcpy(ctx->stored_frame, ctx->fbuf, ctx->buf_size); } } break; @@ -2295,7 +2316,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (ret = process_ftch(ctx, size)) return ret; } else { - memcpy(ctx->frm0, ctx->stored_frame, ctx->buf_size); + memcpy(ctx->fbuf, ctx->stored_frame, ctx->buf_size); } have_img = 1; break; -- 2.49.0 _______________________________________________ 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] 4+ messages in thread
end of thread, other threads:[~2025-05-01 9:58 UTC | newest] Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2025-05-01 9:57 [FFmpeg-devel] [PATCH 0/3] avcodec/sanm: Fixes for "StarWars - Making Magic" Manuel Lauss 2025-05-01 9:57 ` [FFmpeg-devel] [PATCH 1/3] avcodec/sanm: ignore codec48 compression type 6 Manuel Lauss 2025-05-01 9:58 ` [FFmpeg-devel] [PATCH 2/3] avcodec/sanm: add a whitelist for known FOBJ sizes Manuel Lauss 2025-05-01 9:58 ` [FFmpeg-devel] [PATCH 3/3] avcodec/sanm: support "StarWars - Making Magic" video 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