DIABLO 板


LINE

之前要用 mpq2k 看某些 mpq 檔裡的東西, 突然發現 mpq2k 幾乎看不到什麼檔 案了, 尤其在 d2exp 1.10 以後, patch_d2.mpq 讓它找只能找到一個檔案, 因 為 mpq2k 的作者已經不再做任何維護的動作了, 只能自己手動來, 所以才有想 了解這個檔案格式的動力, 想看看能否突破 mpq2k 現有的限制. (之前有想看, 但就是懶 (-,-))))). 目前針對 mpq2k 的一些嚐試已經告一段落, 想做的事也差不多了, 繼續研究的 動力也沒了, 所以把目前的東西整理一下, 希望對想研究的人有一些幫助. * 這篇文章最有用的應該是參考資料, 其他都是廢話 XD 這篇文章可自由轉載, 但請保留出處(ptt.cc diablo 板)及作者(edwar). 參考資料 -------- Inside MoPaQ http://www.campaigncreations.org/starcraft/inside_mopaq/ 這是 mpq2k 作者所寫的網頁, 但是似乎沒有再更新了. (聽說作者不希望有人煩他 mpq2k 相關的事) 從 Inside MoPaQ 提供的 Stormless MPQ Editor 連結拿到的 MpqView 原始碼也很有用. 網頁: http://www.angelfire.com/sc/mpq/ MPQ 檔的一些特性 ---------------- * Blizzard 所使用的一種檔案格式, 存放一些遊戲的資訊檔, 如聲音、動畫 * 用於將多個檔案放進一個檔案裡 * 給一個檔名, 可以檢查一個 MPQ 檔有沒有那個檔, 但是無演算法可從一個 MPQ 檔逆推求得該 MPQ 檔含有那些檔案. - MPQ 使用三個不同參數得到的 32bit hash 來取代檔名這項資訊, 如果 有碰撞產生就認了, 所以無法逆推. * 檔名不分大小寫 檔頭 ---- offset |length | content (hex) | | ---------|-------|------------------- 00000000 | 4 | 4D 50 51 1A ('MPQ'?) 00000004 | 4 | 20 00 00 00 00000008 | 4 | mpq file size ? 0000000C | 4 | unknown (00 00 03 00) 00000010 | 4 | htbl offset: hash table 位置 00000014 | 4 | btbl offset: block table 位置 00000018 | 4 | htbl_len: hash table 大小 / 能容納的檔案數 0000001C | 4 | btbl_len: 現有檔案數 依 MpqView source code 來看, 4D 50 51 1A 字串只需位於 4-word aligned 開始的地方, 開檔案後要先找到這個字串, 之後的其他資訊都是相對於此字串 的相對位置. hash table (htbl) ----------------- hash table 每筆資料含有四個 32bit 的值 offset |length | content (hex) | | ---------|-------|------------------- 00000000 | 4 | hash2 00000004 | 4 | hash3 00000008 | 4 | unknown (好像都是 00 00 00 00 或 FF FF FF FF) 0000000C | 4 | 位於 block table 的那一筆 entry, 從 0 開始算 共有 htbl_len 筆記錄, 如果該筆記錄未被使用, 四個欄位似乎都會是 FF FF FF FF. 能容納多少筆記錄在該 MPQ 檔建立之初就已決定, 只要開始加 檔案就無法變動了, 這也是該檔能夠容納的檔案數. 若是實際上用 hex editor 去看 MPQ 檔, hash table 應該是一堆看起來雜亂 無章的資料, 需要經過 decrypt 的過程轉成正常的樣子. 參考 encrypt / decrypt 部份的說明. 由檔名得到 block entry ====================== 由檔名可以求得三個 32bit hash (參考 hash function, 該處提到的 $scrc1 / $scrc2 / $scrc3 即為所求, 分別當做這裡的 hash1, hash2, hash3), 再 由這三個值找出位於 hash table 何處: 1. hash1 除以 htbl_len 的餘數: 此數告訴你要從 hash table 的那一筆記 錄開始找. (從零開始算) 2. 檢查這筆記錄是否已被使用, 可能是看 offset 0008h 的數值: (a) 00000000h 表示有使用, 進行步驟 3. (b) FFFFFFFFh 則是未被使用, 跳至步驟 4. 3. 檢查 hash2 和 hash3 是否均符合: (a) 是, 均符合: 找到檔案了, offset 000Ch 為該檔的 block entry. (b) 否: 還未找到, 跳到步驟 4 4. 看看是否已經整個 hash table 的每筆記錄都找過卻找不到, 若是, 該檔 不存在 -> 跳出. 5. 繼續下一筆記錄, 跳至步驟 2. 如果是在 hash table 最後一筆, 下一筆 記錄則是第 0 筆記錄. 由於 mpq 處理 hash collision(碰撞)的方式是繼續往後找, 直至有空的位置 可以存放就儲存. 如果碰撞後又有做 delete(刪除)的動作, 可能會產生空洞. 所以步驟 2-(b) 發現該位置未使用也只是跳過去而已. 接下來 ====== * 如果只是要知道該檔名存在與否, 那以上的[由檔名得到 block entry]應 該就可以了. * 還要再得到那個檔案的內容的話, 由此 block entry, 再到 block table 裡找相關的檔案儲存資訊. block table (btbl) ------------------ block table 每筆資料含有四個 32bit 的值 offset |length | content (hex) | | ---------|-------|------------------- 00000000 | 4 | 存放的位置, 相對於檔頭開始(4D 50 51 1A) 00000004 | 4 | 壓縮後的檔案大小; 若未壓縮, 則同未壓縮的檔案大小 00000008 | 4 | 未壓縮的檔案大小 0000000C | 4 | block 屬性, 如有無壓縮、有無編碼等等 共有 btbl_len 筆記錄, 從 0 開始算. 若用 hex editor 去看 MPQ 檔, block table 應該像 hash table 一樣, 看起 來像一堆雜亂無章的資料, 需要經過 decrypt 的過程轉成正常的樣子. 參考 encrypt / decrypt 部份的說明. 屬性 ==== 目前找到的 block 屬性值有: ------ 00000000h 80000000h - 未壓縮, 未編碼 80000100h - from Diablo I MPQ, DCL 壓縮 80000200h - Starcraft MPQ (or Diablo II), 要另外讀壓縮方式 80010200h 80030000h 80030200h ====== bit value | attribute (hex) | ----------|---------------------------------------- 00000000 | 不知, 也許無任何特性 00000100 | PACK, Diablo I MPQ, always compressed with DCL 00000200 | PACK, Starcraft MPQ (or Diablo II), 某處會記錄壓縮方式 00010000 | coded (是指 encrypted?) 00020000 | coded (是指 encrypted?) 80000000 | 不知 (各屬性值意義在 MpqView 的原始檔有更詳盡的解釋) hash function ------------- 預備知識: hash function 概念 MPQ 檔使用一個不錯的單向 hash function 將檔名轉成三個 32bit hash, 此 hash function 也可以用不同的參數就改變輸出的結果, 檔案轉換後的三個 hash 就是三個相異參數得到的. 底下使用 perl 程式碼呈現如何產生 hash: (C 的程式碼請參考 MpqView 原始檔) --- get hash ------------- my @massive_base; sub BuildBaseMassive { my $s1; my ($i, $j); my $rem; $rem = 0x100001; for $i (0 .. 0x100-1) { for $j (0 .. 4) { $rem = ($rem*125+3) % 0x002AAAAB; $s1 = ($rem & 0xFFFF) << 0x10; $rem = ($rem*125+3) % 0x002AAAAB; $s1 |= ($rem & 0xFFFF); $massive_base[$i+0x100*$j] = $s1; } } } sub Crc ($$$) { my $string = shift; my $refMassive_base = shift; my $massive_base_offset = shift; my $byte; my $crc = 0x7fed7fed; my $s1 = 0xEEEEEEEE; foreach (split(//, $string)) { $byte = ord; last if $byte == 0; if ($byte > 0x60 && $byte < 0x7B) { $byte -= 0x20; } $crc = $$refMassive_base[$massive_base_offset+$byte]^($crc+$s1); $crc &= 0xFFFFFFFF; $s1 += $crc+($s1<<5)+$byte+3; $s1 &= 0xFFFFFFFF; } return $crc; } &BuildBaseMassive; my $filename = 'This\is\a\test\file'; my ($scrc1, $scrc2, $scrc3); $scrc1 = &Crc($filename, \@massive_base, 0); $scrc2 = &Crc($filename, \@massive_base, 0x100); $scrc3 = &Crc($filename, \@massive_base, 0x200); === get hash ============= $scrc1, $scrc2, $scrc3 就是由檔名轉換得到的三個 32bit hash, 分別由調整參數 為 0, 0x100, 0x200 得到. encryption/decryption: hash & block table ----------------------------------------- 如果直接用 hex editors 去看 hash table 和 block table, 看到的資料應該是 很雜亂的, 這兩個 table 在寫進來 MPQ 檔之前, 已經被 Encrypt 過了, 所以想 要看到正確的資料, 就要先 Decrypt. 以下是 Encrypt / Decrypt 的 perl code: (此處 sub Decode 是 Decrypt 程式; sub Encode 是 Encrypt 程式) (C 程式碼 Decrypt 部分同樣參考 MpqView 原始碼) --- Encrypt / Decrypt ----------- sub Decode ($$$$) { my $refDataIn = shift; my $refMassive_base = shift; my $crc = shift; my $lenght = shift; my ($i, $dec); my $s1 = 0xEEEEEEEE; for $i (0 .. $lenght-1) { $s1 += $$refMassive_base[0x400+($crc & 0xFF)]; $s1 &= 0xFFFFFFFF; $dec = $$refDataIn[$i]^($s1+$crc); $dec &= 0xFFFFFFFF; $s1 += $dec+($s1<<5)+3; $s1 &= 0xFFFFFFFF; $$refDataIn[$i] = $dec; $crc = ($crc>>0x0b) | ((0x11111111+(($crc^0x7FF)<<0x15)) & 0xFFFFFFFF); } } sub Encode ($$$$) { my $refDataIn = shift; my $refMassive_base = shift; my $crc = shift; my $lenght = shift; my ($i, $dec); my $s1 = 0xEEEEEEEE; for $i (0 .. $lenght-1) { $s1 += $$refMassive_base[0x400+($crc & 0xFF)]; $s1 &= 0xFFFFFFFF; $dec = $$refDataIn[$i]; $$refDataIn[$i] = ($dec^($s1+$crc)) & 0xFFFFFFFF; $s1 += $dec+($s1<<5)+3; $s1 &= 0xFFFFFFFF; $crc = ($crc>>0x0b) | ((0x11111111+(($crc^0x7FF)<<0x15)) & 0xFFFFFFFF); } } my $name_htable = "(hash table)"; my $name_btable = "(block table)"; seek(MPQ, $offset_mpq+$offset_htbl, 0); read(MPQ, $tmp, 4*4*$lenght_htbl); @hash_table = unpack("L*", $tmp); seek(MPQ, $offset_mpq+$offset_btbl, 0); read(MPQ, $tmp, 4*4*$lenght_btbl); @block_table = unpack("L*", $tmp); $tmp = &Crc($name_htable, \@massive_base, 0x300); &Decode(\@hash_table, \@massive_base, $tmp, 4*$lenght_htbl); $tmp = &Crc($name_btable, \@massive_base, 0x300); &Decode(\@block_table, \@massive_base, $tmp, 4*$lenght_btbl); # my @tblock_table = @block_table; # $tmp = &Crc($name_btable, \@massive_base, 0x300); # &Encode(\@tblock_table, \@massive_base, $tmp, 4*$lenght_btbl); === Encrypt / Decrypt =========== @massive_base 和 &Crc(...) 跟 hash function 用的是一樣的. -- 結束了 ^^ --



