MUGEN-SFF文件格式

SFF是Elecbyte开发的图像集文件格式,用于储存人物帧信息,目前有三个版本:SFFV1.01、SFFV2.00、SFFV2.01,分别对应三个版本的主程序:Win、1.0、1.1。由于MUGEN的向下兼容性,1.1支持所有版本的SFF,1.0支持SFFV2.00和SFFV1.01,而Win仅支持SFFV1.01。
本篇教程将以字节的精确度来解释SFF文件,即面向程序员而编写,其中大部分内容都是本人经过长时间研究所得,虽然2017年8月已经公开了大部分内容,但时隔一年,有些东西还是需要重新详细说一下。

对于SFF文件来说,其文件头部总是由位固定的字节来描述的:

Length:12(0-11)
TEXT:ElecbyteSpr\0
DEC:69 108 101 99 98 121 116 101 83 112 114 0
HEX:45 6C 65 63 62 79 74 65 53 70 72 00

紧接着后面的是该SFF文件的版本号:

Length:4(12-15)
    DEC(HEX):
        SFFV1.00:00 01 00 01
        SFFV2.00:00 00 00 02
        SFFV2.01:00 01 00 02

SFFV1

对于SFFV1来说,在版本号之后的数据分别是:

图像组数量:int4(16-19)
图像数量:int4(20-23)
第一个图像子节点头部的位置:int4(24-27),一般为512
子节点头部大小:int4(28-31),一般为32
第一个图像的色表类型:byte(32),一般为0

术语解释:
1.图像子节点:在SFFV1中,数据总是这样排列的:

文件头(Length:32)
第一个图像子节点头部
第一个图像数据
第二个图像子节点头部
第二个图像数据
……
最后一个图像子节点
最后一个图像数

每一个图像子节点头部储存着该图像的基本信息,其结构通常如下:

下一个图像子节点头部的位置:int4(0-3)
图像数据长度:int4(4-7)
图像横坐标:int2(8-9)
图像纵坐标:int2(10-11)
图像组号:int2(12-13)
图像索引号:int2(14-15)
链接索引:int2(16-17)
色表类型:byte(18)
无用数据:bytes(19-31)

2.色表类型:在SFFV1.01中,对于每一个图像其色表定义都有两种类型,当色表类型为1时,表示该图像使用共享色表,当色表类型为0时,表示该图像使用独立色表
使用独立色表即该图像直接使用图像数据中自带的色表
使用共享色表即该图像使用前一个图像的色表,如果前一个图像也使用共享色表,则继续往前搜索,直到遇到使用独立色表的图像为止

3.图像数据:在SFFV1.01中,每一个图像的数据总是一个标准的PCX文件,图像数据的位置在图像子节点头部之后,其长度标注于图像子节点头部中。
对于使用独立色表的图像来说,其色表总是位于PCX文件数据的最后768个字节中,在这768个字节之前总有一个固定的字节(色表起始标志),十进制数为12,十六进制数为C。该768个字节中每三个字节分别表示一个颜色值的R、G、B分量。
对于使用共享色表的图像来说,其PCX文件数据末中可能存在完整的768个字节,也可能存在小于768个字节,又或者以色表起始标志结束,又或者不存在色表起始标志与色表数据,所以应该根据情况添加对应的独立色表。
参考资料:https://baike.baidu.com/item/pcx/948076

4.链接索引:在SFFV1.01中,可能存在两个或者两个以上图像数据相同的图像子节点,为了节省空间,默认位于最前面的子节点为真子节点,而位于真子节点后的图像数据相同的子节点为链接型子节点
对于真子节点来说,其图像数据长度总不为0,链接索引一般为0
对于链接型子节点来说,其图像数据长度总为0,链接索引表示真子节点序号(从0开始)
判断一个子节点是否为链接型时,应该以图像数据长度为标准,而不是链接索引。

潜规则:
1.第一张图像强制为独立色表
2.对于图像9000,0和0,0,强制为独立色表,且强制使用第一个独立色表

SFFV2

对于SFFV2(SFFV2.00、SFFV2.01)来说,在版本号之后的数据分别是:

无用数据:bytes(16-35)
图像子节点头部序列位置:int4(36-39)
图像数量:int4(40-43)
色表子节点头部序列位置:int4(44-47)
色表数量:int4(48-51)
ldata起始位置:int4(52-55)
ldata长度:int4(56-59)
tdata起始位置:int4(60-63)
tdata长度:int(64-67)

术语解释:
1.图像子节点、色表子节点:与SFFV1不同的是,SFFV2的图像数据并不总是一个标准的图像文件,所以在图像数据中是没有色表数据的,即色表被以子节点的形式另外储存。对于每一个图像子节点头部,其结构通常如下:

