pvzclass是如何實(shí)現(xiàn)的?pvzclass源代碼初步分析(8)Creaters
PVZ.h給了pvzclass修改、監(jiān)控游戲內(nèi)多種對(duì)象,乃至直接刪除它們的能力。
但是生成新的對(duì)象,就必須用到pvzclass的另一部分——Creaters(.h/.cpp)。
強(qiáng)烈建議在閱讀本篇前先閱讀第4篇和第5篇的內(nèi)容。
注:本文以2021.8.11更新的版本為準(zhǔn)。



Creaters.h
雖然名叫"Creaters",但這份文件包含的命名空間卻并不帶's',而是"Creater"。
該命名空間下包含三個(gè)部分:常用函數(shù)、宏定義、函數(shù)聲明。
常用函數(shù)包括 makeshort() 和 xytorc()?兩個(gè)工具函數(shù)。
前者的作用看代碼就知道了,是將兩個(gè)8字節(jié)的數(shù)拼成一個(gè)16字節(jié)的數(shù):

這個(gè)函數(shù)主要用于將匯編命令宏(通常是多個(gè)8字節(jié)的數(shù))轉(zhuǎn)換為 WriteMemory() 能識(shí)別的整數(shù)型變量。
xytorc()?的作用則是將行列轉(zhuǎn)換為PVZ內(nèi)部的xy坐標(biāo)。
代碼也很簡(jiǎn)單,判斷是否為六行場(chǎng)景,根據(jù)對(duì)應(yīng)公式轉(zhuǎn)換。
宏定義部分的代碼比較擠,除了PI以外都不能一眼看出其作用。

但仔細(xì)觀察,就會(huì)發(fā)現(xiàn)它們的共同之處:

看來(lái)這些宏和 Asmfuntions.h 中某一段宏的作用一樣,都是應(yīng)用INVOKE宏而已。
剩下的則是函數(shù)聲明,以及結(jié)構(gòu)體 VaseCreateInfo 的定義。
函數(shù)的具體定義在 Creaters.cpp?中可以看到。
VaseCreateInfo 可以存儲(chǔ)罐子的所在行列、外觀、類(lèi)型和內(nèi)容物。
這個(gè)東西的存在純粹是因?yàn)?CreateVase() 的第一聲明太過(guò)繁雜(參數(shù)有7個(gè),單個(gè)聲明就占了250字符左右),不簡(jiǎn)潔。
如果沒(méi)有接觸過(guò)2021.8.10更新,以及這之后的版本的 pvzclass ,可能會(huì)對(duì)部分函數(shù)的類(lèi)型感到陌生:

這是2021.8.10更新的重要內(nèi)容之一:引入C++11的 std::shared_ptr?和 std::make_shared?等四個(gè)內(nèi)容,換下了以前版本 pvzclass?絕大部分C指針和?new 。相關(guān)的宏定義在 PVZ.h 中:

據(jù)貢獻(xiàn)者 theflysong (github名)的說(shuō)法,這是為了避免內(nèi)存泄漏。
如果你并不了解 std::shared_ptr?和 std::make_shared ,你可以近似認(rèn)為 std::shared_ptr?是一個(gè)加強(qiáng)版的指針,而 std::make_shared?則是其專(zhuān)屬的 new 。
?std::unique_ptr?和?std::make_unique?或許是類(lèi)似的,但這次更新只是加入了它們,并沒(méi)有具體應(yīng)用。
回到 Creaters?本身。既然有了前面的宏定義,那么函數(shù)的原理基本已經(jīng)確定了:代碼注入。
下面解讀兩個(gè)特殊的代碼注入。
Creaters.cpp 中幾個(gè)特殊的代碼注入
CreateZombie()
CreateZombie()?中注入代碼前對(duì)PVZ本體的程序做了一些 WriteMemory() ,這些代碼可以幫助?CreateZombie()? 獲取到原本被直接拋棄的僵尸對(duì)象內(nèi)存地址。
但是 CreateZombie() 結(jié)束后,這些改變并沒(méi)有被還原。(實(shí)際上影響不大,不會(huì)導(dǎo)致PVZ出現(xiàn)任何問(wèn)題)
AsmInit()
在 Creaters.h?中,CreateProjectile() ,?__CreatePortal() ,?CreatePortal()?三個(gè)函數(shù)的聲明前有同一句注釋?zhuān)?br>

原因很簡(jiǎn)單,AsmInit()?可以將上述三個(gè)函數(shù)需要的匯編代碼先行注入,并將代碼需要的部分參數(shù)設(shè)置好。

以上為 CreateProjectile()?的一個(gè)聲明。
這個(gè)函數(shù)并沒(méi)有進(jìn)行代碼注入,卻有一個(gè) CreateThread() ,顯然應(yīng)該有誰(shuí)提前注入了代碼。
完成這一工作的正是 AsmInit()。
__CreatePortal()?也有類(lèi)似的情況。
AsmInit()?將兩段代碼分別注入到了 Variable+16?和 Variable+200?的位置,設(shè)置的參數(shù)也與Variable直接相關(guān)。這意味著只要一次AsmInit(),就可以自由使用上面的三個(gè)函數(shù)。
但同時(shí),如果亂動(dòng) Variable,AsmInit()?就會(huì)失去其效用,其他函數(shù)也可能會(huì)產(chǎn)生意想不到的問(wèn)題。
當(dāng)初天青在教程中有意不說(shuō) Variable?的作用,或許就是防止有人亂用吧。

下一期將解讀 Extension.h?和 utils.h?兩個(gè)頭文件的代碼。