我是谁004 发表于 2023-7-29 18:33:22

SLD文件的结构

基本信息
SLD格式是《帝国时代:决定版》和《帝国时代2:决定版》的图像格式。
它采用小端编码,即高字节放在后面。它的内容分为头部和各帧数据,文件的开头是16字节的头部,由五个部分组成:
struct file_header {

      byte format; // 文件格式,为"SLDX"的ASCII值

      unsigned word version; // 版本号,通常等于4

      unsigned word frame_count; // 帧总数

      unsigned dword unknown; // 未知数据,值为0x00100000

      unsigned dword opacity; // 不透明度,范围0 - 255。半透明的图像不会显示下方其他SLD,而只会将它们“挖空”

}之后是每一帧的数据,由如下部分组成:
frame {

      struct frame_header {

                unsigned dword width; // 帧总宽度

                unsigned dword height; // 帧总高度

                dword anchorX; // 锚点X坐标

                dword anchorY; // 锚点Y坐标

                unsigned word frame_type; // 帧类型

                unsigned word index; // 帧序号,从0开始,每帧加1

      }

      normal_chunk { // 普通色数据块

                unsigned dword chunk_size; // 数据块的大小,包括这4字节。实际上会填充至4的倍数长度,例如总长度253,会在尾部加3字节成为256

                layer_header header;

                image_data data;

      }

      shadow_chunk { // 阴影数据块

                unsigned dword chunk_size; // 数据块的大小

                layer_header header;

                image_data data;

      }

      mask_chunk { // 遮罩数据块

                unsigned dword chunk_size; // 数据块的大小

                unsigned word header; // 头部,等于0x0005

                mask_data data;

      }

      smudge_chunk { // 损伤数据块

                unsigned dword chunk_size; // 数据块的大小

                layer_header header;

                image_data data;

      }

      player_chunk { // 玩家色数据块

                unsigned dword chunk_size; // 数据块的大小

                layer_header header;

                image_data data;

      }

}帧的头部frame_type这个属性指定了它使用哪些图层,由二进制位来确定:
1-普通色,2-阴影,4-遮罩,8-损伤,16-玩家色。
当任意一个图层未使用,则在读写时直接跳过,不会分配存储空间。例如,普通单位的图像没有损伤图层,那么在读完遮罩图层后,就会直接读取玩家色图层。
除此之外,还有一个最高位32768。当此位为1时,表明这一帧是地面图像而非立体(会被立体图像遮挡),例如游戏中的小路、花等装饰物。
和以前的版本不同,决定版中,图像所在的图层不是在DAT中决定的,而是取决于图像本身,也只有两个图层可用。同时,这种帧中阴影图层是必需的,否则仍然会被视作立体图像。

各种图层的含义
每个图层会根据其边界坐标,覆盖在帧的对应位置上。除了普通色和阴影是直接绘制的外,其他帧有各自的用途。
普通色的像素分为完全不透明和完全透明两种,勾勒出单位图像的主要内容。
阴影图像在游戏中展现的效果为不同透明度的纯黑色,常用于阴影和柔化边缘。在游戏中,阴影无法位于普通色之上。
遮罩的图层边界和普通色的相同,但每个像素只有透明与不透明两种状态;此图层不会被绘制出来,而用于制造可点击选择的区域,一般与普通色的透明相同。
损伤色采用RGB通道,每个通道的取值越大,则变暗的程度越大:
·当单位生命值在66%~100%之间时,红色通道的图像会逐渐变黑显示出来;
·当单位生命值在33%~66%之间时,完全显示红色通道的图像,逐渐显示绿色通道的图像;
·当单位生命值在0%~33%之间时,完全显示红、绿色通道的图像,逐渐显示蓝色通道的图像。
玩家色色采用单色通道,用HSV方式进行混合。每个通道的取值越大,则玩家色的浓度越高。图层会将其下普通色的色相和饱和度改变为对应玩家色的值,根据通道取值进行混合。


图层头部数据
图层头部数据分为完整和简化两种,普通色和阴影采用完整,损伤和玩家色采用简化,
二者的不同在于,完整头部会将图层的边界记录下来,而简化的不记录;损伤和玩家色采用了和普通色相同的图层边界,因此不需要额外存储它们。
完整的图层头部结构,占据10字节:
struct layer_header {

      word left; // 图层左边界

      word top; // 图层上边界

      word right; // 图层右边界(比最右像素的坐标大1)

      word bottom; // 图层下边界(比最右像素的坐标大1)

