唐凌 发表于 2022-3-20 14:40:16

【VB.NET】解析恋活人物卡PNG文件


# 前言
不知道有没有玩过恋活人发现,人物卡是一个PNG图片文件,而这个图片文件里虽然表面上是证件照,但它还包含了人物照以及捏脸数据。
写本文的目的就是解释恋活的PNG文件是什么样的。

# PNG文件结构
PNG的文件结构算是简单的,就两个部分:PNG签名(Signature)和PNG块(Chunks)。

PNG签名长度为8个字节。
```vb
Public Const PngSignature As Long = &HA1A0A0D474E5089
```
第一个字节是0x89,最高位是置位的以防止被当做文本文件打开
第2-4字节对应字母是PNG。
第5-6字节是CRLF换行。
第7字节是EOF。
第8字节是LF换行。

每个PNG块按顺序分四部分:块大小,块类型,块数据,块CRC。
除了块数据之外,每个成员均为4字节。
PNG文件不讲究文件对齐,所有数据均紧凑排列。
块大小和块CRC字段均为大端序,因此在x86的电脑上要逆字节序使用。
块类型为4字节字符串。
第一个字符的大小写决定它是否是重要的块。若为大写,则该块是重要的块。
第二个字符的大小写决定它是否是标准的块。若为小写,则该快是私有定义的块。
第三个字符按照PNG标准,必须是大写。
第四个字符的大小写决定它是否与重要的块相关。若为大写,则当有重要的块被修改时,该块不能被直接复制使用。

通常而言,一个PNG文件里必然会有`IHDR`,`IDAT`和`IEND`三个块。其中`IHDR`必须是第一个块,`IEND`必须是最后一个块。

## IHDR块
`IHDR`块表示PNG文件头,只有13个字节,定义如下:
```vb
Public Structure PngChunkIHdr
    Dim Width As Integer
    Dim Height As Integer
    Dim BitDepth As Byte
    Dim ColorType As Byte
    Dim CompressionMethod As Byte
    Dim FilterMethod As Byte
    Dim InterlaceMethod As Byte
End Structure
```
其中`Width`表示图片横向的像素单位长度,`Height`表示图片纵向的像素单位长度,`ColorType`表示像素类型。
注意`Width`和`Height`也都是大端序的。
像素类型的值的各类定义为:
```vb
Public Enum PngPixelFormat
    Grayscale = 0
    TrueColorRGB = 2
    IndexedPalette = 3
    GrayscaleAlpha = 4
    RGBA = 6
End Enum
```

## IEND块
`IEND`块表示PNG文件尾,没有块数据。遍历块到此时应当结束遍历。

## IDAT块
`IDAT`块表示图片数据。一个PNG文件里可以有好几个`IDAT`块来表示一个图片。
PNG使用DEFLATE无损压缩算法,因此`IDAT`块里的数据必须解压后再用。
.NET的`DeflateStream`似乎不能解压PNG的`IDAT`块,且由于`PictureBox`控件能显示PNG文件,我没必要舍本逐末地自行把PNG解析成位图后再显示出来。

# 恋活的PNG文件
我最初的猜想是恋活的PNG文件里塞了一个私有的块,而经过解析后验证,并没有什么私有的块,它是在`IEND`块之后追加内容的。
根据多次测试,人物照应该在`IEND`块的34字节之后。它的结构同样是个PNG文件。
中间的34字节应该是恋活的签名(可以看见`KoiKatuCharaS`字样)。
而在人物照的`IEND`块之后的就是具体的捏脸数据了。本文不作深入解析了。

# 程序源码
本文采用Visual Basic 2010进行WinForm开发,[在GitHub上开源](https://github.com/MickeyMeowMeowHouse/DumpKoikatsu)。

0xAA55 发表于 2022-4-6 03:22:56

竟然在PNG里加自己的块。

另外如果能修改这些PNG的话,就可以给它汉化一波。
页: [1]
查看完整版本: 【VB.NET】解析恋活人物卡PNG文件