MPQ Storm库 源代码分析 一个
MPQ什么?
? ? ? ??MPQ維基上說(shuō)的非常明確。
簡(jiǎn)而言之,它是暴雪公司用于游戲數(shù)據(jù)打包的工具。星際爭(zhēng)霸,魔獸爭(zhēng)霸游戲中都有使用。該工具內(nèi)含游戲資源加密和壓縮等功能。
? ? ? ? git下載地址:https://github.com/stormlib/StormLib
? ? ? ??MPQ文件總共同擁有四種格式:
? ? ? ? ? ? ?第一種:FLAT格式。線性存儲(chǔ)。
? ? ? ? ? ? ?另外一種:Partial格式,分包的存儲(chǔ)。
? ? ? ? ? ? ?第三種:MPQE格式,加密存儲(chǔ)。
? ? ? ? ? ? ?第四種:BLOCK格式。模塊存儲(chǔ)。
? ? ? ??
FLAT格式最簡(jiǎn)單。只是該格式也支持對(duì)單個(gè)文件的加密和壓縮。
本文主要分析FLAT格式,主要包括下面五點(diǎn):
? ? ? ??一、創(chuàng)建一個(gè)MPQ文件。
? ? ? ??二、加入資源文件。
? ? ? ??三、封裝MPQ文件。
? ? ? ??四、讀取MPQ文件。
? ? ? ??五、讀取資源文件。
? ? ? ??圖一、圖二、圖三為FLAT格式的內(nèi)部格式。
圖一
? ? ? ??一、創(chuàng)建一個(gè)MPQ文件。
? ? ? ? ? ? ?主要過(guò)程是。調(diào)用SFileCreateArchive接口創(chuàng)建MPQ文件。創(chuàng)建成功之后,該接口會(huì)返回成功與否。同一時(shí)候?qū)PQ文件的句柄賦值給傳進(jìn)來(lái)的變量。
? ? ? ? ? ? ?創(chuàng)建MPQ文件具體流程是:
? ? ? ? ? ? ?1》在內(nèi)存中初始化一個(gè)加密塊StormBuffer;
? ? ? ? ? ? ?2》嘗試打開(kāi)一個(gè)同名的MPQ文件。假如已經(jīng)存在的同名文件不是MPQ文件,MPQ工具就會(huì)嘗試用MPQ文件的格式讀取該文件。為了避免同名的錯(cuò)誤。在StormLib的樣例中,有先刪除同一文件夾想的同名文件,再創(chuàng)建MPQ文件的邏輯。
? ? ? ? ? ? ?3》創(chuàng)建一個(gè)空的MPQ文件。
? ? ? ? ? ? ?4》重置文件大小。
? ? ? ? ? ? ?5》寫(xiě)入一個(gè)假的MPQ文件頭部,即圖一的A指向的區(qū)域。
MPQ的頭部有四種不同的格式。分別擁有不同的長(zhǎng)度,只是默認(rèn)有一個(gè)最大長(zhǎng)度是208字節(jié)。寫(xiě)這個(gè)假的MPQ文件頭部,最基本的目的是占位,保證第一個(gè)數(shù)據(jù)文件的寫(xiě)入位置是在正確的。
? ? ? ? ? ? ?6》假設(shè)是大文件,則創(chuàng)建大文件的索引表HetTable。
? ? ? ? ? ? ?7》創(chuàng)建HashTabl,用來(lái)索引文件的。
? ? ? ? ? ? ?以上七個(gè)過(guò)程。主要實(shí)現(xiàn)了兩個(gè)結(jié)果,1、創(chuàng)建了MPQ文件,2、在內(nèi)存中生成須要用到的MPQ數(shù)據(jù)。
? ? ? ??二、加入資源文件。
? ? ? ? ? ? ?主要過(guò)程是,調(diào)用SFileCreateFile接口。在MPQ文件里創(chuàng)建一個(gè)新的文件句柄。通過(guò)SFileWriteFile接口寫(xiě)入數(shù)據(jù)。
調(diào)用SFileCloseFile釋放內(nèi)存資源。
? ? ? ? ? ? ?具體流程:
? ? ? ? ? ? ?1》檢查是不是內(nèi)部文件。MPQ主要有兩個(gè)內(nèi)部文件,一個(gè)是listfile。另外一個(gè)attributes。假設(shè)不是內(nèi)部文件,則向MPQ文件里加入新的文件。
? ? ? ? ? ? ?2》找到MPQ文件里能夠?qū)懭氲奈恢谩<僭O(shè)是第一次寫(xiě)入。則此時(shí)是圖一B指向的區(qū)域。
但此時(shí)還沒(méi)有寫(xiě)入。
? ? ? ? ? ? ?3》通過(guò)HashString方法,將資源文件名稱(chēng)轉(zhuǎn)化為hash索引值。以新的hash索引值從當(dāng)前的HashTable中取一個(gè)TMPQHash數(shù)據(jù)結(jié)構(gòu)。
? ? ? ? ? ? ?4》假如不是第一次建立新文件。則此時(shí),通過(guò)TMPQHash的dwBlockIndex值。在FileTable做dwBlockIndex偏移。獲取新文件的入口TFileEntry。假如是第一次建立新文件。則會(huì)先到FileTable里面取出一個(gè)空暇的位置,分配一個(gè)TFileEntry。默認(rèn)是取FileTable位置做dwFileTableSize偏移,而此時(shí)dwFileTableSize值是0,所以就是FileTable表中第一個(gè)位置。即圖一B指向的區(qū)域。
同一時(shí)候分配一個(gè)TMPQHash(hash索引的取值與文件名稱(chēng)有關(guān)),分配完之后。與FileEntry關(guān)聯(lián)。在FileEntry里通過(guò)dwHashIndex索引到hash表中的位置,TMPQHash通過(guò)dwBlockIndex索引到FileEntry。
? ? ? ? ? ? ?5》拿到了FileEntry以后,在SFileAddFile_Init函數(shù)中初始化FileEntry的其它值。
經(jīng)過(guò)這些邏輯。就能夠拿到一個(gè)TMPQFile文件指針,用來(lái)寫(xiě)入數(shù)據(jù)。
? ? ? ? ? ? ?6》通過(guò)SFileWriteFile接口寫(xiě)入文件數(shù)據(jù),能夠?qū)①Y源文件分批寫(xiě)到MPQ文件里。也能夠?qū)①Y源文件一次寫(xiě)入MPQ文件里,兩種都不會(huì)有問(wèn)題。
圖二
? ? ? ? ? ? ?7》在將要寫(xiě)文件的前,會(huì)先寫(xiě)入一個(gè)扇區(qū),默認(rèn)有8字節(jié),扇區(qū)與將要寫(xiě)入的數(shù)據(jù)的加密有關(guān)聯(lián)。然后寫(xiě)入
數(shù)據(jù),數(shù)據(jù)有選項(xiàng),能夠選擇是否加密,是否壓縮,是否選擇數(shù)據(jù)作為單元文件存儲(chǔ)。
寫(xiě)入完成之后,會(huì)再次寫(xiě)入當(dāng)
前扇區(qū),詳細(xì)為什么要兩次寫(xiě)入扇區(qū),下一篇會(huì)繼續(xù)分析。圖二中。L指向的是真正的資源文件數(shù)據(jù),K、M各自是扇
區(qū)。資源文件可選擇加密和壓縮。
圖三
? ? ? ??三、封裝MPQ文件。
? ? ? ? 封裝挺重要的,封裝時(shí)會(huì)將新生成的文件相關(guān)的數(shù)據(jù)更新到MPQ文件頭部。覆蓋原先的假頭部。
封裝的主要過(guò)程是。將與文件相關(guān)的HET表和BET表,以及HASH表,文件FileTable。按順序?qū)懙阶詈笠粋€(gè)資源文件的結(jié)尾位置。
封裝邏輯運(yùn)行完之后。新的MPQ文件內(nèi)部結(jié)構(gòu)如圖三。
? ? ? ??詳細(xì)流程是: ? ? ? ? ? ? ? ?
? ? ? ? ? ? ?1》確定是否要寫(xiě)入listfile、attributes等文件,假設(shè)要,則會(huì)在MPQ文件里創(chuàng)建,并寫(xiě)入。
? ? ? ? ? ? ?2》調(diào)用SFileCloseArchive,找到能夠?qū)懭際ET、BET、HASH、FILETABLE表的位置,然后將這些表的數(shù)據(jù)寫(xiě)入到新的MPQ文件里。寫(xiě)入后MPQ文件內(nèi)部如圖三。表的數(shù)據(jù)會(huì)寫(xiě)在第N個(gè)數(shù)據(jù)文件之后。最后,回到頭部更新MPQ文件頭部。
? ? ? ? ? ? ?3》釋放內(nèi)存中的資源。
? ? ? ??四、讀取MPQ文件。
? ? ? ??主要流程是。打開(kāi)MPQ文件,先讀取頭部。在讀取正確之后,解析各個(gè)表的位置。獲取數(shù)據(jù)文件位置。而且隨意一個(gè)環(huán)節(jié)出錯(cuò)。都會(huì)退出。
? ? ? ??詳細(xì)流程:
? ? ? ? ? ? ?1》初始化一個(gè)StormBuff(與數(shù)據(jù)加密有關(guān))。然后用208字節(jié)的長(zhǎng)度,從MPQ文件頭部0位置開(kāi)始讀取,一次讀取208字節(jié)。
讀取完之后強(qiáng)制轉(zhuǎn)化為T(mén)MPQHeader數(shù)據(jù)結(jié)構(gòu)。
若TMPQHeader內(nèi)部的數(shù)據(jù)是正確的,則開(kāi)始讀取文件,為了推斷文件是否正確,MPQ會(huì)有很多檢測(cè)。只是MPQ文件頭部默認(rèn)有四種格式。
每一種格式的長(zhǎng)度都不一樣。在確定頭部之后。會(huì)調(diào)用memset。將超出的部分置0,確保新生成的MPQHeader數(shù)據(jù)正確。
? ? ? ? ? ? ?2》通過(guò)新獲得的頭部數(shù)據(jù)結(jié)構(gòu)。讀取HASH表。Hash表通過(guò)TMPQHeader的dwHashTablePos屬性。找到其在文件里的位置,并讀取出。接著將文件里的數(shù)據(jù),讀取到內(nèi)存里面,完畢Hash表數(shù)據(jù)的讀取。
? ? ? ? ? ? ?3》建立文件數(shù)據(jù)表。通過(guò)讀取Hash表,初始化文件數(shù)據(jù)表FileTable。有了FileTable就能夠得到FileEntry,有了文件入口。就能讀取數(shù)據(jù)了。
? ? ? ? ? ? ?4》加載listfile和attributes文件。
眼下我沒(méi)用到這兩個(gè)選項(xiàng)。
? ? ? ? ? ? ?5》返回MPQ文件句柄。
? ? ? ??五、讀取資源文件。
? ? ? ? 有兩種讀取方式。
第一種是取出當(dāng)中某一個(gè)特定的文件,另外一種遍歷MPQ文件內(nèi)部全部的數(shù)據(jù)文件。
? ? ? ??第一種取出一個(gè)文件是比較簡(jiǎn)單的。
? ? ? ??詳細(xì)流程是:
? ? ? ? ? ? ?1》調(diào)用SFileOpenFileEx接口(Ex即外部文件的意思)。傳入文件名稱(chēng)給該接口,接口內(nèi)部會(huì)將文件名稱(chēng)轉(zhuǎn)化為hash數(shù)據(jù)。通過(guò)hash數(shù)據(jù)的dwBlockIndex屬性,算出FileEntry數(shù)據(jù),有了FileEntry,就能夠獲得要讀取的文件的數(shù)據(jù)的位置。而且獲取該數(shù)據(jù)文件的句柄。也能夠先調(diào)用SFileHasFile推斷是否有該文件。
? ? ? ? ? ? ?2》調(diào)用SFileGetFileSize獲取文件大小,申請(qǐng)內(nèi)存空間。調(diào)用SFileGetFileInfo獲取文件信息,不是數(shù)據(jù)。
? ? ? ? ? ? ?3》調(diào)用SFileReadFile獲取MPQ文件里特定資源文件的數(shù)據(jù)。
? ? ? ? ? ? ?4》調(diào)用SFileCloseFile釋放申請(qǐng)的內(nèi)存。
? ? ? ??另外一種略微復(fù)雜一些。
? ? ? ??詳細(xì)流程是:
? ? ? ? ? ? ?1》調(diào)用SFileFindFirstFile,匹配符為*,則會(huì)所有匹配。返回一個(gè)hFind值。
? ? ? ? ? ? ?2》運(yùn)行讀取一個(gè)文件的流程,上面已經(jīng)說(shuō)明,此處不再贅述。
? ? ? ? ? ? ?3》調(diào)用SFileFindNextFile獲取下一個(gè)文件的入口。
? ? ? ? ? ? ?4》循環(huán)運(yùn)行1==>3流程。直至沒(méi)有文件能夠讀取。
版權(quán)聲明:本文博客原創(chuàng)文章,博客,未經(jīng)同意,不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的MPQ Storm库 源代码分析 一个的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 5G NR空口物理层主要参数解读
- 下一篇: win10开机优化