翔鹰帝国网|帝国时代论坛|帝国时代系列|神话时代
 找回密码
 注册翔鹰会员(昵称)
搜索
查看: 1078|回复: 2

[文献] SLD文件的结构

[复制链接]

417

主题

19

精华

8万

积分

教皇

耕战
13421
鹰币
41665
天龙币
18
回帖
6023

特级嘉禾勋章三级帝国勋章十字军勋章翔鹰建站十周年纪念章

附庸关系0
 楼主| 发表于 2023-7-29 18:33:22 | 显示全部楼层 |阅读模式
基本信息
SLD格式是《帝国时代:决定版》和《帝国时代2:决定版》的图像格式。
它采用小端编码,即高字节放在后面。它的内容分为头部和各帧数据,文件的开头是16字节的头部,由五个部分组成:
  1. struct file_header {

  2.         byte[4] format; // 文件格式,为"SLDX"的ASCII值

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

  4.         unsigned word frame_count; // 帧总数

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

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

  7. }
复制代码
之后是每一帧的数据,由如下部分组成:
  1. frame {

  2.         struct frame_header {

  3.                 unsigned dword width; // 帧总宽度

  4.                 unsigned dword height; // 帧总高度

  5.                 dword anchorX; // 锚点X坐标

  6.                 dword anchorY; // 锚点Y坐标

  7.                 unsigned word frame_type; // 帧类型

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

  9.         }

  10.         normal_chunk { // 普通色数据块

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

  12.                 layer_header header;

  13.                 image_data data;

  14.         }

  15.         shadow_chunk { // 阴影数据块

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

  17.                 layer_header header;

  18.                 image_data data;

  19.         }

  20.         mask_chunk { // 遮罩数据块

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

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

  23.                 mask_data data;

  24.         }

  25.         smudge_chunk { // 损伤数据块

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

  27.                 layer_header header;

  28.                 image_data data;

  29.         }

  30.         player_chunk { // 玩家色数据块

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

  32.                 layer_header header;

  33.                 image_data data;

  34.         }

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

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


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

  2.         word left; // 图层左边界

  3.         word top; // 图层上边界

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

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

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

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

  8. }
复制代码

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

  2.         byte flags; // 标志

  3.         byte type; // 图层类型

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


两种压缩图像方式
  1. image_data {

  2.         word draw_count; // 图元分组数

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

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

  5. }
复制代码
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。

遮罩的编码方式
  1. mask_data {

  2.         unsigned word[rows] offsets; // 指令偏移量

  3.         byte[] data; // 指令数据

  4. }
复制代码

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

004时代:战役时代
我很乐意看到有人在MOD技术上超过我。
回复

使用道具 举报

0

主题

0

精华

25

积分

骑士

耕战
0
鹰币
15
天龙币
0
回帖
13
附庸关系0
发表于 2023-12-6 04:59:24 | 显示全部楼层
回复

使用道具 举报

0

主题

0

精华

3

积分

扈从

耕战
0
鹰币
10
天龙币
0
回帖
4
附庸关系0
发表于 2025-4-22 23:09:27 | 显示全部楼层

Hello WAIFOR. I have studied your SLD structure information and SLD Parser and beleive you may have made a mistake regarding block duplication from the previous frame. Please try with War Wagon Death Animation



As you can see, blocks from previous frame are being duplicated where they shouldn't be.

Many thanks for any help on correcting this issue in your code, your tool is extremely helpful to me :-)
回复

使用道具 举报

本版积分规则

排行榜|小黑屋|翔鹰帝国

GMT+8, 2025-4-30 18:43 , Processed in 0.128451 second(s), 44 queries , File On.

Powered by Hawk Studio  QS Security Corp.® Licensed

Copyright © 2001-2023, Hawkaoe.net All Rights Reserved

快速回复 返回顶部 返回列表