C++程序反編譯筆記(21) 掃雷的核心算法和數(shù)據(jù)結(jié)構(gòu)
????大家新年好啊!
????我覺得最后再分析一下掃雷的核心算法和數(shù)據(jù)結(jié)構(gòu), 就可以結(jié)束了. 如果疑問, 可以提出來.

雷區(qū)數(shù)據(jù)結(jié)構(gòu)
????通過繪制雷區(qū)的的代碼可以找到表示雷區(qū)的變量.


? ? 上面的代碼將 g_compDCs[index] 里的位圖數(shù)據(jù)復(fù)制到窗口中,?g_compDCs是一個(gè)HDC[16]的數(shù)組, 每個(gè)元素保存了一幅圖片, 比如未點(diǎn)擊的圖片, 數(shù)字1-9的圖片, 旗子的圖片等. 將 index 替換成 0 ~ 15 的常數(shù), 可以看到對(duì)應(yīng)的是哪幅圖片.
????而g_mineTable就是存放雷區(qū)信息的數(shù)組了. 這是一個(gè)char[864]的二維數(shù)組, 864 = 32 * 27, 每行32個(gè)元素, 這就是自定義時(shí)最大的地圖是24 * 30的原因. 上圖中的循環(huán)是從1開始的, 說明數(shù)組中的0索引不使用, 大概是為了符合從1開始的習(xí)慣.
????數(shù)組元素占8位, 其中低5位是圖片索引, 可以從 & 0x1F 看出來, 0x1F = 0001 1111. 高3位是3個(gè)標(biāo)志位, 下面再說.?

擴(kuò)展的算法
????所謂擴(kuò)展, 就是點(diǎn)一個(gè)0格子后, 判斷周圍的格子是否要自動(dòng)點(diǎn)開.
????通過在鼠標(biāo)彈起的地方下斷點(diǎn), 然后單步執(zhí)行這種方法找出點(diǎn)開格子的函數(shù).?

????

????0x40 = 0100 0000, 說明雷區(qū)數(shù)組元素的位6是表示格子有沒有點(diǎn)開過的. 繼續(xù)跟蹤

????cell 是雷區(qū)元素指針, *cell > 0 說明最高位為0. 代碼是執(zhí)行到這個(gè)if語句內(nèi)部的.

其中g(shù)_array是3個(gè)全局變量合成的一個(gè)變量, 它實(shí)際是一個(gè)100個(gè)點(diǎn)的數(shù)組, 保存的是當(dāng)前格子周圍的所有0格子的坐標(biāo).?

????g_array.Reset 是這樣的

????后面的代碼就是打開當(dāng)前格子及它周圍的8個(gè)格子了.

????(cell & 0x40) != 0 說明這個(gè)格子處理過了, 直接返回. 16應(yīng)該是一個(gè)特殊的值, 不代表具體的格子, 14則是 ? 的格子, 可能也有什么特殊意義.
????dword_10057A4 是已經(jīng)處理過的格子數(shù)量, 與判斷游戲是否結(jié)束有關(guān), 這個(gè)沒有深入分析.
????接著, 用CountMines計(jì)算當(dāng)前格子周圍有多少個(gè)地雷, 標(biāo)志這個(gè)格子已經(jīng)處理過來, 繪制點(diǎn)開的格子圖片, 如果是0格子的話, 就加入到g_array中.

????這個(gè)函數(shù)計(jì)算周圍雷數(shù), 最外層的 if 意義不明. 最里面的 if 可以推出 雷區(qū)數(shù)組元素的最高位表示該格子是否是地雷.

后話
????對(duì)于沒有深入分析掃雷的原因, 我說明一下:
????我覺得研究的是反編譯的技術(shù), 目標(biāo)是構(gòu)建出可以運(yùn)行的項(xiàng)目, 并且這個(gè)項(xiàng)目編譯出來的程序和原來的程序盡可能相等. 至于源代碼, 想要百分百還原出來是不可能的, 函數(shù)名, 注釋這些信息, 在編譯的時(shí)候就完全被去掉了(除非是調(diào)試版的程序).
????接下來要去找工作了, 有時(shí)間的話會(huì)反編譯Windows的記事本程序.
????掃雷項(xiàng)目的下載地址是 :??https://gitee.com/chenguc/Saolei.