※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 220.138.156.138
1F:推 Forcast:◆ 這一篇文章值 230 銀 02/07 23:31
2F:推 jimmylee7775:推 02/07 23:31
3F:推 waterfantasy:我的眼睛花了 02/07 23:32
4F:推 red0210:眼睛都花了 02/07 23:33
5F:推 MeauD2:這篇 很難>"< 應屬於大內高手流 02/07 23:33
6F:推 micado:我只看到一推程式設定碼 02/07 23:34
7F:推 ericinttu:雖然我看不懂,但真是高手才辦得到 Orz 02/07 23:32
8F:推 GGL:@@" 02/07 23:34
9F:推 pocky0511:第一行就看不懂 第二頁就按END 02/07 23:35
10F:推 micado:不過我還是想說"帥ㄚ~老皮!" 02/07 23:36
11F:→ edwar:大量中英夾雜, 很難排版 >"< 我自己留的每段分開, 較不傷眼 02/07 23:35
12F:推 omarandy:完全...不懂ˊˋ 02/07 23:43
13F:推 ScorAlan:大師讓小弟拜一下Orz 02/08 00:00
14F:推 Joybo:我走到了Programming版了嗎 02/08 00:17
15F:推 Joybo:沒學過perl,很好奇他怎知道Encode / Decode的方式,組語? 02/08 00:21
16F:→ edwar:perl 的部分是留著我自己以後要看的 XD 02/08 00:24
17F:→ edwar:Encode/Decode 也許是 trace 出來的? 02/08 00:24
18F:→ edwar:樓上有人看不懂沒關係, 只要有人能看懂再寫好用的工具程式 02/08 00:26
19F:→ edwar:就好了 XD 02/08 00:27







