欢迎您,请 登录 或 注册会员

偶久网

偶久网 首页 改图教学 查看内容

魔兽改图MPQ技术内幕

2016-6-5 01:40| 作者:邪恶叔| 小编:邪恶叔| 查看: 1682

MPQ,也称MoPaQ,是Mike O'Brien发明的一种压缩文件格式。 
在1996作为,MPQ应用在Diablo(暗黑破坏神)游戏中。 
然而它的版权属于 Blizzard 的父公司 Havas Interactive,并且在Mike O'Brien离开暴雪后继续使用。 正是MPQs由于在Diablo(暗黑破坏神)中的出色表现,使其继续应用在Starcraft(星际争霸), Warcraft 2(魔兽争霸2), Diablo 2(暗黑破坏神2), Lords of Magic(魔法大帝)中。 

第二章 关于MPQ的介绍 


MPQ内部包含了许多文件,包括坐标算法、声音、动画、字符串、数字数据和故事情节信息。 
明显地,MPQ的潜力很大。要想利用MPQ,那么您就需要了解它。 

在有MPQ格式之前,一直使用的是WAR格式,在Warcraft 2,甚至在Warcraft1中存放游戏数据。然而WAR格式是简单的,不精制的,是由缺乏经验的程序员所编写的文件格式(相信我,我知道)。文件在档案中仅使用参考序数和是否被压缩做为唯一可选择调用的方法。 
尽管如此它仍然完成了它的任务。它提供了压缩格式下的文件调用。但是,很快缺点开始出现。调用时使用参考序数,意味着一长传文件接口的名单必须被保留和被咨询,当程序员需要使用其中一个文件,那么则需要级长的时间,工作变得越来越繁琐。 
当时这些问题并没有那么严重,所以有人坚持使用WAR格式,但是一切在使用Battle.net(网络对战)后,问题变得不能接受。 


MPQ的特点 
如被提及以前,MPQ格式一直被用做修正WAR的设计缺陷。但是现在他们也想增加一些全新的特点到MPQ。在暴雪的游戏中,MPQ格式的特点总结为以下几点: 

Security. 安全 

暴雪一定不希望在游戏中玩家可以修改数据。或许他们提早知道MPQ格式可以为Starcraft使用。 不管怎样,安全是最重要的,由此他们显然做了级大的努力去维护游戏的安全性。 

Efficiency. 效率 

MPQs要求执行时先简单预先输入的各种各样的任务数据然后实时放出。对于预先输入数据,时间并不重要。 但是实时放出就是另一件事了,其中的数据必须快速地被解压使用。 

Multilinguality.多语言的计算机处理 

在最开始的时候,暴雪就计划发布其游戏在全球游戏市场,因此他们尽可能的做到多语言。 在创新时,他们决定设计多语种能写入MPQ格式。 
。 
Expandability.扩展 

显然的,在游戏中需要使用独立的数据。太大的数据不仅是效率低并且减慢游戏速度,如果补丁修改了,也是很麻烦的。暴雪明白这个道理,因而MPQ格式的要求就是有能力完全,高效率的,从多个档案数据中调用需要的数据。 


什么是strom 
相比在程序模块中复制函数,多数程序员喜欢把相同代码放到shared libraries(共享程序库)里。shared libraries是包含了任意程序功能的函数模块。不仅能避免多余,并且能缩小程序大小。 
正因为如此,暴雪使用一个称为Storm的共享程序库(PC机上为Storm.dll,MAC机为Storm.bin)。 
所有现代的暴雪游戏中都使用strom存放重要功能,比如读取MPQ,Battle.net和一些图形化例程。 
当暴雪要发布新版本的游戏,只需要增加功能到strom,无需改变原有功能。 这意味着旧版本的游戏只用升级新版本strom就可以了,这就是我们俗称的安装补丁。 
就像所有共享程序库,任何想使用它的程序都可以访问到它的函数。这就是为什么strom只包含MPQ读取功能。 


什么是 MPQ API Library DLL 

虽然 Storm 没有包含任何编写MPQ的功能。 
但是 StarEdit 包含,因为 SCM/SCX 文件也是 MoPaQ文件。 
但是这些函数被加密了,所以只有知识渊博的黑客们才可以使用。 
对于Blizzard 来说不幸的是,有一个这样的黑客,他的名字是 Andrey Lelikov(aka Lelik)。 
他发现了一种访问这些宝贵的函数的途径,并把这个复杂的过程封装在 
LMPQAPI.DLL(Lelik's MPQ API Library DLL)文件中。该文件自动破解 
StarEdit,将这些函数展示在所有的程序员面前。
第三章 MPQ的基本原理 

通过整个计算机发展史来看,绝大多数的进步都是在求解问题中发生的。 
那么在这一章中,我们将采取看看一些涉及到MPQ的问题及其解决办法。 

HASH (散列或哈希) 

问题:你有一个非常大的字符串数组,和一个字符串 
怎么知道字符串是否在数组中? 

你可能会开始在数组中与其他字符串比较每个字符串,但是,当进行应用后,你会发现,这种方法在实际使用时是特别慢的。在此之前,你又怎能在没有与其他字符串比较的情况下,确定这个字符串是否存在? 

解决方法:hash 
hash是规模较小的数据类型(例如数字)能指向其他较大的数据类型(通常是字符串) 。在这种情况下,您可以在数组中先存储hash。然后再计算其他字符串的hash,并比较它存储的hash。通过字符串比较,如果hash在数组相匹配的新的hash,就可以核实存在。这就是所谓的索引查找,可以加快对于不同大小的数组和平均长度的字符串的搜索速度约100倍。 

unsigned long HashString(char *lpszString) 
{
unsigned long ulHash = 0xf1e2d3c4; 

while (*lpszString != 0) 
{
ulHash <<= 1; 
ulHash += *lpszString++;

return ulHash;
}