Compression 压缩 问题:您有一个很大的程序(比如说, 50 megs ) ,您要分发在互联网上。但50 megs是一个非常大的下载量,而且别人未必有兴趣等待四个半小时去下载这个程序。 解决方法:压缩。 压缩是一门艺术。是在更小的内存中重新放置等量的数据。 有数以百计不同的压缩算法,使用不同的方式。 MPQ实际使用的算法是the Data Compression Library, licensed from PKWare (one of the leaders in applied compression),在此解释太过于复杂。相反,我会尝试解释一个更简单的压缩算法的例子。 本章节并不完全 ,因为作者没写完 Encryption 加密 这个世界上总是有喜欢剽窃的人存在,所以我们需要有一个保护资料安全的系统。 千百年来人们一直试图传递信息给他人。从手写的信件进行,信使徒步穿越古希腊,纳粹潜艇的无线电传输,在第二次世界大战,使用信用卡交易,到网络应用的今天,有能力去确保别人无法获得您的信息是必要的。 所谓的加密是复杂的艺术的保护,然而我们不知道设计第一个算法的人,我们也不知道到底有多少的算法。一切从简单的数据加扰,嬗变,甚至算法,其中有解密密钥(有时也称为密码)是不同的加密密钥(在一个方法所谓非对称加密) ,已做了一次又一次。 做为一个全面的权威加密方法,本文章肯定从来没有索赔,也不期望。 您只需要知道加密是你与MPQ直接相关的。 让我们从一个简单的加密算法开始,这是刊登在《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; } 这是非常简单的哈希代码,不应被用来在一个实际的程序中使用。 即使代码是隐藏的(没有双关语意),这也是简单的 。 不言而喻,这是通过块进行加密的,把每个字节与相应的字节的密码转换为二进制。然后修改字节的密码,加入13 ( 选择13是因为这是一个素数)。这样做是为了使代码的模式,更难以识别。 那么,用此算法,加密字符串“encryption” ( 65 6E 63 72 79 70 74 69 6F 6E),加密的密码“ mpq ” (4D 50 51 ),这样会得到一个无法读取字符串(28 3E 32 28 24 2E 13 03 04 1A)。 现在,这个算法是对称的。这意味着密码是用来加密有相同密码的块。事实上,由于转换为二进制是一个对称的运作,完全相同的算法可以用来解密。请注意,大多数的对称加密算法是不完全对称,所以他们要求加密和解密的功能有所不同。 好吧,下面就就是关键的地方。 如果您想要编写,就必须在哪里都知道加密算法。 教导给您这个方法是我的使命。 MPQ的加密算法混合其他加密技术。它创建了一个加密表(这也是用在散列函数) ,并使用一个文件的加密密钥,以挑选出某些成员的加密表。然后对表中的成员进行转换成二进制数据加密。现在,用一个相当奇怪的方法来做,所以或许有些代码将显示您it is overcomplicated :-p。以下代码生成密码表数组长度为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); } } } 你是不是越来越觉得暴雪聘请了一名心怀不满的微积分教授写这些算法?还好对与我这不是问题,如果你不明白此代码。如果您想要编写,您需要这些功能,你不一定要了解他们。无论如何,在密码表初始化后,我们可以解密MPQ数据,具有下列功能(不要期望我向您解释,我不想知道如何运作自己! ) : 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; } } 第四章 STROM 称为STROM库函数,或者简称为STROM。 它是对于本身的运行系统,拥有庞大的功能库函数。甚至不需要Microsoft支持。 它本身包含了足够强大的功能,甚至不需要调用本地API函数。 事实上,STROM包含了所有暴雪编写的可以重复使用的功能。 但它也拥有一些操作系统特殊的要求 比如那些在GDI,DirectX,QuickDraw等等。 原因很简单,就是为了减轻从一个系统到另一个系统的接口问题。 毕竟,这就是为什么花成千上万的工作时间把数以千计的操作系统函数从Windows源调用Mac一样,为什么在不花时间去做调用,而去改写功能? 根据STROM的多个版本,大约累计了275个实际有用的功能。 正如您看到的,没更新STROM时,仍然使用STROM库函数。同样,更新后依然是旧的STROM库函数, 只是做了更新。这是为了保证游戏在不同版本的兼容性。 这些275个使用功能分为约20个功能集(通常在Windows环境下称为subsystems,在MAC环境下称为managers。 下面所示部分清单: Memory Subsystem -记忆体子系统-例行的共同记忆功能,包括分配新的内存,释放分配内存,灌装记忆体,以及更多。STROM没有自己的内存管理,包括内置的错误检查和其他强大的功能。该子系统功能与在PC上与'mem'前缀相等。 String Subsystem - 字符串子系统-功能是使用字符串,如复制,合并,搜索等这些职能是大多数部分,相等于'str'的功能。 File Subsystem - 文件子系统-功能是存取文件系统。有能力读出(但不包括写入)无论是在磁盘上的可靠文件,还是mpq档案。撇开mpq读取的功能,其他功能都是高级系统功能运行方式。 Network Subsystem -网络子系统-功能是接入远端的电脑系统,通过使用IPX,调制解调器, TCP/IP和直接电缆。职能是与服务器或在游戏中玩家的通讯。使用高级系统特殊调用。 Error Subsystem -错误子系统- 功能是捕捉和处理错误。这些职能大部分没有与任何操作系统的等值。 Registry Subsystem -登录子系统- 功能是持久性储存数据到计算机中。使用注册表在Windows系统,或MACS系统上。 Bitmap Subsystem -位图系统-功能是位图文件装载和显示。使用系统特殊调用。 目前为止,大约只记录了40种功能,因为我手边没有足够时间来做,认真来做的话大约需要几个月。 此外这里只只讨论MPQ。 Using the Strom API 使用STROM API函数 说明:其余的这一章是针对Windows平台的! 正如我以前说过,STROM功能任何人都可使用它们。不过,暴雪并不希望如此。 我花了最近两天时间,总结出来:STROM使用一个非常邪恶的方法来对付我们这种想使用它的人。 我花了至少10个小时的努力,试图解除愚蠢的事,而我现在可以很骄傲的说我成功了,我会全力为您解释冗长而复杂的细节。 经过我和Mike O'Brien所谓的the Storm Interface Library(接口库)斗智斗勇。发现这是由一个头文件和导入库组成,所以我做了Storm booby-traps这个工具 。要记得DLLs 101 ,是包含被用来当程序编译连接程序DLL的导入表的导入库 。这意味着什么,就是所有您需要做的就是storm.lib (在STROM接口库)与模块连接在您的程序和#include storm.h头文件。这真令我疯狂,我不得不让您可以轻松使用the Storm Interface Library。 现在,为了以后少点麻烦,让我们现在就看看STROM的功能。 Opening an MPQ Archive- SFileOpenArchive 打开MPQ存档函数—SFileOpenArchive BOOL WINAPI SFileOpenArchive(LPCSTR lpFileName, DWORD dwMPQID, DWORD dwUnknown, HANDLE *lphMPQ); Parameter What it is lpFileName [in] A pointer to NULL terminated string that holds the path of the MPQ to open. SFileOpenArchive will crash if this is NULL. dwMPQID [in] An ID value that is saved internally in Storm for the MPQ. What this parameter is used for is not clear at this time. dwUnknown Unknown. Should always be NULL. lphMPQ [out] Pointer to a HANDLE variable that, upon successful completion, will hold the HANDLE of the MPQ.SFileOpenArchive will fail if this is NULL. |