欢迎光临散文网 会员登陆 & 注册

C++程序反编译笔记(21) 扫雷的核心算法和数据结构

2023-01-24 16:18 作者:GC_CH  | 我要投稿

    大家新年好啊!

    我觉得最后再分析一下扫雷的核心算法和数据结构, 就可以结束了. 如果疑问, 可以提出来.

雷区数据结构

    通过绘制雷区的的代码可以找到表示雷区的变量.

    上面的代码将 g_compDCs[index] 里的位图数据复制到窗口中, g_compDCs是一个HDC[16]的数组, 每个元素保存了一幅图片, 比如未点击的图片, 数字1-9的图片, 旗子的图片等. 将 index 替换成 0 ~ 15 的常数, 可以看到对应的是哪幅图片.

    而g_mineTable就是存放雷区信息的数组了. 这是一个char[864]的二维数组, 864 = 32 * 27, 每行32个元素, 这就是自定义时最大的地图是24 * 30的原因. 上图中的循环是从1开始的, 说明数组中的0索引不使用, 大概是为了符合从1开始的习惯.

    数组元素占8位, 其中低5位是图片索引, 可以从 & 0x1F 看出来, 0x1F = 0001 1111. 高3位是3个标志位, 下面再说. 

扩展的算法

    所谓扩展, 就是点一个0格子后, 判断周围的格子是否要自动点开.

    通过在鼠标弹起的地方下断点, 然后单步执行这种方法找出点开格子的函数. 

    

    0x40 = 0100 0000, 说明雷区数组元素的位6是表示格子有没有点开过的. 继续跟踪

    cell 是雷区元素指针, *cell > 0 说明最高位为0. 代码是执行到这个if语句内部的.

其中g_array是3个全局变量合成的一个变量, 它实际是一个100个点的数组, 保存的是当前格子周围的所有0格子的坐标. 

    g_array.Reset 是这样的

    后面的代码就是打开当前格子及它周围的8个格子了.

    (cell & 0x40) != 0 说明这个格子处理过了, 直接返回. 16应该是一个特殊的值, 不代表具体的格子, 14则是 ? 的格子, 可能也有什么特殊意义.

    dword_10057A4 是已经处理过的格子数量, 与判断游戏是否结束有关, 这个没有深入分析.

    接着, 用CountMines计算当前格子周围有多少个地雷, 标志这个格子已经处理过来, 绘制点开的格子图片, 如果是0格子的话, 就加入到g_array中.

    这个函数计算周围雷数, 最外层的 if 意义不明. 最里面的 if 可以推出 雷区数组元素的最高位表示该格子是否是地雷.

后话

    对于没有深入分析扫雷的原因, 我说明一下:

    我觉得研究的是反编译的技术, 目标是构建出可以运行的项目, 并且这个项目编译出来的程序和原来的程序尽可能相等. 至于源代码, 想要百分百还原出来是不可能的, 函数名, 注释这些信息, 在编译的时候就完全被去掉了(除非是调试版的程序).

    接下来要去找工作了, 有时间的话会反编译Windows的记事本程序.

    扫雷项目的下载地址是 :  https://gitee.com/chenguc/Saolei.

C++程序反编译笔记(21) 扫雷的核心算法和数据结构的评论 (共 条)

分享到微博请遵守国家法律