Super Street Fighter II TURBO Movie Format

From ModdingWiki
Jump to navigation Jump to search
Super Street Fighter II TURBO Movie Format
Super Street Fighter II TURBO Movie Format.png
Format typeImage
HardwareVGA
Colour depth8-bit (VGA)
Minimum size (pixels)320x200
Maximum size (pixels)320x200
PaletteInternal
Plane count1
Transparent pixels?No
Hitmap pixels?No
Games

This format is used by Super Street Fighter II TURBO to store intro movies.

General

Each .FMV file is compressed with Rob Northern Compression, then with RLE Compression of delta frames.

Algorithm

using System.Drawing;

namespace SuperStreetFighter2Turbo;

public sealed class Fmv
{
    public Fmv(Stream stream)
    {
        Reader = new BinaryReader(stream);

        if (Reader.ReadInt32() != 0x564D4641)
        {
            throw new InvalidDataException();
        }

        FrameCount = Reader.ReadUInt16();

        Frame.AsSpan().Fill(Reader.ReadByte());

        for (var i = 0; i < Palette.Length; i++)
        {
            var r = Expand(Reader.ReadByte());
            var g = Expand(Reader.ReadByte());
            var b = Expand(Reader.ReadByte());

            Palette[i] = Color.FromArgb(r, g, b);
        }

        Reader.ReadByte();

        return;

        static byte Expand(byte b)
        {
            return (byte)((b * 255 + 31) / 63);
        }
    }

    public byte[] Frame { get; } = new byte[320 * 200];

    private int FrameCount { get; }

    private int FrameIndex { get; set; }

    public Color[] Palette { get; } = new Color[256];

    private BinaryReader Reader { get; }

    public bool Read()
    {
        if (FrameIndex >= FrameCount)
        {
            return false;
        }

        FrameIndex++;

        var index = 0;

        while (true)
        {
            var flags = Reader.ReadByte();

            if ((flags & 0xC0) == 0xC0)
            {
                return true;
            }

            var count = 4 * (flags & 0x3F);

            var chunk = Frame.AsSpan(index, count);

            switch (flags & 0xC0)
            {
                case 0x40:
                {
                    chunk.Fill(Reader.ReadByte());
                    break;
                }
                case 0x80:
                {
                    Reader.ReadExactly(chunk);
                    break;
                }
            }

            index += count;
        }
    }
}

Credits

aybe on the 17th of May 2026