MPQ hash
原文:http://blog.csdn.net/olncy/article/details/2466418
開始嘗試翻譯一些英文文章,最近正好對(duì)mpq產(chǎn)生興趣,看到一片文章叫做 inside MPQ,于是翻譯一下,就當(dāng)鍛煉自己吧。這篇文章非常的不厚道,在關(guān)鍵地方戛然而止,而且沒有更新的跡象。讓人郁悶無比。但是還是比國內(nèi)一些研究MPQ的 少的可憐的文章要好些。看了這些文章,無比惋惜國內(nèi)技術(shù)的滯后和黑客技術(shù),逆向工程技術(shù)的貧乏。我們總是拿來主義,做應(yīng)用。自己的原創(chuàng)真的太少了。
LEGAL COPYRIGHTS
The MPQ Format The copyrights to the MPQ format are held by Havas Interactive, Blizzard Entertainment's parent company, all rights reserved This Article The copyrights to this document and content are held by Justin Olbrantz(Quantam), all rights reserved. You may freely distribute this document provided that you do not derive profit from the distribution, and that the document remains complete and unchanged. You may quote this document ONLY with my explicit permission. Contact me to obtain permission to quote.Also, although I would appreciate recognition for your use of this information, I will not be held legally responsible for anything you may do with it. Anyway that you misuse this information is your problem, and I will not be responsible for it.
?
這個(gè)LEGAL COPYRIGHTS我就不做翻譯了。a
對(duì)于我這篇翻譯的文章,申明如下:
可以轉(zhuǎn)載,但要注明作者是王宇,并且保證整個(gè)內(nèi)容包括上面幾段內(nèi)容的完整性。并且我對(duì)一切后果不承擔(dān)責(zé)任。
MPQ 技術(shù)內(nèi)幕
作者 Justin Olbrantz(Quantam)
譯者 王宇
第1章
MPQ簡介
MPQ 或者稱作 MoPaQ 是Mike O'Brien創(chuàng)建的擁有私人版權(quán)的檔案文件格式。Mike O'Brien是暴雪公司的多人游戲引擎方面的天才。他在1996年,為了暗黑破壞神而開發(fā)出這種檔案文件格式。并且自戀的以自己的名字“Mike O'brien PaCK”給這種格式命名MPQ。但是文檔的版權(quán)卻由Havas Interactive(暴雪的父公司)所有。所以,即使現(xiàn)在Mike離開了暴雪,暴雪仍然擁有MPQ格式的使用權(quán)。MPQ格式在暗黑破壞神,星際爭霸, 魔獸爭霸2,3,暗黑破壞神2,BNE(譯者備注:我不知道這是什么游戲),Lords of Magic(由sierra公司開發(fā),這個(gè)公司同樣隸屬于Havas)等游戲中都有應(yīng)用。
一個(gè)檔案文件是指一個(gè)包含其他文件在內(nèi)的文件,并且它經(jīng)常是以壓縮的形式存在的。Havas用MPQ包含了游戲中幾乎所有的東西。比如安裝文件,游 戲數(shù)據(jù)等等。其中游戲數(shù)據(jù)的MPQ封裝是非常重要的。這些MPQ當(dāng)中包括了圖像,聲音,等級(jí),字符串,故事線信息等等。Obviously, the potential for customization is astounding. (譯者備注:這句不好翻)但是,為了用MPQ,你必須首先理解它。
在MPQ之前
在MPQ發(fā)明之前很長一段時(shí)間,有一種個(gè)是叫做WAR(Warcraft ARchive)格式。這種格式是在魔獸爭霸2甚至1中存儲(chǔ)數(shù)據(jù)的格式。這種雛鳥格式非常的簡單,也沒有優(yōu)化,總是看起來就是一個(gè)實(shí)實(shí)在在的新手文件格 式。檔案中的文件是按照坐標(biāo)來尋址的,唯一的一點(diǎn)點(diǎn)優(yōu)化就是用了一些壓縮技術(shù)。但是,雖然它簡單,它完成了它需要完成的任務(wù)。它提供了一種快速但是骯臟的 方法壓縮的存儲(chǔ)了很多文件。但是不久,缺點(diǎn)就開始暴露出來了。按照坐標(biāo)來尋址意味著必須保存一個(gè)很長的入口表來供程序員使用檔案中某些文件的時(shí)候調(diào)用。當(dāng) 這個(gè)表越來越長的時(shí)候,工作就變得越來越冗長。而且這種簡單的格式意味著黑客可以很容易的在15分鐘內(nèi)破解除這種格式,然后可以隨心所欲的在這些文件上做 一些事。這些問題一開始看起來可能還不太糟,但是當(dāng)暗黑破壞神所要求的persistent characters(譯者備注:這個(gè)我不懂),站網(wǎng)的普及讓這些問題變得無法接受了。
為什么是MPQ
正如前面所說,MPQ格式是為了彌補(bǔ)一些WAR非常嚴(yán)重的缺陷設(shè)計(jì)的。但是它仍然添加了很多新的特性。總的說來,MPQ的特點(diǎn)如下:
安全性:暴雪最不愿意的就是人們象破解魔獸爭霸2那樣破解它以后的游戲。而且暴雪很可能已經(jīng)覺得要把MPQ格式應(yīng)用到星際爭霸上面。不管怎么樣,安全性是最最重要的。這點(diǎn)可以從那些暴雪維護(hù)這種格式的折磨人的努力中看出來。
效率:MPQ需要完成一系列工作,從最簡單的預(yù)讀數(shù)據(jù)到復(fù)雜的實(shí)時(shí)流。對(duì)于預(yù)讀數(shù)據(jù)倒還沒什么,但是對(duì)于實(shí)時(shí)流,因?yàn)閿?shù)據(jù)必須以很快的速度一邊玩游戲一邊解壓縮,所以,速度是強(qiáng)制的。
多語言:在最一開始,暴雪就計(jì)劃把它的產(chǎn)品推向世界市場,所以,它希望它的游戲的翻譯能盡量容易。于是它用了一種革新的方法,就是把多語言性的本領(lǐng)放在MPQ格式里面。
可擴(kuò)展性:很顯然的,把一個(gè)游戲所有的數(shù)據(jù)放入一個(gè)檔案是很傻的。不僅沒有效率,速度很慢,而且售后升級(jí)會(huì)變得非常麻煩。暴雪當(dāng)然知道這點(diǎn),因此,為了使售后升級(jí)簡單,有效,優(yōu)雅,它在MPQ格式的設(shè)計(jì)上就考慮到了這個(gè)問題。
?
風(fēng)暴 Storm
很多程序員為了防止冗余代碼,通常會(huì)把一些常用的代碼封裝到共享庫里面。這些共享庫可以提供程序員常用的函數(shù)。這樣可以減少冗余和程序體積。所以, 暴雪用一個(gè)共享庫叫做Storm(在微軟平臺(tái)上叫做Storm.dll, 在蘋果平臺(tái)上叫做Storm.bin)這個(gè)庫被現(xiàn)在的暴雪游戲用來儲(chǔ)存重要函數(shù),比如MPQ的讀入,戰(zhàn)網(wǎng),甚至是圖像路由。當(dāng)暴雪發(fā)布一個(gè)新游戲的時(shí)候, 它會(huì)在storm里面加入函數(shù),但是不會(huì)修改舊的函數(shù)。這意味著一個(gè)老的游戲可以用新的Storm庫而不會(huì)出問題。像任何共享庫一樣,Storm的函數(shù)可 以被任何人使用,這樣就使它的安全性變得很差。這就是Storm只包含MPQ的讀取函數(shù)而MPQ的寫入函數(shù)卻是暴雪的私人財(cái)產(chǎn),它不會(huì)允許任何人去使用的 原因了。
星際爭霸的任務(wù)編輯器
大家都知道星際爭霸的任務(wù)編輯器可以編輯任務(wù)。但是星際爭霸的任務(wù)就是MPQ!這意味星際的任務(wù)編輯器可以創(chuàng)建MPQ,所以其中有MPQ的創(chuàng)建函數(shù)。不過星際爭霸的任務(wù)編輯器不是一個(gè)共享庫,所以要用一系列詭異的黑客技術(shù)去破解它。于是有了MPQ API 庫。
?
第2章
基礎(chǔ)
大多數(shù)計(jì)算機(jī)歷史上的進(jìn)步是因?yàn)橛刑厥獾膯栴}需要解決。在這章,我們將了解一下關(guān)于MPQ格式的問題和它們的解決方案。
哈希
問題:你有一個(gè)很大的字符竄數(shù)組。你有另一個(gè)字符竄str需要判斷是否存在于這個(gè)數(shù)組里面。可能你就會(huì)按照順序一個(gè)一個(gè)的比較數(shù)組里面的內(nèi)容。但是 在實(shí)際應(yīng)用中,你會(huì)發(fā)現(xiàn)這種方法遠(yuǎn)慢于實(shí)際需求。必須對(duì)此做一些優(yōu)化。但是如何你才能知道這個(gè)字符竄是否存在卻不用把它同數(shù)組中的所有其它字符竄比較呢?
解決方案:哈希。哈希是用來代替大一些的數(shù)據(jù)類型(比如字符竄)的小一些的數(shù)據(jù)類型(比如數(shù)字)。在我們這個(gè)問題里,你可以把字符竄數(shù)組儲(chǔ)存為哈希 數(shù)組。然后你就可以比較另外的那個(gè)字符竄str的哈希同儲(chǔ)存的哈希數(shù)組中所有的哈希。如果哈希數(shù)組中的一個(gè)哈希同str的哈希匹配,那么這個(gè)哈希所代表的 字符竄就可以同str進(jìn)行比較來判斷到底是否相同。這種方法叫作下標(biāo)(indexing),根據(jù)數(shù)組大小和字符竄長度的不同,它可以把速度提升將近100 倍。
unsigned?long?HashString(char?*lpszString){?
????unsigned?long?ulHash?=?0xf1e2d3c4;
????while?(*lpszString?!=?0)
????{?
????????ulHash?<<=?1;
????????ulHash?+=?*lpszString++;?
????}
????return?ulHash;?
}?
?以上的代碼展示了一個(gè)非常簡單的哈希算法。函數(shù)計(jì)算了字符竄中的字符個(gè)數(shù),在每個(gè)字符加入之前把哈希值左移1位。應(yīng)用這個(gè)算法,字符竄"arr/ units.dat"將會(huì)被哈希成0x5A858026,而"unit/neutral/acritter.grp" 將會(huì)被哈希成0x694CD020。不可否認(rèn),現(xiàn)在這個(gè)算法非常的簡單,而且沒有什么用處。因?yàn)樗a(chǎn)生了一個(gè)相對(duì)可以預(yù)見的結(jié)果。而且會(huì)有很多沖突。 chogntu 是指多個(gè)字符竄哈希到同樣一個(gè)數(shù)值。 而另一方面,MPQ格式卻用了一種非常復(fù)雜的哈希算法(如下所示)去生成一個(gè)完全不可預(yù)料的哈希值。事實(shí)上,這種哈希算法叫做單行道哈希(one-way hash)。單行道哈希是指根據(jù)哈希值不能推回去找到源字符竄的哈希算法。應(yīng)用這種MPQ算法,文件名"arr/units.dat" 將被哈希為0xF4E6C69D,而"unit/neutral/acritter.grp"將被哈希為0xA26067F3.
unsigned?long?HashString(char?*lpszFileName,?unsigned?long?dwHashType){?
????unsigned?char?*key?=?(unsigned?char?*)lpszFileName;
????unsigned?long?seed1?=?0x7FED7FED,?seed2?=?0xEEEEEEEE;
????int?ch;
????while(*key?!=?0)
????{?
????????ch?=?toupper(*key++);
????????seed1?=?cryptTable[(dwHashType?<<?8)?+?ch]?^?(seed1?+?seed2);
????????seed2?=?ch?+?seed1?+?seed2?+?(seed2?<<?5)?+?3;?
????}
????return?seed1;?
}?
?
?哈希表
問題:你試圖使用之前例子里面的下標(biāo)法,但是你的程序需要非常嚴(yán)格的速度限制。這時(shí)候你就會(huì)發(fā)現(xiàn)下標(biāo)法不夠快了。這時(shí)候你讓它變得更快的方法只能是 不讓它檢查數(shù)組中所有的哈希。或者,更好的是只讓字符串同數(shù)組中的某個(gè)元素比較1次就能判斷出這個(gè)字符竄是否存在于這個(gè)數(shù)組。聽起來太好了以至于不可能對(duì) 不對(duì)?
解決方案:哈希表。哈希表是一種下標(biāo)為字符串哈希值得數(shù)組。我的意思是說,我們?yōu)檫@個(gè)哈希表構(gòu)建一個(gè)不同于字符串?dāng)?shù)組的定長數(shù)組(我們把它的元素個(gè) 數(shù)定位1024,2的偶數(shù)次冪)。這時(shí)候,當(dāng)你想要知道一個(gè)字符串是否在哈希表中時(shí),你得首先計(jì)算這個(gè)字符串如果在哈希表中,那么它的位置是多少。首先我 們計(jì)算這個(gè)字符串的哈希,然后用哈希模取之前的表長(1024)就得到了位置值。因此,如果你用之前的簡單哈希算法,"arr/units.dat"將被 哈希為0x5A858026,得到它得位置值為 0x26 (0x5A858026 模取 0x400 商為 0x16A160余數(shù)為0x26)。0x26這個(gè)位置的字符串(如果有的話)將被讀出來與目標(biāo)字符串比較。如果0x26這個(gè)字符串與目標(biāo)字符串不匹配或者 0x26的這個(gè)字符串不存在,則這個(gè)目標(biāo)字符串不存在于這個(gè)數(shù)組中。以下的代碼說明了這點(diǎn):
int?GetHashTablePos(char?*lpszString,?SOMESTRUCTURE?*lpTable,?int?nTableSize){?
int?nHash?=?HashString(lpszString),?nHashPos?=?nHash?%?nTableSize;
if?(lpTable[nHashPos].bExists?&&?!strcmp(lpTable[nHashPos].pString,?lpszString))?
return?nHashPos;?
else?
return?-1;?//Error?value?
}?
可是現(xiàn)在,這個(gè)算法有一個(gè)巨大的缺陷。你認(rèn)為當(dāng)沖突(2個(gè)字符竄哈希到同樣一個(gè)值)發(fā)生的時(shí)候會(huì)怎么樣?顯然它們不能占用哈希表中的同一個(gè)元素。一 般,這種缺陷通過使哈希表中的每一個(gè)元素成為一個(gè)鏈表來實(shí)現(xiàn)。每個(gè)鏈標(biāo)中將存放哈希值相同的字符竄。MPQ使用文件名哈希表來跟蹤內(nèi)部的所有文件。但是這 個(gè)表的格式與正常的哈希表有一些不同。首先,它沒有使用哈希作為下標(biāo),把實(shí)際的文件名存儲(chǔ)在表中用于驗(yàn)證,實(shí)際上它根本就沒有存儲(chǔ)文件名。而是使用了3種 不同的哈希:一個(gè)用于哈希表的下標(biāo),兩個(gè)用于驗(yàn)證。這兩個(gè)驗(yàn)證哈希替代了實(shí)際文件名。當(dāng)然了,這樣仍然會(huì)出現(xiàn)2個(gè)不同的文件名哈希到3個(gè)同樣的哈希。但是 這種情況發(fā)生的概率平均是1:18889465931478580854784,這個(gè)概率對(duì)于任何人來說應(yīng)該都是足夠小的咯。MPQ哈希表不同用通常的鏈 表沖突解決法,當(dāng)沖突發(fā)生時(shí),元素將被下移到下一個(gè)空著的位置。請(qǐng)看下面的代碼,基本就是MPQ定位文件名的方法:
int?GetHashTablePos(char?*lpszString,?MPQHASHTABLE?*lpTable,?int?nTableSize){?
????const?int?HASH_OFFSET?=?0,?HASH_A?=?1,?HASH_B?=?2;
????int?nHash?=?HashString(lpszString,?HASH_OFFSET),?
??????? nHashA?=?HashString(lpszString,?HASH_A),?
??????? nHashB?=?HashString(lpszString,?HASH_B),?
??????? nHashStart?=?nHash?%?nTableSize,
??????? nHashPos?=?nHashStart;
????while?(lpTable[nHashPos].bExists)
????{?
????????if?(lpTable[nHashPos].nHashA?==?nHashA?&&?lpTable[nHashPos].nHashB?==?nHashB)?
????????????return?nHashPos;?
????????else?
????????????nHashPos?=?(nHashPos?+?1)?%?nTableSize;
????????if?(nHashPos?==?nHashStart)?
????????????break;?
????}
????return?-1;?//Error?value?
}?
雖然這段代碼可能看起來讓你費(fèi)解,但是它背后的理論卻并不復(fù)雜。它在讀取一個(gè)文件的時(shí)候基本遵循了以下的步驟:
1 計(jì)算3個(gè)哈希(1個(gè)下標(biāo)哈希和2個(gè)檢查哈希)并且把他們存入變量
2 移動(dòng)到下標(biāo)哈希所指的元素
3 這個(gè)元素存在嗎?如果不存在,停止搜索,返回“文件沒有找到”
4 元素的兩個(gè)檢查哈希是否我們搜索的文件的檢查哈希相匹配?如果相匹配,就返回當(dāng)前的元素。
5 移動(dòng)當(dāng)前下標(biāo)到下一個(gè),如果達(dá)到最后一個(gè)下標(biāo),則回到第1個(gè)
6 我們剛一動(dòng)到的元素的下標(biāo)哈希是否相同(我們是否搜索了整個(gè)表)如果是,停止搜索,返回“文件沒有找到”
7 回到第3步
如果你留心了,你會(huì)發(fā)現(xiàn),在我的解釋和例子中MPQ哈希表需要保存所有的文件名。但是,你有沒有想過當(dāng)所有的哈希表行全部都填滿的時(shí)候會(huì)發(fā)生什么? 答案可能會(huì)讓你非常驚訝:你將不能再添加任何文件。有人問我為什么一個(gè)MPQ會(huì)有文件數(shù)目限制,有沒有什么方法可以解決這種限制。你已經(jīng)直到第一個(gè)問題的 答案了,對(duì)于第2個(gè)問題,很遺憾,你不能解決這種文件數(shù)目限制。因?yàn)楣1聿荒茉俨挥绊懻麄€(gè)文件改變的情況下改變大小。這是因?yàn)楣1碇忻總€(gè)元素的哈希都 因?yàn)楣1泶笮〉淖兓l(fā)生改變,這樣我們就不能得到文件在新的哈希表中的位置,于是我們就不能得到文件名了。
壓縮
問題:你有一個(gè)很大的程序(比如50MB)你現(xiàn)在希望把它發(fā)不到Inter網(wǎng)上。但是50MB將會(huì)是非常大的下載,人們可能就不會(huì)愿意等上幾個(gè)小時(shí)去下載這么一個(gè)東西。
解決方案:壓縮。壓縮是指把一大堆數(shù)據(jù)用一種很小的格式表達(dá)出來。世界上有很多種壓縮算法,每一種都用不同的方法工作。而我們的MPQ使用的數(shù)據(jù)壓縮算法是PKWare的數(shù)據(jù)壓縮庫。而這個(gè)庫在這里解釋的話就太復(fù)雜了。所以,我在這里想解釋一種相對(duì)簡單的奪得壓縮算法。
此節(jié)因?yàn)樽髡叩哪芰υ?#xff0c;沒有完成。
?
加密
一個(gè)系統(tǒng)對(duì)于間諜之眼窺視的防護(hù)一直是永恒的話題。人們已經(jīng)努力傳送私人信息給別人了上百年。從古希臘信使步行傳送的手寫書信到2戰(zhàn)時(shí)納粹潛艇的無 線電,再到今天網(wǎng)絡(luò)信用卡交易。保證別人不能得到你的信息的能力是非常必要的。這種復(fù)雜的保護(hù)方法叫做加密。雖然我們不知道第一個(gè)加密算法是誰發(fā)明的,但 是我們知道世界上游多的數(shù)不過來的加密算法。任何事物,從簡單的數(shù)據(jù)編碼到解密算法都是被使用了一次又一次的。這篇文章,當(dāng)然沒有解釋,也不期望解釋一個(gè) 加密算法,但是理解加密是你接觸MPQ工作的必須。
我們首先來看一個(gè)發(fā)布在 Basic Lab Notes上的加密算法:
void?EncryptBlock(void?*lpvBlock,?int?nBlockLen,?char?*lpszPassword){?
????int?nPWLen?=?strlen(lpszPassword),?nCount?=?0;
????char?*lpsPassBuff?=?(char?*)_alloca(nPWLen);
????memcpy(lpsPassBuff,?lpszPassword,?nPWLen);
????for?(int?nChar?=?0;?nCount?<?nBlockLen;?nCount++)
????{?
????????char?cPW?=?lpsPassBuff[nCount];
????????lpvBlock[nChar]?^=?cPW;
????????lpsPassBuff[nCount]?=?cPW?+?13;
????????nCount?=?(nCount?+?1)?%?nPWLen;?
????}
????return;?
}?
正如展示的哈希代碼那樣,這段代碼也非常的簡單,當(dāng)然也就不能用在需要安全性的實(shí)際程序中。即便這段代碼看起來很神秘,它做的事情卻非常簡單。它將 整個(gè)的輸入塊加密。異或密碼的每一個(gè)字節(jié)。然后把所得加上13(之所以選擇13是因?yàn)?3是質(zhì)數(shù))。這樣就能夠使代碼更加難以確認(rèn)。在這種情況下,字符串 "encryption" (65 6E 63 72 79 70 74 69 6F 6E)在密碼"MPQ" (4D 50 51)下將會(huì)被加密成為(28 3E 32 28 24 2E 13 03 04 1A)現(xiàn)在,這段代碼是對(duì)稱的。對(duì)稱意味著加密的密鑰和解密的密鑰是相同的。實(shí)際上,因?yàn)楫惢蚴且粋€(gè)對(duì)稱的操作,所以同加密相同的算法可以被用來解密。注 意到大部分對(duì)稱加密算法并非完全對(duì)稱,所以需要加密和解密的函數(shù)不相同。好,現(xiàn)在事情開始變得麻煩了。如果你希望直接的使用MPQ格式,那么你必須知道它 的加密和解密算法。而我就來教你如何使用它.MPQ的加密算法是一些其他加密算法有趣的雜交。它創(chuàng)建一個(gè)加密表(也用在哈希函數(shù)里面),然后用一個(gè)文件的 加密鑰去從加密表中去除某些數(shù)字,再把這些數(shù)字同加秘?cái)?shù)據(jù)進(jìn)行異或。現(xiàn)在這種做事的方法是非常非常奇怪的,所以可能一些代碼看起來非常的復(fù)雜。以下的代碼 生成一個(gè)長度為0x500的加密表。
?
void?prepareCryptTable(){?
????unsigned?long?seed?=?0x00100001,?index1?=?0,?index2?=?0,?i;
????for(index1?=?0;?index1?<?0x100;?index1++)
????{?
????????for(index2?=?index1,?i?=?0;?i?<?5;?i++,?index2?+=?0x100)
????????{?
????????????unsigned?long?temp1,?temp2;
????????????seed?=?(seed?*?125?+?3)?%?0x2AAAAB;
????????????temp1?=?(seed?&?0xFFFF)?<<?0x10;
????????????seed?=?(seed?*?125?+?3)?%?0x2AAAAB;
????????????temp2?=?(seed?&?0xFFFF);
????????????cryptTable[index2]?=?(temp1?|?temp2);?
????????}?
????}?
}
你是否有點(diǎn)感覺到暴雪雇傭了一個(gè)超級(jí)沒有人品的微積分教授撰寫了這個(gè)代碼?至少我是這么感覺的。還好即使你不能看懂這段代碼也沒有什么大問題。如果 你希望能夠直接使用MPQ,那么你可能會(huì)需要這些函數(shù)。你沒有必要完全看明白他們。不管怎么樣,當(dāng)加密表初始化以后,我們就可以用下面的函數(shù)來解密MPQ 數(shù)據(jù)(不要指望我會(huì)向你解釋這個(gè)代碼,因?yàn)槲乙矝]有看懂):
void?DecryptBlock(void?*block,?long?length,?unsigned?long?key){?
????unsigned?long?seed?=?0xEEEEEEEE,?unsigned?long?ch;
????unsigned?long?*castBlock?=?(unsigned?long?*)block;
????//?Round?to?longs
????length?>>=?2;
????while(length--?>?0)
????{?
????????seed?+=?stormBuffer[0x400?+?(key?&?0xFF)];
????????ch?=?*castBlock?^?(key?+?seed);
????????key?=?((~key?<<?0x15)?+?0x11111111)?|?(key?>>?0x0B);
????????seed?=?ch?+?seed?+?(seed?<<?5)?+?3;
????????*castBlock++?=?ch;?
????}?
}
翻譯后記:
這只是我閑來無事翻譯著玩的東西,都沒有認(rèn)真的推敲翻譯的語句,甚至有一些語句我是沒有看懂的,或者明明知道這樣翻譯是不好的但還是寫上去了。甚至 我都沒有興趣自己從頭到尾把這篇文章再看1遍。之所以只翻譯道第2章是因?yàn)榈?,4章分別講述Storm和Starcraft Campaign Editor and the MPQ API Library是如何使用的,沒有什么翻譯的價(jià)值。而真正精彩的5,6兩章作者又沒有寫完。所以說作者實(shí)在不厚道。一下給出英文源出處,希望我的翻譯只是 拋磚引玉,能激發(fā)大家越讀英文原版的激情。很多時(shí)候翻譯的過程中損失的信息還是相當(dāng)嚴(yán)重的。
英文源出處:http://www.campaigncreations.org/starcraft/inside_mopaq
總結(jié)
- 上一篇: GPGPU Achitectures阅读
- 下一篇: 服务器状态502 503 504,服务器