通用格式设计
1 序
二进制文件的格式一般采用结构 文件头 + 核心内容。
- 文件头:标识文件的基本信息,其设计方法基本一致。
 - 核心内容:这部分是真正的文件数据内容。
 
二进制文件的格式一般采用结构 文件头 + 核心内容。
描述了文件的整体信息,常见的字段有魔数、检验码、版本号、文件大小等。
顾名思义,即是将文件的整体信息通常放在文件的开头。
在少数情况下,也会将文件头放在文件的尾部,也就是‘文件尾’,但是一般还是叫做 文件头。
魔数作为文件格式的标识,一般用于识别文件是否程序所能处理的文件。其值可以随意选取,主要看设计者自身的喜好。
例如 zip 格式的魔数就是 “PK\x03\x04”,其中 PK 就是设计者 Philip Katz 的名字首字母。
一般程序在分析文件时,会先判断魔数的值是否匹配,不匹配就表明文件格式不正确。
需要注意的是,假如魔数正确,文件格式并非一定能够读取正确,还需要进一步判断。
文件头通常还会有个检验码,用于检验文件是否完整并且没有经过修改的。这个检验码可以使用 crc, 可以使用 md5,也可以使用其它算法。
这也是检查文件格式的正确性中的重要一步,因为即便是魔数相同,校验码所采用的算法不同,和校验码所作的位置不同,所计算的校验码,基本上是不一的,所以进一步就,能区分出文件格式是否正确。
文件头通常还会包含版本号。
版本号不同,就表明文件格式发生变化,可能变化很小,也可能变化很大;可能是某些字段的值在解释上发生变化,也可能是直接添加了一些结构。所以导致文件的读取方式可能会有所不同。
版本号有时只是单独一个数字,不断往上递增。有时也会拆分成两个数字,为主版本号和次版本号。主版本号修改,通常表示文件格式发生大变动。而次版本号修改,通常只是表示添加了一些小功能。
字节顺序在文件格式设计中至关重要。
字节顺序分为大端字节序和小端字节序。不同的机器字节序有可能不同,设计文件格式时需要考虑文件用什么字节序保存数据的。
读取和存储的字节序不一致就会导致程序出错。
通用结构定义如下:
struct FileHeader{
    uint8 mMagic[4];    //魔数
    uint8 mHash[16];    //检验码
    uint32 mEndian;     //字节序
    uint32 mVersion;    //文件版本
    ...
};分段(或叫分节,后续默认叫法为分段)的二进制文件格式是比较常见的,比如编译器 GCC 输出的目标文件一般为 ELF 的分段文件。
这类文件一般采用 文件头 + 分段 的结构:
file header 
section 0
section 1
....
section N
文件头设计方法在 文件头 中介绍,本章节主要描述分段式存储数据的方法。
一般有两种结构:
分散式分结构在分区的头部的描述分段的大小等信息,然后各个分段首尾相连的,分段写入。
分段的每一个段的结构通常是:
tag + length
section data
tag 和 length 合起来是分区头部,描述分段中的数据标识,后面紧跟着体数据。
tag 一般是一个整数,用来标识分区的类型。不同的分区用不同的 tag 值表示,不同的分区种类可以使用不同的读取方式。
整体结构如下:
file header 
section 0
            tag     //section 0 的标识
            length  //section 0 的数据长度
            data[]  //section 0 的数据
section 1
....
section N
段表式的结构是有一个描述分段信息的段表,后面存储各个分区的数据。
主要优点就是,可以通过段描述表(简称段表)快速的访问分段的内容,其格式如下。
section table
section 0
section 1
....
section N
段表一般描述分段的类型、数据长度、相对偏移等,结构如下。
kind
length
offset
C 语言定义如下:
struct SegTabItem{
    uint32 kind;    //类型
    uint32 length;  //数据长度
    uint32 offset;  //相对偏移
};整体结构如下:
file header 
section table
                kind    //section 0 的类型
                length  //section 0 的数据长度
                offset  //section 0 的相对偏移
                kind    //section 1 的类型
                length  //section 1 的数据长度
                offset  //section 1 的相对偏移
                ...
                kind    //section N 的类型
                length  //section N 的数据长度
                offset  //section N 的相对偏移
section 0
section 1
....
section NTLV 是一种可变的格式,由三个域构成:标识域(Tag)+长度域(Length)+值域(Value),简称TLV格式。
其中:
T 和 L 的长度固定,一般是2或4个字节,V 的长度由 Length 指定。
data 0
    T = 1
    L = 1
    V = 1
data 1
    T = 1
    L = 1
    V = 2
...
data N
    T = 1
    L = 1
    V = n
当然V中的数据也是可以嵌套,至于嵌套几层看设计者的规定的。
结构如下:
data 0
    T = 1
    L = 3
    V =[
            data x
            T = 1
            L = 1
            V = 1
        ]
data 1
    T = 1
    L = 1
    V = 2
...
data N
    T = 1
    L = 1
    V = n