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

[文献] SLD文件的结构

[复制链接]

417

主题

19

精华

8万

积分

教皇

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

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

附庸关系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

精华

18

积分

骑士

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

使用道具 举报

本版积分规则

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

GMT+8, 2024-4-25 19:53 , Processed in 0.200230 second(s), 33 queries , File On.

Powered by Hawk Studio  QS Security Corp.® Licensed

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

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