Length:28
图像组号:int2(0-1)
图像索引号:int2(2-3)
图像宽度:int2(4-5)
图像高度:int2(6-7)
图像横坐标:int2(8-9)
图像纵坐标:int2(10-11)
链接索引:int2(12-13)
压缩算法:byte(14)
位深度:byte(15)
图像数据块偏移:int4(16-19)
图像数据长度:int4(20-23)
色表索引:int2(24-25)
数据块标志:int2(26-27)

对于每一个色表子节点头部,其结构通常如下:

Length:16
色表组号:int2(0-1)    
色表索引号:int2(2-3)
颜色数量:int2(4-5)
链接索引:int2(6-7)
色表数据块偏移:int4(8-11)
色表数据长度:int4(12-15)

2.图像节点头部序列位置、色表子节点头部序列位置:与SFFV1不同的是,SFFV2的图像子节点头部和图像数据的储存位置并不连续,而各个图像子节点的储存位置是连续的,即从图像子节点头部序列位置开始,每28个字节为一个图像子节点头部,数量取决于文件头定义的图像数量
对于色表子节点来说同理,从色表子节点头部序列位置开始,每16个字节为一个色表子节点头部,数量取决于文件头定义的色表数量

3.ldata、tdata:SFFV2的图像子节点头部和图像数据的储存位置是不连续的,即图像数据以数据块的形式被另外储存,SFFV2有两种数据块,分别是ldata和tdata
对于ldata来说,总是储存着图像的压缩数据,即加载时不解压,直到需要使用时才解压的图像数据;除此之外,所有的色表数据都统一储存在ldata内
对于tdata来说,总是储存着图像的原始数据,即加载时必须直接解压的图像数据

4.压缩算法:SFFV2提供了一系列图像压缩算法,以一个字节的形式标注
为0时表示无压缩,为原始数据
为2时表示使用Rle8
为3时表示使用Rle5
为4时表示使用Lz5
为10时表示使用PNG8
为11时表示使用PNG24
为12时表示使用PNG32
各压缩算法后面的数字代表适用的图像位深度,特别地,仅SFFV2.01支持PNG8、PNG24、PNG32算法
压缩算法决定了图像数据的形式,对于被压缩图像(压缩算法不为0)来说,其图像数据的前四个字节表示解压后的数据长度,用于校验,即真正的图像数据从第五个字节开始;对于无压缩图像(压缩算法为0)来说,其图像数据没有校验长度
对于使用Rle8、Rle5、Lz5压缩的图像来说,其图像数据解压后为像素点阵数据(即位图数据),而不是一个标准的图像文件
对于使用PNG8、PNG24、PNG32压缩的图像来说,其图像数据解压后总是一个标准的PNG文件,尽管如此,PNG8文件里的色表数据一定是错误的,需要使用对应的色表进行校正才能正常显示

5.位深度、颜色数量:SFFV2支持的图像位深度分别有5位、8位、24位、32位,其中5位图像的色表颜色数量为32,8位图像的色表颜色数量为256,24位和32位没有色表

6.图像数据块偏移、图像数据长度、数据块标志:当数据块标志为0时,表示图像数据位于ldata数据块,为1时,表示图像数据位于tdata数据块;图像数据块偏移表示图像数据在对应数据块中的相对起始位置,相对于整个文件的绝对位置为【ldata/tdata位置+图像数据块偏移】
当图像数据长度为0时,表示图像为链接型图像,链接型图像的处理方式与SFFV1相同

7.色表索引、色组组号、色表索引号:色表索引指定图像所使用的色表所在子节点的序号(从0开始),色表组号和索引号一般情况下用不到,具体的作用这里暂时不说

8.色表数据块偏移、色表数据长度:色表数据统一储存在ldata内,色表数据长度取决于颜色数量,一般情况下,8位色表的数据长度为1024,5位色表的数据长度为128;当色表数据长度为0时,表示该色表为链接型,处理方式与链接型图像大同小异
对于色表数据,每四个字节代表一个颜色,前三个字节分别表示该颜色的R、G、B,第四个字节的具体作用这里暂时不说,可当无用数据处理

SFFV2压缩算法官方文档

Rle8:

Simple run-length encoding for 8-bit-per-pixel bitmaps.
Any byte with bits 6 and 7 set to 1 and 0 respectively is an RLE control packet. All other bytes represent the value of the pixel.
RLE packet (1 byte)
       bits 0-5: run length
       bits 6-7: run marker (01)