      byte flags; // 标志:128-在上一帧对应图层中叠加内容,可以减少文件体积

      byte type; // 图层类型:0-RGB,用BC1压缩,1-单色,用BC4压缩

}
简化的图层头部结构,占据2字节:
struct simple_layer_header {

      byte flags; // 标志

      byte type; // 图层类型

}例如,一个图层在画布中占据的范围为(40, 32)-(119, 91),那么left、top、right、bottom的值分别为40、32、120、92。基于后续的编码缘故,图层的长和宽都必须是4的倍数。
压缩方式需要和图层的种类一致,不可改用另一种。对于普通色和损伤,采用BC1压缩;对于阴影和玩家色,采用BC4压缩。


两种压缩图像方式
image_data {

      word draw_count; // 图元分组数

      unsigned byte[] draws; // 每组图元的数量,数量等于draw_count的二倍

      qword[] tiles; // 图元正文,数量等于draws偶数元素之和

}SLD文件采用BC1压缩和BC4压缩(又叫DXT1和DXT4压缩)两种图像编码方式。这两种均将图像按照4×4划分一个个图元,按照从左往右、从上往下的顺序进行编码。因此,编码的图像的长宽都是4的倍数。
无论哪种压缩方式,数据体都由上文描述的部分组成。
每组图元可以分为两部分,第一部分是全部空白(透明)的,这部分不会存储在tiles中。第二部分是非空白的,其内容存储在tiles中。
例如draws前四个元素分别为15、8、20、12,那么重现图像时从左上角开始,先跳过15个空格,也就是说左上角60×4都是透明色;然后从tiles中取第0到7个图元进行绘制,占据32×4空间。
之后再跳过20个空格,绘制12个图元,对应tiles中是第8到第19个。如此不断,直到draws中的长度读完为止。
当当前水平位置超过图像宽度时,就会拐到下一行;譬如宽度为100像素,即25个图元,那么第二组图元的空白部分在图像第一行只有2个,后18个会从第二行开头开始。
如果连续的空白或非空超过255,编码时会将另一个数值置为0。举个例子,连续300个非空图元,编码图元数量就会是[*,255,0,45]这样子的。
当图层设置了“保留上一帧”属性时,会在上一帧的基础上进行绘制。空白段会跳过而保留原来的像素,但非空部分会擦除然后替代,这样在内容和前一帧大同小异时,可以节省存储空间。
BC1/DXT1压缩:
BC1压缩使用两个2字节的RGB565颜色(即二进制16位从高到低分别是5位红、6位绿、5位蓝)C1和C2,以及它们的插值结果;
后4个字节对16个像素每个用2位来表示颜色索引,因此0~3可以有4种颜色;
C1的值大于C2时,四种颜色分别为C1、C2、mix(C1,C2, 1/3)、mix(C1,C2, 2/3) (mix()表示将两种颜色按比例混合,混合值在0到1之间,值越小越接近C1);
C1的值不大于C2时,四种颜色分别为C1、C2、mix(C1,C2, 1/2)、透明。
BC4/DXT4压缩:
BC4压缩使用两个1字节的256级灰度V1和V2,以及它们的插值结果;
后6个字节对16个像素每个用3位来表示颜色索引,因此0~7可以有8种颜色;
V1的值大于V2时,八种颜色分别为V1、V2,以及二者从1/7到6/7的混合;
C1的值不大于C2时,八种颜色分别为V1、V2,二者从1/5到4/5的混合,以及0和255。

遮罩的编码方式
mask_data {

      unsigned word offsets; // 指令偏移量

      byte[] data; // 指令数据

}
遮罩的编码结构内容较少,但编码方式更复杂一些。上述为其数据结构。而且,遮罩也采用了和普通色一样的图层边界。
指令偏移量的数量等于图层行数,也就是图层高度÷4。而偏移量为相对于指令数据部分的地址,因此第一个值总是0,每个值表示data中从此开始为绘制的指令。
而指令数据规则如下:
①读取一个字节B;
②若B大于128,则将后续的(B - 128)个图元按顺序铺在帧上,每个图元2字节,从低到高位依次表示了16个像素的状态;
③若B小于(小于等于?)128,则继续读取,直到下一个大于128为止。
这些数之和为重复的次数,重复放置最近一个图元(没有则为空白,通常是全满或全空);(附注:B为1时会被视为0)
④重复①到③,直到一行对应的这段数据读完为止。

cta 发表于 2023-12-6 04:59:24

{:151:}
页: [1]
查看完整版本: SLD文件的结构