標籤:
MPQ什嗎?
MPQ維基上說的非常明確。
簡而言之,它是暴雪公司用於遊戲資料打包的工具。星際爭霸,魔獸爭霸遊戲中都有使用。該工具內含遊戲資源加密和壓縮等功能。
git:https://github.com/stormlib/StormLib
MPQ檔案總共同擁有四種格式:
第一種:FLAT格式。線性儲存。
另外一種:Partial格式,分包的儲存。
第三種:MPQE格式,加密儲存。
第四種:BLOCK格式。模組儲存。
FLAT格式最簡單。只是該格式也支援對單個檔案的加密和壓縮。
本文主要分析FLAT格式,主要包括下面五點:
一、建立一個MPQ檔案。
二、加入資源檔。
三、封裝MPQ檔案。
四、讀取MPQ檔案。
五、讀取資源檔。
圖一、圖二、圖三為FLAT格式的內部格式。
圖一
一、建立一個MPQ檔案。
主要過程是。調用SFileCreateArchive介面建立MPQ檔案。建立成功之後,該介面會返回成功與否。同一時候將MPQ檔案的控制代碼賦值給傳進來的變數。
建立MPQ檔案具體流程是:
1》在記憶體中初始化一個加密塊StormBuffer;
2》嘗試開啟一個同名的MPQ檔案。假如已經存在的同名檔案不是MPQ檔案,MPQ工具就會嘗試用MPQ檔案的格式讀取該檔案。為了避免同名的錯誤。在StormLib的範例中,有先刪除同一檔案夾想的同名檔案,再建立MPQ檔案的邏輯。
3》建立一個空的MPQ檔案。
4》重設檔案大小。
5》寫入一個假的MPQ檔案頭部,即圖一的A指向的地區。
MPQ的頭部有四種不同的格式。分別擁有不同的長度,只是預設有一個最大長度是208位元組。寫這個假的MPQ檔案頭部,最基本的目的是佔位,保證第一個資料檔案的寫入位置是在正確的。
6》假設是大檔案,則建立大檔案的索引表HetTable。
7》建立HashTabl,用來索引檔案的。
以上七個過程。主要實現了兩個結果,1、建立了MPQ檔案,2、在記憶體中產生須要用到的MPQ資料。
二、加入資源檔。
主要過程是,調用SFileCreateFile介面。在MPQ檔案裡建立一個新的檔案控制代碼。通過SFileWriteFile介面寫入資料。
調用SFileCloseFile釋放記憶體資源。
具體流程:
1》檢查是不是內部檔案。MPQ主要有兩個內部檔案,一個是listfile。另外一個attributes。假設不是內部檔案,則向MPQ檔案裡加入新的檔案。
2》找到MPQ檔案裡能夠寫入的位置。假設是第一次寫入。則此時是圖一B指向的地區。
但此時還沒有寫入。
3》通過HashString方法,將資源檔名稱轉化為hash索引值。以新的hash索引值從當前的HashTable中取一個TMPQHash資料結構。
4》假如不是第一次建立新檔案。則此時,通過TMPQHash的dwBlockIndex值。在FileTable做dwBlockIndex位移。擷取新檔案的入口TFileEntry。假如是第一次建立新檔案。則會先到FileTable裡面取出一個空暇的位置,分配一個TFileEntry。預設是取FileTable位置做dwFileTableSize位移,而此時dwFileTableSize值是0,所以就是FileTable表中第一個位置。即圖一B指向的地區。
同一時候分配一個TMPQHash(hash索引的取值與檔案名稱有關),分配完之後。與FileEntry關聯。在FileEntry裡通過dwHashIndex索引到hash表中的位置,TMPQHash通過dwBlockIndex索引到FileEntry。
5》拿到了FileEntry以後,在SFileAddFile_Init函數中初始化FileEntry的其它值。
經過這些邏輯。就能夠拿到一個TMPQFile檔案指標,用來寫入資料。
6》通過SFileWriteFile介面寫入檔案資料,能夠將資源檔分批寫到MPQ檔案裡。也能夠將資源檔一次寫入MPQ檔案裡,兩種都不會有問題。
圖二
7》在將要寫檔案的前,會先寫入一個扇區,預設有8位元組,扇區與將要寫入的資料的加密有關聯。然後寫入
資料,資料有選項,能夠選擇是否加密,是否壓縮,是否選擇資料作為單元檔案儲存體。
寫入完成之後,會再次寫入當
前扇區,詳細為什麼要兩次寫入扇區,下一篇會繼續分析。圖二中。L指向的是真正的資源檔資料,K、M各自是扇
區。資源檔可選擇加密和壓縮。
圖三
三、封裝MPQ檔案。
封裝挺重要的,封裝時會將新產生的檔案相關的資料更新到MPQ檔案頭部。覆蓋原先的假頭部。
封裝的主要過程是。將與檔案相關的HET表和BET表,以及HASH表,檔案FileTable。按順序寫到最後一個資源檔的結尾位置。
封裝邏輯運行完之後。新的MPQ檔案內部結構三。
詳細流程是:
1》確定是否要寫入listfile、attributes等檔案,假設要,則會在MPQ檔案裡建立,並寫入。
2》調用SFileCloseArchive,找到能夠寫入HET、BET、HASH、FILETABLE表的位置,然後將這些表的資料寫入到新的MPQ檔案裡。寫入後MPQ檔案內部三。表的資料會寫在第N個資料檔案之後。最後,回到頭部更新MPQ檔案頭部。
3》釋放記憶體中的資源。
四、讀取MPQ檔案。
主要流程是。開啟MPQ檔案,先讀取頭部。在讀取正確之後,解析各個表的位置。擷取資料檔案位置。而且隨意一個環節出錯。都會退出。
詳細流程:
1》初始化一個StormBuff(與資料加密有關)。然後用208位元組的長度,從MPQ檔案頭部0位置開始讀取,一次讀取208位元組。
讀取完之後強制轉化為TMPQHeader資料結構。
若TMPQHeader內部的資料是正確的,則開始讀取檔案,為了推斷檔案是否正確,MPQ會有很多檢測。只是MPQ檔案頭部預設有四種格式。
每一種格式的長度都不一樣。在確定頭部之後。會調用memset。將超出的部分置0,確保新產生的MPQHeader資料正確。
2》通過新獲得的頭部資料結構。讀取HASH表。Hash表通過TMPQHeader的dwHashTablePos屬性。找到其在檔案裡的位置,並讀取出。接著將檔案裡的資料,讀取到記憶體裡面,完畢Hash表資料的讀取。
3》建立檔案資料表。通過讀取Hash表,初始設定檔案資料表FileTable。有了FileTable就能夠得到FileEntry,有了檔案入口。就能讀取資料了。
4》載入listfile和attributes檔案。
眼下我沒用到這兩個選項。
5》返回MPQ檔案控制代碼。
五、讀取資源檔。
有兩種讀取方式。
第一種是取出當中某一個特定的檔案,另外一種遍曆MPQ檔案內部全部的資料檔案。
第一種取出一個檔案是比較簡單的。
詳細流程是:
1》調用SFileOpenFileEx介面(Ex即外部檔案的意思)。傳入檔案名稱給該介面,介面內部會將檔案名稱轉化為hash資料。通過hash資料的dwBlockIndex屬性,算出FileEntry資料,有了FileEntry,就能夠獲得要讀取的檔案的資料的位置。而且擷取該資料檔案的控制代碼。也能夠先調用SFileHasFile推斷是否有該檔案。
2》調用SFileGetFileSize擷取檔案大小,申請記憶體空間。調用SFileGetFileInfo擷取檔案資訊,不是資料。
3》調用SFileReadFile擷取MPQ檔案裡特定資源檔的資料。
4》調用SFileCloseFile釋放申請的記憶體。
另外一種略微複雜一些。
詳細流程是:
1》調用SFileFindFirstFile,匹配符為*,則會所有匹配。返回一個hFind值。
2》運行讀取一個檔案的流程,上面已經說明,此處不再贅述。
3》調用SFileFindNextFile擷取下一個檔案的入口。
4》迴圈運行1==>3流程。直至沒有檔案能夠讀取。
著作權聲明:本文部落格原創文章,部落格,未經同意,不得轉載。
MPQ Storm庫 原始碼分析 一個