like.gif 您可能會有興趣的文章
icon.png[問題/行為] 貓晚上進房間會不會有憋尿問題
icon.pngRe: [閒聊] 選了錯誤的女孩成為魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一張
icon.png[心得] EMS高領長版毛衣.墨小樓MC1002
icon.png[分享] 丹龍隔熱紙GE55+33+22
icon.png[問題] 清洗洗衣機
icon.png[尋物] 窗台下的空間
icon.png[閒聊] 双極の女神1 木魔爵
icon.png[售車] 新竹 1997 march 1297cc 白色 四門
icon.png[討論] 能從照片感受到攝影者心情嗎
icon.png[狂賀] 賀賀賀賀 賀!島村卯月!總選舉NO.1
icon.png[難過] 羨慕白皮膚的女生
icon.png閱讀文章
icon.png[黑特]
icon.png[問題] SBK S1安裝於安全帽位置
icon.png[分享] 舊woo100絕版開箱!!
icon.pngRe: [無言] 關於小包衛生紙
icon.png[開箱] E5-2683V3 RX480Strix 快睿C1 簡單測試
icon.png[心得] 蒼の海賊龍 地獄 執行者16PT
icon.png[售車] 1999年Virage iO 1.8EXi
icon.png[心得] 挑戰33 LV10 獅子座pt solo
icon.png[閒聊] 手把手教你不被桶之新手主購教學
icon.png[分享] Civic Type R 量產版官方照無預警流出
icon.png[售車] Golf 4 2.0 銀色 自排
icon.png[出售] Graco提籃汽座(有底座)2000元誠可議
icon.png[問題] 請問補牙材質掉了還能再補嗎?(台中半年內
icon.png[問題] 44th 單曲 生寫竟然都給重複的啊啊!
icon.png[心得] 華南紅卡/icash 核卡
icon.png[問題] 拔牙矯正這樣正常嗎
icon.png[贈送] 老莫高業 初業 102年版
icon.png[情報] 三大行動支付 本季掀戰火
icon.png[寶寶] 博客來Amos水蠟筆5/1特價五折
icon.pngRe: [心得] 新鮮人一些面試分享
icon.png[心得] 蒼の海賊龍 地獄 麒麟25PT
icon.pngRe: [閒聊] (君の名は。雷慎入) 君名二創漫畫翻譯
icon.pngRe: [閒聊] OGN中場影片:失蹤人口局 (英文字幕)
icon.png[問題] 台灣大哥大4G訊號差
icon.png[出售] [全國]全新千尋侘草LED燈, 水草

請輸入看板名稱,例如:WOW站內搜尋

TOP