MPQ Storm库 源码分析 一
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库 源码分析 一的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决页面报错 500-内部服务器错误
- 下一篇: 一些俗语,不思八九,常想一二