Pseudocode to decode an RLE8 chunk:
       one_byte = read(1 byte)
       if (one_byte & 0xC0) == 0x40, then
         color = read(1 byte)
         for run_count from 0 to (val & 0x3F) - 1, do
           output(color)
       else
         output(one_byte)

Rle5:

RLE5 is a run-length encoding used for the compression of 5-bit-per-pixel bitmaps. RLE5 is a hybrid of two run-length encoding algorithms. The first allows encoding of long runs of color 0 pixels, the second is a 3-bit-RL/5-bit-color run-length algorithm.
RLE5 packet (2 bytes)
       byte 0         : run length
       byte 1 bit 7   : color bit; .2-.7
       byte 1 bits 0-6: data length
Pseudocode to decode an RLE5 chunk:
       RLE5packet = read(2 bytes)
       if RLE5packet.color_bit is 1, then
         color = read(1 byte)
       else
         color = 0
       for run_count from 0 to RLE5packet.run_length, do
         output(color)
       // Decode 3RL/5VAL
       for bytes_processed from 0 to RLE5packet.data_length - 1, do
         one_byte = read(1 byte)
         color = one_byte & 0x1F
         run_length = one_byte >> 5
         for run_count from 0 to run_length, do
           output(color)

Lz5:

What is an LZ5 compressed image?
It is a stream of LZ5 packets.

What is an LZ5 packet?
There is a control packet, aka "control byte". There are also four kinds of data packets.
    Short RLE
    Long RLE
    Short LZ
    Long LZ

What is the ordering of packets?
Control packet followed by 8 data packets. This is repeated until the end of the stream. The last control packet may be followed by fewer than 8 data packets if the stream ends there.

How is it decoded?
Decompressed data is written into a caller-allocated buffer after each data packet is processed. Note that processing of later packets may depend on the decompressed data output from earlier packets.

What is the control packet?
The control packet is a collection of 8 bitflags. The flag in bit #n determines whether the n'th data packet after this control packet is an RLE packet or an LZ packet. The control packet is always followed by as many data packets are available, up to a maximum of 8. If there are fewer than 8 data packets available (because of end of stream) then the value of the unused bitflags is unspecified.

What is a short RLE packet?
It outputs 1--7 pixels of a single color.
Byte 1
    bits 0-4: pixel color
    bits 5-7: number of times to output

What is a long RLE packet?
It outputs 8--263 pixels of a single color.
Byte 1
    bits 0-4: pixel color
    bits 5-7: 0
Byte 2
    bits 0-7: number of times to output this pixel, minus 8

What is a naive memcpy?
It is a memcpy that does the naivest possible thing in the case of overlapping src and dst buffers. It is used by LZ packets to copy a previous run of decompressed data. A reference implementation is given below.
void naivememcpy(byte *dst, const byte *src, int num)
{
  byte mybyte;
  while (num--) {
    mybyte=*src++;
    *dst++=mybyte;
  }
}
Note: this allows us to achieve a kind of run length encoding for pixel sequences, not just individual pixels (as long as the sequence to be duplicated immediately precedes the dst pointer).

What is an LZ copy?
Basically, it is:
naivememcpy(dst, dst - ofs, len)
where ofs is positive.

What is a short LZ packet?
It does an LZ copy of length 1--64, from offset 1--256. It is usually two bytes long, but the 4th, 8th, ..., 4k'th short LZ packets in the stream are only one byte long. See the discussion of recycled bits.
Byte 1
    bits 0-5: LZ copy length, minus 1
    bits 6-7: recycled bits
Byte 2 [if necessary]
    bits 0-7: LZ offset, minus 1

What is a long LZ packet?
It does an LZ copy of length 3--258, from offset 1--1024.
Byte 1
    bits 0-5: 0
    bits 6-7: top 2 bits of offset-1
Byte 2
    bits 0-7: bottom 8 bits of offset-1
Byte 3
    bits 0-7: LZ copy length, minus 3

What are recycled bits?
Each short LZ packet contributes 2 recycled bits. Recycled bits are accumulated until there are 8 of them, which occurs at every fourth short LZ packet. These 8 bits are then used as Byte 2 for that packet. The ordering of the bits is:
    bits 6-7: recycled bits of short LZ packet 4k + 1
    bits 4-5: recycled bits of short LZ packet 4k + 2
    bits 2-3: recycled bits of short LZ packet 4k + 3
    bits 0-1: recycled bits of short LZ packet 4k + 4
The value of the recycled bits is undefined if there are not enough short LZ packets to use them on. That is, if there are n short LZ packets in the stream, then the value of the recycled bits for the last (n % 4) short LZ packets is undefined.