前一阵子忽然想给我写的一个编辑器增加阅读UMD文件的功能,就上网查了查相关资料。
结合网上的资料我写了一个UMD解析的类(for VC,其中调用了zlib动态库),完整的类文件上传在我的资源中。
以下就把我感觉不错的一个资料给大家共享下:
UMD是一种常用的电子书格式,本文只对文本类型的格式进行解析,动漫格式不做涉及。本文是搜集网络上关于UMD文件的资料并进行整理而成的,其中有些信息还不完整,可能还有的信息没有提到。这不是一本大全,仅仅是对一些常见块的分析。
作者:Dandelion <cnbeta2005@gmail.com>
日期:2010/8/22
免责声明
本文档仅做学习交流之用,您可以在不修改文档的前提下任意分发该文档,对于使用文中内容发生的侵权行为及对您造成的损失,本人将不承担任何责任!
概要介绍
UMD文件有三种格式类型,一种叫纯文本格式,一种叫漫画&写真集格式,以及连环画(文字+图画)。本文只涉及纯文本格式的UMD,后面将直接使用UMD指代这类格式的文件。
UMD文件总体上是由一组连续的块组成的,每一块按照约定的顺序先后排列在一起构成了UMD文件的结构。根据块的职责,我将其分成两类:功能块和数据块。有的功能块自身就可以完全的描述信息,而有的一些由于信息量大,特别地将数据放在别处(即数据块),如正文、章节偏移和章节标题,它们使用一个功能块和若干个数据块,通常数据块都紧接在相应的功能块之后出现。
块标识
下表列出了已知的块标识:
块标识 | 描述 | 参数定义 |
0x01 | umd文件头 | DCTS_CMD_ID_VERSION |
0x02 | 文件标题 | DCTS_CMD_ID_TITLE |
0x03 | 作者 | DCTS_CMD_ID_AUTHOR |
0x04 | 年 | DCTS_CMD_ID_YEAR |
0x05 | 月 | DCTS_CMD_ID_MONTH |
0x06 | 日 | DCTS_CMD_ID_DAY |
0x07 | 小说类型 | DCTS_CMD_ID_GENDER |
0x08 | 出版商 | DCTS_CMD_ID_PUBLISHER |
0x09 | 零售商 | DCTS_CMD_ID_VENDOR |
0x0B | 内容长度 | DCTS_CMD_ID_FILE_LENGTH |
0x0C | 文件结束 | DCTS_CMD_ID_FIXED_LEN |
0x81 | 正文 | DCTS_CMD_ID_REF_CONTENT |
0x82 | 封面(jpg) | DCTS_CMD_ID_COVER_PAGE |
0x83 | 章节偏移 | DCTS_CMD_ID_CHAP_OFF |
0x84 | 章节标题,正文 | DCTS_CMD_ID_CHAP_STR |
0x87 | 页面偏移(Page Offset) | DCTS_CMD_ID_PAGE_OFFSET |
0x0A | CONTENT ID | DCTS_CMD_ID_CONTENT_ID |
0xF0 | CDS KEY | DCTS_CMD_ID_CDS_KEY |
0xF1 | 许可证(LICENCE KEY) | DCTS_CMD_ID_LICENSE_KEY |
表-1 所有已知的块标识。黄色背景的项目说明它们的数据存储在后接数据块中。
之所以说是“已知的块标识”,主要是因为本人没有见过真正UMD的规范(似乎格式并未公布),这也是写本文的一个原因。上表中的信息及本文中绝大多数的信息都来源于网络上许多网友的劳动成果。
块结构
下面来看看UMD文件中的2类块结构
格式 | 大小(字节) | 描述 |
# | 1 | 指示这是一个功能块(0x23) |
Category | 2 | 块标识(见表1) |
Unknown | 1 | 未知,可能的值有0、1 |
Length | 1 | 整个功能块的长度(字节数) |
Content | n | 功能块的内容。一般是字符型的数据,并且以UNICODE编码存储,不带字符串结尾符0。Content所占用的字节数加上前面各字段的大小(前面共有5字节)即为Length。如果此功能块后接一个数据块的话,Content存放的不是实际的内容,而是一个4字节的数,它引用着一个数据块,该数据块头部也会包含同样的4个字节来标识自己。 |
表-2 功能块结构
格式 | 大小(字节) | 描述 |
$ | 1 | 指示这是一个数据块(0x24) |
RandVal | 4 | 一个数据块标识,与对应功能块中的Content相同。注意正文数据块有所不同,后面将提到。 |
Length | 4 | 整个数据块的长度(字节数) |
Content | N | 实际的数据,一般都很大。如正文的内容实际上划分为了很多块,分别放入了不同的数据块中。同样当Content存储的是字符串时,编码依然是UNICODE。Content占用字节数加上前面字段的大小(前面共有9字节)即为Length。 |
表-3 数据块结构
如上所示,功能块与数据块都有着各自的格式,UMD中的块都遵循这两种格式之一。尽管大体上简单明了,由于Content中实际存储的数据不同,其Content中的结构也有所不同。
Content格式
下面将我将列出常用的Content格式。
功能块Content格式
先说功能块中的Content,主要分3类:
其一、块标识为0x01(文件头)的功能块,其Length为8,Content长度为3(由8-5计算得出)。3个字节中第一个字节标识出了该UMD文件的类型(1-文本,2-漫画),另2个字节我不知有何意义,网络上有注释为:short random1.Next(0×401, 0x7fff) % 0xffff //PGKSeed,不过我还没发现有什么用处,就当随机数扔掉好了。
其二、包含基本属性的一些功能块,它们在Content中存储的是字符串(不含结尾符)。字符编码为Unicode16 LittleEndian。
其三、带有数据块的功能块。它们后面都紧跟着一个数据块,Content最后为4字节随机数,随后的数据块也有着同样的4字节随机数保存在RandVal中。Content的长度可能大于4字节,因为可以在Content内加入其他的一些数据。
块标识 | 描述 | Content结构 | 字段大小 | 字段描述 | 共计 |
0x01 | UMD文件版本 | Flag | 1 | UMD文件类型(1-文本,2-漫画) | 3 |
Random | 2 | 随机数 | |||
0x02 ~ 0x09 | UMD基本属性 | Content | N | UNICODE文本 | N |
0x0a | CID | Content | N | 数据 | N |
0x0b | 内容长度 | Length | 4 | 内容长度 | 4 |
0x0c | UMD文件结束 | Length | 4 | 整个文件长度 | 4 |
0x81 | 正文结束 | BlockRandom | 4 | 指向正文索引数据块的RandVal | 4 |
0x82 | 封面 | Flag | 1 | 指示图片文件类型。1-JPG | 5 |
BlockRandom | 4 | 指向图像数据块的RandVal | |||
0x83 | 章节偏移 | BlockRandom | 4 | 指向章节偏移数据块的RandVal | 4 |
0x84 | 章节标题 | BlockRandom | 4 | 指向章节标题数据块的RandVal | 4 |
0x87 | 页面偏移 | fontSize | 1 | 字体大小 | 6 |
screenWidth | 1 | 屏幕宽度 | |||
BlockRandom | 4 | 指向一个页面偏移数据块 | |||
0xF0 | CDS Key | Content | / | 未知 | / |
0xF1 | Licence Key | LicenceKey | 16 | 16字节空数据 | 16 |
表-4 各功能块的Content格式
数据块Content格式
现在说说数据块中的Content格式吧,根据数据块对应的功能块的不同,结构也不相同。现在让我们就按表1中的顺序逐个说明吧。
首先是正文数据块,其Content中存储的是纯粹的字符串压缩数据(UNICODE编码的正文压缩数据)。要读取其中的数据,你需要先按正文数据块中的Length-9来从文件中读出压缩数据,然后使用zlib解压缩得到正文文本。需要注意的是解压出来的正文中,换行字符并非Windows下的”\r\n”,而是0x2029,据了解说是symbian下的换行符。这意味着你可能需要特别处理(如替换0x2029为\r\n)0x2029才能在windows下显示出全部的文本。
现在说说封面,其Content是个图像文件,你读出数据然后另存为图像就可以。图像具体格式信息存储在对应的功能块中。
再说章节偏移,里面存储的是各个章节在正文(解压后的文本)中的偏移,即表示第几章是从何处开始的。每个偏移使用4个字节,由此我们可以知道Content总一共存了((Length-9)/4)个偏移。
现在说章节标题,它的Content是一个简单模式的重复。这个模式就是:标题长度-标题内容。其中标题长度用1字节保存,标题内容紧跟在标题长度之后,内容也是UNICODE的,不过是明文,没压缩的。用个表来说明下就一目了然了:
Chap1长度 | Chap1内容 | Chap2长度 | Chap2内容 | …… | ChapN长度 | ChapN内容 |
页面偏移不具体写了,下表中列出了其格式,对应于0x87。
对应的块标识 | 数据块描述 | Content | 字段大小 | 字段描述 | 共计 |
0x81 | 正文结束 | BlockRandom | 4 | 一个BlockRandom指向一个 正文数据块的RandVal。 | 4*N |
…… | 4*(N-2) | ||||
BlockRandom | 4 | ||||
0x82 | 封面 | Image | N | 图片文件数据 | N |
0x83 | 章节偏移 | Offset | 4 | 每个Offset都代表了一个章节 在正文数据中的偏移。 | 4*N |
…… | 4*(N-2) | ||||
Offset | 4 | ||||
0x84 | 章节标题 | Len 1 | 1 | 第一章标题长度 | Len(1) +Len(2) +… +Len(n) +n |
Cont 1 | Len(1) | 第一章标题内容 | |||
…… | / | / | |||
Len n | 1 | 第n章标题长度 | |||
Cont n | Len(n) | 第n章标题内容 | |||
0x87 | 页面偏移 | Offset | 4 | 此块与章节偏移块类似,都存储的是偏移 不过这里是每一页的偏移。 | 4*N |
…… | …… | ||||
Offset | 4 |
表-5 各数据块的Content格式
最后,来看看在一个完整的UMD文件中,各个块的详细分布情况:
0xDE9A9B89 | UMD文件标识 | UMD文件标识 |
0x01 | UMD文件说明(文本,图片) | 基本属性头 |
0x02 | 文件标题 | |
0x03 | 作者 | |
0x04 | 年 | |
0x05 | 月 | |
0x06 | 日 | |
0x07 | 小说类型 | |
0x08 | 出版商 | |
0x09 | 零售商 | |
0x0b | 内容长度 | |
0x83 | 章节偏移,功能块 | 章节偏移及标题 |
{Data} | 章节偏移,数据块 | |
0x84 | 章节标题,功能块 | |
{Data} | 章节标题,数据块 | |
{Text Data 1} | 正文压缩数据块,下同(蓝色) | 正文压缩数据 (ZLIB) |
{Text Data 2} |
| |
…… |
| |
0x0a | CID标识,随机出现在正文数据块间,原因不明 | |
…… |
| |
0xf1 | Licence Key,随机出现在正文数据块间,原因不明 | |
…… |
| |
{Text Data n-1} |
| |
{Text Data n } |
| |
0x81 | 正文结束,功能块 | 正文数据索引 |
{Data} | 正文结束,数据块(保存所有的正文数据块索引) | |
0x82 | 封面,功能块 | 封面 |
{Data} JPG | 封面,数据块(JPG) | |
0x87 | 页面偏移,功能块,字号、屏幕宽度不同会有多个 | 页面偏移 |
{Offset Data 1} | 页面偏移,数据块,下同 | |
0x87 | 页面偏移2 | |
{Offset Date 2} |
| |
…… |
| |
0x87 | 页面偏移m | |
{Offset Date m} |
| |
0x0c | UMD文件结束,功能块 | 结束 |