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库 源码分析 一相关推荐

  1. 《微信小程序-进阶篇》Lin-ui组件库源码分析-列表组件List(一)

    大家好,这是小程序系列的第二十篇文章,在这一个阶段,我们的目标是 由简单入手,逐渐的可以较为深入的了解组件化开发,从本文开始,将记录分享lin-ui的源码分析,期望通过对lin-ui源码的学习能加深组 ...

  2. Android主流三方库源码分析(九、深入理解EventBus源码)

    一.EventBus使用流程概念 1.Android事件发布/订阅框架 2.事件传递既可用于Android四大组件间通信 3.EventBus的优点是代码简洁,使用简单,事件发布.订阅充分解耦 4.首 ...

  3. sigslot库源码分析

    言归正传,sigslot是一个用标准C++语法实现的信号与槽机制的函数库,类型和线程安全.提到信号与槽机制,恐怕最容易想到的就是大名鼎鼎的Qt所支持的对象之间通信的模式吧.不过这里的信号与槽虽然在概念 ...

  4. surprise库源码分析

    最近工作上需要使用到协同过滤,来计算相似度,因此根据https://blog.csdn.net/weixin_43849063/article/details/111500236的步骤对surpris ...

  5. Python Requests库源码分析

    1. Requests库简介 书籍是人类进步的阶梯,源码是程序员进步的阶梯.为了进步,我们就要不断地阅读源码,提升自己的技术水平.今天我们来剖析一下Python的Requests库. Requests ...

  6. cJSON库源码分析

    cJSON是一个超轻巧,携带方便,单文件,简单的可以作为ANSI-C标准的Json格式解析库. 那什么是Json格式?这里照搬度娘百科的说法: Json(JavaScript Object Notat ...

  7. python库源码分析_python第三方库Faker源码解读

    源码背景 Faker是一个Python第三方库,GITHUB开源项目,主要用于创建伪数据创建的数据包含地理信息类.基础信息类.个人账户信息类.网络基础信息类.浏览器信息类.文件信息类.数字类 文本加密 ...

  8. 山东大学软件实验课程-Ebiten-基于go语言实现的2D游戏库源码分析第一篇-综述 2021SC@SDUSC

    2021SC@SDUSC 目录 一.项目综述 二.go语言安装及环境配置 1.Go的安装 2.IDE的使用 三.小组内成员分工 一.项目综述 Ebiten 是Go 编程语言的开源游戏库.Ebiten ...

  9. SoundTouch音频处理库源码分析及算法提取(1)

    SoundTouch音频处理库的使用异常简单,经过简单的编译之后,设置编译环境,以vc为例 ,直接在include包含SoundTouch目录下的include路径,接着在lib添加SoundTouc ...

  10. 云风coroutine库源码分析

    项目介绍-coroutine 云凤写过一个简易协程库,介绍如下: It's an asymmetric coroutine library (like lua). You can use corout ...

最新文章

  1. Android 导航栏如何轻松搞定
  2. Fill in blank -Thermal experiment theory and technology2018-01-06
  3. 台式电脑如何截屏_电脑如何安装安卓系统 台式机装安卓系统教程
  4. This dependency was not found: * !!vue-style-loader!css-loader?……解决方案
  5. 【华为云技术分享】ARMv8-A存储模型概述(2)
  6. 物联网通信之Coap 协议
  7. ssis for循环容器_SSIS包中的序列容器
  8. R2: 相关系数、复相关系数及半偏相关系数之间的联系
  9. Java 中equals 与 == 的区别:
  10. 双击运行 jar 文件
  11. 模式识别和机器学习的区别
  12. 透过表象看本质!?之三——Kalman滤波
  13. bat脚本打开cmd执行命令
  14. 【网络攻防课实验】一:本地系统密码破解
  15. 倒不过的“饮食时差”,减肥路上的最大阻碍?
  16. 20行代码教你如何批量提取图片中文字
  17. win10右键菜单没有新建Excel选项的解决方法
  18. IN4007和IN4148的用途
  19. 2017中国之旅系列之四:福建东山岛之旅
  20. Webdings 图形字体

热门文章

  1. Java编码安全规范
  2. IDEA 设置导入/导出
  3. 制药企业常用质量管理软件TrackWise概述
  4. vue单页面html缓存问题,vue单页面 回退页面 keeplive 缓存问题
  5. 华为 项目管理10大模板 【Word版 (可直接套用)】
  6. C#学习笔记-WPF框架(MvvmLight)
  7. webstorm使用指南
  8. OpenStack柏林峰会主会场侧记:OpenStack立柱、拱券与灵魂
  9. 电脑主机前置耳机插孔没声音——解决办法
  10. VBA word 文件类型html,Word VBA SaveAs错误:不兼容的文件类型和文件扩展名