使用“DREK”签名 (*.drk) 解码未知图像格式

逆向工程 二元分析 文件格式 开箱 解压 二元诊断
2021-06-15 02:40:41

有一些未知的图像,我想将其解码为RRGGBBAA格式。这是一项非常艰苦的工作,但目前我可以理解,二进制文件的哪一部分负责什么。但是我被卡在最后,我不知道如何继续。这是我发现的:

我知道,第一张图片的大小是 9*3。

文件开头有一些表格:

0x08 0x08 0x10
0x08 0x08 0x08
0x00 0x00 0x00
0x10 0x10 0x18
0x18 0x18 0x20
0x10 0x14 0x18
0x08 0x08 0x10

这是图像数据:

0x09 0x00 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x00 0x00 0x00
0x0c 0x00 0x00 0x00 0x00 0x01 0x03 0x09 0x01 0x01 0x02 0x11 0x01 0x01 0x02 0x02
0x01 0x0f 0x0f 0x03 0x0a 0x03 0x08 0x05 0x08 0x04 0x08 0x03 0x09 0x00 0x09 0x00
0x0c 0x00 0x02

表中元素的数量可以被三除,所以我认为它是某种 RRGGBB 调色板。

但我不知道如何解码图像数据。它的大小不是 9*3,所以它可能被压缩。这是一个非常小的图像,我认为这就是压缩使二进制文件比以前更大的原因。


编辑:

我在这里上传了这个文件:下载文件

我给它上色,以更好地理解它的结构。

文件的第一部分:

十六进制

  • 红色部分只是某种文件签名。
  • 棕色部分是以字节为单位的文件长度。(1107)
  • 绿色显示,文件中存储了多少图像(当前为一张)
  • 蓝色部分显示表格开始的位置(0x60 = 第 96 个字节)
  • 灰色部分显示,表格中的内容有多长。目前是 7,所以表有 7*3 = 21 个字节,其他值是CD
  • 桌子是紫色的

文件的第二部分:

十六进制

  • 蓝色部分是图像的标题。
  • 黄色部分显示,图像的长度(51bytes)
  • 红色是图片宽度,棕色是高度(3*9)
  • 每个文件中的 8 个零字节(用绿色选择)是相同的。

我又上传了两个文件。

  • 一个类似的小文件:下载
  • 还有一个更大的:下载

更大的文件的分辨率为 800*600 我怀疑是这个(截图):

背景

在开头(从3C0),每个 4 字节组中只设置前两个或三个字节。0xD28,我无法识别任何模式。


编辑2:

Spektre 的代码适用于大多数文件。但是有一些小图标,带有透明度,看起来很扭曲。

例如这个图标:下载

这就是它在棕色背景下的样子:

图标

在这种情况下,扫描线不是固定宽度的。并且252每条扫描线开头的未知标志和254每32字节后的标志也不同。

我可以识别二进制中的模式和对称性,但我还没有弄清楚它是如何工作的。

我对图标图像数据的扫描线进行了着色,以便更好地概览:

图标十六进制

他们中的许多人以 0x02


编辑3:

我上传了一些图片,并附有截图:

我又上传了两张图片,我发现这些图片失真了。第一个是图标,几乎是完美的。第二个是龙,几乎无法辨认。不幸的是,我无法提供这两个的屏幕截图:下载

在 JS 中实现了核心算法(来自Spektre 的回答)。可以在这里找到并在线编辑:JSFiddle 链接


编辑4:

我在山和龙方面取得了一些进展。

我认为,标志字节的前 7 位显示,行开始的 x 坐标( ´xstart = flag>>1;´ )。最低有效位是一个开关,用于标记该行是否具有此偏移量。您可以在此处尝试/编辑当前代码:JSFiddle 链接

结果是这样的:

山_进步

预期的结果将与此类似(不远):

山

山周围扭曲的部分是阴影/透明度,但我仍然无法找到关于具有 alpha 值的块的开始和结束位置的任何标记/标志。


编辑5:

我想,我在山的图像中找到了一个模式。在标志字节之后,下一个字节可能会显示 ( flag2>>1)(Color, Alpha)行的开头有多少个块。

左边的山现在看起来稍微好一点:

山左

不幸的是,此更改破坏了其他图像

2个回答

[Complete ReEdit3] 进一步改进并缩短文本以适应 30KB 的限制

