【摸鱼】新游戏神尾的引擎的一点解析
顺便也分析一下图片压缩算法。
这个引擎的图像格式是KG。文件头部4字节GCGK标识。
IDA走起。
老办法,搜一波扩展名直接定位。
文件头12字节。
结构如下:
图像数据分成两部分。
1、一个int数组。大小是 图像高度 * sizeof( int )
1、图像数据。
上面这个函数只负责把文件读入内存,还没解析。
IDA里可以按X查找哪里调用了这个函数。
看到只有一个地方,直接过去看。
上面一坨好像只是把文件头的信息复制到别的地方去。先不管。
继续往下看。
几个看起来很长的嵌套循环。
这里就是解压图像数据的代码。
IDA的代码还是挺好看懂,但是还需要整理一下。
VS走起。
解压算法简述:
offset_table记录了在压缩的图像数据中,每一行的字节的起始位置。
所以使用一个循环来遍历offset_table的每个值,解压每一行像素。
每一行的第一个字节是透明度,第二个字节是像素数量。
也就是说,该算法把透明度相同的像素的透明度值压缩成了一个。
例如:有20个像素,完整的数据需要20*4=80个字节来储存。把透明度值压缩之后,
只需要1(透明度)+1(数量)+20(像素)*3=62字节。
并且如果遇到完全透明的像素,只输出2个字节即可。
循环判断:透明度+数量,然后读取像素(如果有) 即可完成一行像素的解压。
重复上述过程解压每一行像素,即可完成整个图像的解压。
压缩过程如下:
它检查每一行的每个像素,以第一个遇到的像素为基准,往右查找,直到遇到透明度不同的像素或者到达行尾。
完成一次查找后,输出一个字节表达本次查找遇到的透明度,再输出一个字节表达本次查找到了多少个透明度相同的像素。
接着输出本次查找到的像素的RGB值,每个像素3字节。如果本次查找到的像素为完全透明,则不输出任何像素字节。
重复上述查找、输出字节过程,直到查找完一行像素。
完成一行查找后,记录本行输出的字节数,作为下一行输出字节的起始位置,也就是offset。
重复上述过程直到查找完整个图像的每一行。
最后把每一行输出的字节的offset写入offset_table即可完成压缩。
这个引擎的图像格式是KG。文件头部4字节GCGK标识。
IDA走起。
老办法,搜一波扩展名直接定位。
文件头12字节。
结构如下:
图像数据分成两部分。
1、一个int数组。大小是 图像高度 * sizeof( int )
1、图像数据。
上面这个函数只负责把文件读入内存,还没解析。
IDA里可以按X查找哪里调用了这个函数。
看到只有一个地方,直接过去看。
上面一坨好像只是把文件头的信息复制到别的地方去。先不管。
继续往下看。
几个看起来很长的嵌套循环。
这里就是解压图像数据的代码。
IDA的代码还是挺好看懂,但是还需要整理一下。
VS走起。
解压算法简述:
offset_table记录了在压缩的图像数据中,每一行的字节的起始位置。
所以使用一个循环来遍历offset_table的每个值,解压每一行像素。
每一行的第一个字节是透明度,第二个字节是像素数量。
也就是说,该算法把透明度相同的像素的透明度值压缩成了一个。
例如:有20个像素,完整的数据需要20*4=80个字节来储存。把透明度值压缩之后,
只需要1(透明度)+1(数量)+20(像素)*3=62字节。
并且如果遇到完全透明的像素,只输出2个字节即可。
循环判断:透明度+数量,然后读取像素(如果有) 即可完成一行像素的解压。
重复上述过程解压每一行像素,即可完成整个图像的解压。
压缩过程如下:
它检查每一行的每个像素,以第一个遇到的像素为基准,往右查找,直到遇到透明度不同的像素或者到达行尾。
完成一次查找后,输出一个字节表达本次查找遇到的透明度,再输出一个字节表达本次查找到了多少个透明度相同的像素。
接着输出本次查找到的像素的RGB值,每个像素3字节。如果本次查找到的像素为完全透明,则不输出任何像素字节。
重复上述查找、输出字节过程,直到查找完一行像素。
完成一行查找后,记录本行输出的字节数,作为下一行输出字节的起始位置,也就是offset。
重复上述过程直到查找完整个图像的每一行。
最后把每一行输出的字节的offset写入offset_table即可完成压缩。