* [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