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