首先输入一些我是如何到达这里的(对于将来尝试对不同格式执行相同操作的读者)。

  1. 图像数据大小

    将提供的背景图像与其原始图像大小进行比较xs*ys揭示了直接依赖性,这意味着没有压缩或具有始终相同的像素与数据比的压缩。我假设没有压缩。

  2. 图像数据对齐。

    第一个数字DWORD与零填充对齐。通过仔细观察,它总是DWORD[ys]暗示每个ScanLine 的一些信息数字在不断增加。对于800x600带有 step 的图像,826它是包含相对偏移量的ScanLine目录。测试证实了这一点:

    模式

    小图像太小,看不到任何细节,所以我完全忽略了它们。

    垂直线是包含某种信息ScanLine 的第一个开始26 BYTES不一致经过更多的教学和像素测量后,这些线出现,每条线可能包含一些标志信息(如:平铺高光、透明度或效果蒙版)。如果跳过结果更接近:xsBYTE33 BYTES

    颜色

  3. 颜色

    我只是拿了你的ScreenShot并将第一个解码的像素与其进行了比较。在调色板中找到匹配每个图像颜色的颜色索引,发现差异是简单的增量。所以我首先在解码时增加颜色索引并且结果很好。经过一些研究,我发现调色板数据具有257颜色而不是一开始假设的颜色,256因此我更改调色板偏移以进行补偿(不再需要更改颜色索引)暗示第一个颜色条目(通过偏移更改跳过)是某种特殊颜色(见项目符号#1)。后来我推断它根本不是颜色,也不属于调色板数据,而是属于标题数据。现在的结果是:

    最终输出

    如果我不会发现颜色依赖性,那么我会为解码和屏幕截图图像做直方图,然后分析对大多数出现的颜色索引的依赖性,希望能发现某种模式。

  4. 比较

    我将解码后的图像与您的ScreenShot像素匹配,但图像不匹配。可能对它应用了一些后期处理或照明,或者解码中有一些错误。很难决定,因为我无法访问原始应用程序或了解其内部工作原理。您应该检查更多图像以查看是否存在一些伪影。小图像无法使用,因为您不知道内容,而且尺寸很小,无法显示任何重要细节......

  5. 透明图片

    那么Iter项目ATOR发现,一些文件没有正确地解码,并推断它是与透明度(如图像是图标和后来也精灵)。因此进行了一些更深入的分析以取得一些进展。

    经过一些思考和循环之后,我创建了特殊的调试视图,以便我可以以某种方便的方式查看编码图像数据、参考屏幕截图和相应的调色板颜色,以发现模式或依赖项。结果是这样的:

    手动颜色交叉参考

    我只是将数据的每个像素/字节渲染为正方形,颜色与调色板条目相对应,并在其中渲染十六进制代码。在下面渲染了编码的ScanLine和屏幕截图ScanLine(里面印有来自图像调色板的交叉匹配颜色代码。然后我痛苦地交叉引用Paint 中的两个ScanLine,在比较了所有图标后,我开始推断编码格式。间隙意味着某种标志或前缀,序列或特殊命令。将这些规则实施到解码器中后,图像突然变得更好看。

    我处于一种状态,我得到的所有图标都是像素完美的,但在更多图像(精灵)进入后,很明显某些代码可能是可变的,需要对文件中的块进行更深入的分析来搜索一些代码表或字典。

    为了帮助进行分析,我将解码器调试视图重写为具有识别完美颜色匹配(正方形)和接近颜色匹配(圆形)的能力的单独类,以便首先更好地评估非透明像素上的代码。

    更好的调试视图

    我从这样的规则开始:

    我从中推断出这一点(不完全正确,请参阅实际状态的代码):

    前 2 个字节是标志 03 unknonw 标志/命令跳过标志可以跟随!

    • 也结束序列 23(标志和像素跟随为 01) 02 扫描线结束标志和像素跟随,序列结束(如果不是前 5 个像素)序列开始:13,17,23,27,2B 像素跟随:0D ,0F,12,15,16,18,1A,1C,1D,1E 标志或像素如下:06,07,0C,0E?,10

    • 序列不能作为第一个像素开始,否则它是像素

    • 序列可以在 5 个像素已经完成后停止,否则 01/03 只是像素

    我后来直接在解码器中对其进行了更多改进

  6. 扫描线偏移

    当我发现ScanLine的第一个字节是否用作透明图像的偏移时,一些图像看起来好多了。Ite Ator发现它是偏移量和 LSB 位是这个能力的开关。我发现如果第一个ScanLine的第一个 BYTE0xFC那么它不是透明图像并且解码在第一个项目符号中描述。mode 0/1在解码器中引入了变量来识别这一点。并且偏移量仅在模式 1(透明)下有效。我还发现,offset=(flag>>1)+1因为它从编码效率方面更有意义(测试确认这是正确的假设)。

  7. 文件块

    为了寻找丢失的代码表或字典,我开始剖析文件格式(尚未使用的每个字节)。经过一些编码和分析后,我向解码器添加了另一个调试输出,该输出以对齐的形式为每个文件/帧创建文本,因此我可以轻松地比较我得到的所有图像,因为在两个 ro 更多十六进制视图中进行比较不是很方便。

    这里的标题数据:

    file                          K  E  R  D              file_size        frames image_ofs                                                                                                                                                                                                   palette_ofs used_colors
    dragon              00000000: 4B 45 52 44 C8 00 00 00 A1 BF 01 00 01 00 0B 00 60 03 00 00 01 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 2E 00 0A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00 00 01 00 00 18 CD CD CD 
    mountain            00000000: 4B 45 52 44 C8 00 00 00 58 8C 01 00 01 00 02 00 60 03 00 00 01 00 00 00 54 00 00 00 00 00 00 00 56 8A 01 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00 00 01 00 00 18 CD CD CD 
    icon0               00000000: 4B 45 52 44 C8 00 00 00 4F 05 00 00 01 00 01 00 60 03 00 00 01 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00 5A 00 00 00 18 CD CD CD 
    icon1               00000000: 4B 45 52 44 C8 00 00 00 4F 05 00 00 01 00 01 00 60 03 00 00 01 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00 59 00 00 00 18 CD CD CD 
    icon2               00000000: 4B 45 52 44 C8 00 00 00 4D 05 00 00 01 00 01 00 60 03 00 00 01 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00 50 00 00 00 18 CD CD CD 
    icon3               00000000: 4B 45 52 44 C8 00 00 00 46 0B 00 00 01 00 01 00 60 03 00 00 01 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00 00 01 00 00 18 CD CD CD 
    back                00000000: 4B 45 52 44 C8 00 00 00 78 9D 07 00 01 00 01 00 60 03 00 00 01 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 00 00 01 00 00 18 CD CD CD 
    

    这里是 ImageData:

    file                                                                    frame sz          xs          ys                                                                                                                                                                                                                      xs    ys                
    dragon              00000360: 00 00 00 00 00 00 00 00 00 00 00 00 08 CD 01 00 04 25 00 00 B2 00 00 00 5B 00 00 00 E5 FF FF FF B3 FF FF FF 00 00 00 00 C0 03 00 00 C4 28 00 00 00 00 00 00 00 CD CD CD 00 00 00 00 04 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD B2 00 5B 00 00 00 00 00 
                        000028C4: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 02 00 A1 26 00 00 B6 00 00 00 5B 00 00 00 DD FF FF FF B4 FF FF FF 00 00 00 00 24 29 00 00 C5 4F 00 00 60 03 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 B6 00 5B 00 00 00 00 00 
                        00004FC5: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 03 00 57 27 00 00 C4 00 00 00 5E 00 00 00 D6 FF FF FF B1 FF FF FF 00 00 00 00 25 50 00 00 7C 77 00 00 C4 28 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C4 00 5E 00 00 00 00 00 
                        0000777C: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 04 00 26 28 00 00 BF 00 00 00 64 00 00 00 D3 FF FF FF AA FF FF FF 00 00 00 00 DC 77 00 00 02 A0 00 00 C5 4F 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 BF 00 64 00 00 00 00 00 
                        0000A002: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 05 00 70 29 00 00 C3 00 00 00 67 00 00 00 D0 FF FF FF A6 FF FF FF 00 00 00 00 62 A0 00 00 D2 C9 00 00 7C 77 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C3 00 67 00 00 00 00 00 
                        0000C9D2: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 06 00 DA 29 00 00 C6 00 00 00 6C 00 00 00 CF FF FF FF A2 FF FF FF 00 00 00 00 32 CA 00 00 0C F4 00 00 02 A0 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C6 00 6C 00 00 00 00 00 
                        0000F40C: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 07 00 33 2A 00 00 C5 00 00 00 6F 00 00 00 D1 FF FF FF 9F FF FF FF 00 00 00 00 6C F4 00 00 9F 1E 01 00 D2 C9 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C5 00 6F 00 00 00 00 00 
                        00011E9F: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 08 00 1A 2B 00 00 C0 00 00 00 6C 00 00 00 D4 FF FF FF A2 FF FF FF 00 00 00 00 FF 1E 01 00 19 4A 01 00 0C F4 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C0 00 6C 00 00 00 00 00 
                        00014A19: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 09 00 59 29 00 00 BE 00 00 00 68 00 00 00 DA FF FF FF A7 FF FF FF 00 00 00 00 79 4A 01 00 D2 73 01 00 9F 1E 01 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 BE 00 68 00 00 00 00 00 
                        000173D2: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 0A 00 E5 26 00 00 B5 00 00 00 62 00 00 00 E1 FF FF FF AB FF FF FF 00 00 00 00 32 74 01 00 17 9B 01 00 19 4A 01 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 B5 00 62 00 00 00 00 00 
                        00019B17: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 0B 00 CA 23 00 00 AB 00 00 00 5A 00 00 00 EA FF FF FF B3 FF FF FF 00 00 00 00 77 9B 01 00 41 BF 01 00 D2 73 01 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 AB 00 5A 00 00 00 00 00 
    mountain            00000360: 00 00 00 00 00 00 00 00 00 00 00 00 08 CD 01 00 A7 BA 00 00 37 01 00 00 D3 00 00 00 00 00 00 00 C9 FF FF FF 00 00 00 00 C0 03 00 00 67 BE 00 00 00 00 00 00 00 CD CD CD 00 00 00 00 04 CD CD CD 00 00 00 00 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 37 01 D3 00 00 00 00 00 
                        0000BE67: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 02 00 2B C7 00 00 3C 01 00 00 DD 00 00 00 FC FF FF FF C4 FF FF FF 00 00 00 00 C7 BE 00 00 F2 85 01 00 60 03 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3C 01 DD 00 00 00 00 00 
    icon0               00000360: 00 00 00 00 00 00 00 00 00 00 00 00 08 CD 01 00 2F 01 00 00 0D 00 00 00 0D 00 00 00 11 00 00 00 F9 FF FF FF 00 00 00 00 C0 03 00 00 EF 04 00 00 00 00 00 00 00 CD CD CD 00 00 00 00 04 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 0D 00 0D 00 00 00 00 00 
    icon1               00000360: 00 00 00 00 00 00 00 00 00 00 00 00 08 CD 01 00 2F 01 00 00 0D 00 00 00 0D 00 00 00 11 00 00 00 F9 FF FF FF 00 00 00 00 C0 03 00 00 EF 04 00 00 00 00 00 00 00 CD CD CD 00 00 00 00 04 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 0D 00 0D 00 00 00 00 00 
    icon2               00000360: 00 00 00 00 00 00 00 00 00 00 00 00 08 CD 01 00 2D 01 00 00 0D 00 00 00 0D 00 00 00 11 00 00 00 F9 FF FF FF 00 00 00 00 C0 03 00 00 ED 04 00 00 00 00 00 00 00 CD CD CD 00 00 00 00 04 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 0D 00 0D 00 00 00 00 00 
    icon3               00000360: 00 00 00 00 00 00 00 00 00 00 00 00 08 CD 01 00 26 07 00 00 26 00 00 00 26 00 00 00 05 00 00 00 ED FF FF FF 00 00 00 00 C0 03 00 00 E6 0A 00 00 00 00 00 00 00 CD CD CD 00 00 00 00 04 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 26 00 26 00 00 00 00 00 
    back                00000360: 00 00 00 00 00 00 00 00 00 00 00 00 08 CD 01 00 58 99 07 00 20 03 00 00 58 02 00 00 08 00 00 00 B4 FD FF FF 00 00 00 00 C0 03 00 00 18 9D 07 00 00 00 00 00 00 CD CD CD 00 00 00 00 04 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 20 03 58 02 00 00 00 00 
    

    更好地揭示结构。我从中发现了一些东西,比如帧索引和 ImageData 对齐,0x0360而不是0x0370我之前假设的。此外,0xCD模式仅存在于第一帧而不是所有位置。文件格式是这样的:

    Offset              DataType                Meaning
    --------------------------------------------------------
    Header (0x0000):
    0x0000              char[4]                 `DREK` Signature
    0x0008              DWORD                   whole file size
    0x000E              WORD                    number of frames
    0x0010              DWORD                   ImageData offset from start of file 0x0360
    0x0054              DWORD                   Palette offset from start of file 0x0060
    0x0058              DWORD                   used_colors from Palette (rest are set to (CD,CD,CD))
    --------------------------------------------------------
    Palette: (0x060)
    +0x0000             BYTE[768]               RGB palette
    --------------------------------------------------------
    ImageData (0x0360)
    +0x000E             WORD                    frame 1,2,3,4,...
    +0x0010             DWORD                   sz (ImageData size - 0x60)
    +0x0014             DWORD                   xs
    +0x0018             DWORD                   ys
    +0x0060             WORD                    xs
    +0x0064             WORD                    ys
    +0x0068             DWORD[ys]               ScanLine offsets relative to end of this table (0x0068 + ys*4)
    +0x0068+(ys*4)      BYTE[sz-68-(ys*4)]      ScanLineData
    +0x0060+sz                                  Next frame ImageData if frame<frames
    

现在终于解码器了

该代码写在Borlands BDS2006 C ++使用VCL TBitmapAnsiString因此,你需要把它改写为你这样的GFX访问和串编程环境的支持。如果您需要有关 gfx 代码含义的帮助,请参阅:

此外,文件访问函数的名称可能略有不同。我对代码做了一些注释,但无论如何它应该是显而易见的。至少对于那些已经做过这样的事情的人来说。

定义只是在调试和正常解码之间切换。如果不存在,则您根本不需要调试图像类img为了保持这个简单并且在 30KB 限制内,我删除了调试绘制类的代码(那些想要/需要它的人在编辑历史记录中查看)。这意味着忽略所有_debug_draw定义......

现在图标定义的像素被正确解码。但我做了很多假设,可能不适用于所有图像。我需要更多关于 ScreenShots 的例子来使它更健壮。代码本身应该编码 alpha 通道,也可能是位置/偏移量。

我更改了一些代码,现在我暂时忽略了当存在未正确解码的代码时会造成混乱0x02结尾ScanLine我还添加了一些我发现的特殊情况。模式 1 的 x 偏移也存在。

在代码查找行中:

// image data (decoded)

这就是主要解码的地方。解码变量是:

  • _pixel- 接下来BYTE是像素
  • _sequence- 像素序列如下(以渲染0x010x03渲染超过 5 个像素结束
  • _stop - 标记为扫描线的结束 0x02
  • seq - 只计算序列中处理了多少像素

在注释中,一些代码被标记// OK为不冲突且简单明了。其余注释表示以下代码:

  • pXX 表示像素 0xXX
  • cXX 手段代码 0xXX
  • ?XX 意味着不确定是像素还是代码
  • ?pXX 表示最有可能的像素 0xXX

Transparent ScanLine 数据在第一个 BYTE 之后开始,所以不要只跳过 2 个字节!!!

这里有一些预览:

新预览

和龙帧:

龙帧

解码器的 C++ 源代码:

#ifdef _debug_save
AnsiString dbg_hdr="file                          K  E  R  D              file_size        frames image_ofs                                                                                                                                                                                                   palette_ofs used_colors\r\n";
AnsiString dbg_img="                                                                        frame sz          xs          ys                                                                                                                                                                                                                      xs    ys                \r\n";
#endif
void load_drk(Graphics::TBitmap *bmp,AnsiString name)
    {
    AnsiString s,fnam;
    fnam=name.SubString(1,name.Length()-4);

    // variables
    BYTE *dat;
    int hnd,adr,adr0,siz;
    int i,x,y,xs,ys,sz,mode,frame,frames;
    DWORD *p,*dir,pal[256],r,g,b,a;
    // allow direct pixel access
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    // read file into memory
    hnd=FileOpen(name,fmOpenRead);
    if (hnd<0) return;
    siz=FileSeek(hnd,0,2);
        FileSeek(hnd,0,0);
    dat=new BYTE[siz];
    if (dat==NULL) { FileClose(hnd); return; }
    FileRead(hnd,dat,siz);
    FileClose(hnd);
    // decode
    for (;;)
        {
        #ifdef _debug_save
        // save frames to bmp
        s=fnam+"                    ";
        s=s.SubString(1,20);
        dbg_hdr+=s;
        dbg_img+=s;
        dbg_hdr+="00000000: "; for (i=0;i<0x60;i++) { s=s.sprintf("%X ",dat[i]); while (s.Length()<3) s="0"+s; dbg_hdr+=s; }
        dbg_hdr+="\r\n";
        #endif

        // signature
        if (siz<0x3C8) break;
        if (((DWORD*)(dat))[ 0]!='DREK') break;
        if (((DWORD*)(dat))[ 2]!=   siz) break;
        // palette
        for (adr=0x060,i=0;i<256;i++)
            {
            b=dat[adr]; adr++;
            g=dat[adr]; adr++;
            r=dat[adr]; adr++;
            pal[i]=(r<<16)|(g<<8)|(b);
            }
        // frames
        frames=((WORD*)(dat+0x0E))[0];
        adr0  =((DWORD*)(dat))[4];
        for (frame=0;frame<frames;adr0+=sz+0x60,frame++)
            {
            adr=adr0;
            // resolution
            if (adr0+0x60>siz) break;
            sz=((DWORD*)(dat+adr))[4];
            xs=((DWORD*)(dat+adr))[5];
            ys=((DWORD*)(dat+adr))[6];
            if (adr0+0x60+sz>siz) break;
            bmp->SetSize(xs,ys);

            #ifdef _debug_save
            // save frames to bmp
            if (frame) dbg_img+="                    ";
            s=s.sprintf("%X: ",adr0); while (s.Length()<10) s="0"+s; dbg_img+=s;
            for (i=adr0;i<adr0+0x68;i++) { s=s.sprintf("%X ",dat[i]); while (s.Length()<3) s="0"+s; dbg_img+=s; }
            dbg_img+="\r\n";
            #endif

            // scanline table
            dir=new DWORD[ys+1];
            if (dir==NULL) break;
            adr+=0x68;
            for (y=0;y<ys;y++)
             dir[y]=adr+(ys<<2)+((DWORD*)(dat+adr))[y];
            dir[ys]=adr0+sz+0x68;

            #ifdef _debug_draw
            // set size and palette of debug image
            img.resize(xs,ys);
            for (i=0;i<256;i++) img.rgb_pal[i]=pal[i];
            // copy reference data to debug image
            for (y=0;y<ys;y++) img.p[y].ref="";
            if ((png->Height)&&(png->Width))
                {
                Graphics::TBitmap *qqq=new Graphics::TBitmap;
                qqq->Assign(png);
                qqq->HandleType=bmDIB;
                qqq->PixelFormat=pf32bit;
                for (y=0;(y<qqq->Height)&&(y<ys);y++)
                    {
                    img.p[y].ref="";
                    img.p[y].cmp="";
                    p=(DWORD*)qqq->ScanLine[y];
                    for (x=0;(x<qqq->Width)&&(x<xs);x++)
                        {
                        for (a=0,b=256,g=768;a<256;a++)
                            {
                            r=rgb_absdistance(p[x],pal[a]);
                            if (g>r) { g=r; b=a; }
                            } a=b;
                        if (g) g='1'; else g='0';
                        img.p[y].ref+=char(BYTE(a));
                        img.p[y].cmp+=char(BYTE(g));
                        }
                    }
                delete qqq;
                }
            // copy encoded data to debug image
            for (y=0;y<ys;y++)
                {
                adr=dir[y];
                img.p[y].adr=adr;
                img.p[y].enc="";
                img.p[y].dec="";
                img.p[y].spc="";
                for (;adr<dir[y+1];adr++)
                 img.p[y].enc+=char(BYTE(dat[adr]));
                }
            #endif

            // image data (decoded)
            a=dat[dir[0]];
            mode=0; if (a!=0xFC) mode=1;
            for (y=0;y<ys;y++)
                {
                adr=dir[y];                     // get actual ScanLine offset in file
                p=(DWORD*)bmp->ScanLine[y];     // get actual ScanLine pointer in bmp
                int _sequence=0;
                int _pixel=0;
                int _stop=0;
                int seq=0;
                for (x=0;x<xs;x++) p[x]=0; x=0;

                if (mode==1)
                    {
                    // 1st ScanLine BYTE (x-offset)
                    a=dat[adr]; adr++;
                    if (BYTE(a&1)==1)
                        {
                        x=(a>>1)+1;
                        #ifdef _debug_draw
                        for (i=0;i<x;i++) img.p[y].spc+='\0';
                        #endif
                        }
                    // 2nd ScanLine BYTE special cases
                    a=dat[adr];
                    if (a==0x0D) adr++; 
                    }
                for (a=0;adr<dir[y+1];)
                    {
                    if (mode==0)
                        {
                        a=dat[adr]; adr++;              // color index
                        if (int(x&31)==0)
                            {
                            r=a;                        // flag
                            a=dat[adr]; adr++;          // color index
                            }
                        }
                    if (mode==1) for (;;)
                        {
                        a=dat[adr]; adr++;              // color index
                        if (_pixel) { _pixel=false; break; }
                        if (_sequence) { seq++; if (seq<6) break; }
                        // commands
                        if (a==0x01)
                            {
                            a=dat[adr]; adr++;
                            _pixel=true; _sequence=false;
                            //if (a==0x07) { adr++; _pixel=false; }
                            continue;
                            }
                        if (a==0x03)
                            {
                            if (!_sequence)
                                {
                                if (dat[adr]==0x18) _pixel=1;
                                continue;
                                }
                            if (_sequence==0x2B) break;
                            a=dat[adr]; adr++;
                            _pixel=true; _sequence=false;
                            //if (a==0x07) { adr++; _pixel=false; }
                            continue;
                            }
                        if (_sequence) break;
                        // unused: 0A,14,1F,20,21,22,24,25,26,28,29,2A,2C+
                        // flag/color prefix
    //                  if((a==0x02)&&(adr>dir[y]+3)) { _stop=true; break; }    // end of ScanLine
                        if (a==0x04) { _pixel=1; continue; }    // ?? ?p18 p47 ?p47
                        if (a==0x05) { _pixel=1; continue; }    // ?? ?18 ?09
                        if (a==0x06) { _pixel=1; continue; }    // ?? ?p18 ?p47
                        if (a==0x07) { _pixel=1; continue; }    // ?? ?18 ?46 ?47
                        if (a==0x08) { _pixel=1; continue; }    // ?? ?09
                        if (a==0x09) { _pixel=0; continue; }    // ?? ?p02 !!!!!!!!!!!
                        if (a==0x0B) { _pixel=1; continue; }    // ?? ?p02
                        if (a==0x0C) { _pixel=1; continue; }    // OK p1D
                        if (a==0x0D)                            // ?? c04 p23 ?c06 p05 p12
                            {
                            _pixel=0;
                            if (dat[adr]==0x02) _pixel=1;
                            if (dat[adr]==0x05) _pixel=1;   // p47 c0D p05 | c0D c05 p09
                            if (dat[adr]==0x12) _pixel=1;
                            if (dat[adr]==0x23) _pixel=1;
                            continue;
                            }
                        if (a==0x0E) { _pixel=1; continue; }    // OK p24 p26 p16
                        if (a==0x0F) { _pixel=1; continue; }    // OK p0F
                        if (a==0x10) { _pixel=1; continue; }    // OK p04
                        if (a==0x11) { _pixel=1; continue; }    // ?? ?p02
                        if (a==0x12) { _pixel=1; continue; }    // OK p27
                        if (a==0x15) { _pixel=1; continue; }    // OK p16
                        if (a==0x16) { _pixel=1; continue; }    // OK p16
                        if (a==0x18) { _pixel=1; continue; }    // OK p00 p02
                        if (a==0x19)                            // ?? p1B
                            {
                            _pixel=1;
                            if (dat[adr]==0x01) _pixel=0;
                            continue;
                            }
                        if (a==0x1A) { _pixel=1; continue; }    // OK p05 p04
                        if (a==0x1B) { _pixel=1; continue; }    // OK p1F
                        if (a==0x1C) { _pixel=1; continue; }    // OK p10
                        if (a==0x1D) { _pixel=1; continue; }    // OK p33 p1E p14
                        if (a==0x1E) { _pixel=1; continue; }    // OK p34 p50 p13

                        // test from dragon and mountain
                        if (a==0x49) { _pixel=1; continue; }    // ?? ?p02
                        if (a==0xE3) { _pixel=1; continue; }    // ?? p18
                        if (a==0xD7) { _pixel=1; continue; }    // ?? pBC
                        if (a==0xDA) { _pixel=1; continue; }    // ?? ?p02

                        // sequence start
                        if (a==0x13) { seq=0; _sequence=a; } // 03
                        if (a==0x17) { seq=0; _sequence=a; } // 01
                        if (a==0x23) { seq=0; _sequence=a; } // 01 03
                        if (a==0x27) { seq=0; _sequence=a; } // 01
                        if (a==0x2B) { seq=0; _sequence=a; } // 01 !03 03
                        if (_sequence)
                            {
                            if (adr==dir[y]+3) { _sequence=false; break; }
                            continue;
                            }
                        break;
                        }
                    if (_stop) break;
                    if (x>xs) break;
                    if ((mode==1)&&((a==0xFE)||(a==0xFF))) { a=dat[adr]; adr++; }   // shadows?

                    if ((x>=0)&&(x<xs)) p[x]=pal[a]; x++;
                    #ifdef _debug_draw
                    // copy decoded data to debug image
                    img.p[y].dec+=char(BYTE(a));
                    #endif
                    }
                }
            delete[] dir; dir=NULL;
            #ifdef _debug_save
            // save frames to bmp
            s=frame;
            while (s.Length()<3) s="0"+s;
            png->Assign(bmp);
            png->SaveToFile("decoded_"+fnam+"_"+s+".png");
            #endif
            }
        img.compute();
        break;
        }
    delete[] dat;
    }

我能够解码图像。Spektre在检测文件结构方面做得很好,调试视图在此过程中非常有用。我在JS中实现了算法,源代码在这里:https : //github.com/K-Adam/DrekDecoder

概括

每行以一个标志字节开始。它告诉解码器要写入多少像素以及使用哪种模式。共有三种可能的写入模式:

  • 不透明
  • 透明的
  • 跳过

在不透明模式下,每个像素由一个字节表示,它引用调色板中的 RGB 颜色。在透明模式下,像素是成对的字节:[Alpha, ColorIndex]

在每个序列之后跟随一个控制字节。它的结构不同于标志字节,在每种写方式上也不同。该控制字节将告知要写入多少像素以及接下来使用哪种模式,或者是否已到达行尾。

细节

标志字节

如果标志为零(flag == 0x0),则该行为空。

如果设置了 LSB,(flag & 0b1)那么在行的开头有一个偏移量,解码器将以跳过模式启动其余的位将代表偏移量的长度(flag >> 1) + 1

如果第二位设置为透明,否则将使用不透明模式:

mode = (flag & 0b10) ? Transparent : Opaque

像素数可以在 5 MSB 中找到 (flag >> 3) + 1

跳过模式

解码器将简单地将n空像素写入输出。下一个字节将是一个控制字节。

如果设置了 LSB,则解码器将切换到透明模式。其他 7 位代表透明像素的数量。

如果三个 LSB 是0b100那么解码器将继续在跳过模式,否则它将更改为不透明模式。其他位表示像素数(vv >> 3) + 1

不透明模式

每个数据字节引用调色板中的一个 RGB 颜色。

如果设置了 LSB,则解码器将切换到(v1 >> 1) + 1像素的透明模式否则控制字节的三个 LSB 是标志:

  • 0b000 行尾
  • 0b110 在不透明模式下继续
  • 0b100 切换到跳过模式

剩下的就是像素数了(v1 >> 3) + 1

透明模式

透明块由成对的序列表示:[Alpha, Color]。alpha 字节的 5 MSB 是透明度 (0-32)。颜色字节引用调色板中的 RGB 颜色。

控制字节的两个 LSB 是一个标志:

  • 0b10 行尾
  • 0b11 切换到不透明模式
  • 0b01 切换到跳过模式
  • 0b00 继续透明模式

其余的位是像素数(v1 >> 2) + 1,除了透明模式,它是(v1 >> 3) + 1

解码过程

由于颜色是通过索引引用的,因此需要两个字节来表示一个透明像素。通过观察山的左下部分,从那些交替的图案中可以清楚地看出,那里必须是透明的。圆形图标周围的颜色也不对,所以我怀疑外部像素也是半透明的。

在大图像0xFE中每 32 个字节后出现一次。它不引用调色板中的颜色,因此它必须是某种控制字节。

然后我开始用二进制写下字节,颜色似乎不对。我按照它们出现的位置和位置对这些值进行分组,然后我发现,在透明和不透明区域的边界上,最低有效位是相似的。

经过进一步分析,当我能够手动解码较小的图标时,我实现了该算法,并对较大的图标进行了测试。从那里,很容易消除结果中剩余的异常。