MPQ Storm库 源代码分析 一个
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库 源代码分析 一个的更多相关文章
- Muduo网络库源代码分析(六)TcpConnection 的生存期管理
TcpConnection是使用shared_ptr来管理的类,由于它的生命周期模糊.TcpConnection表示已经建立或正在建立的连接.建立连接后,用户仅仅须要在上层类如TcpServer中设置 ...
- Muduo网络库源代码分析(四)EventLoopThread和EventLoopThreadPool的封装
muduo的并发模型为one loop per thread+ threadpool.为了方便使用,muduo封装了EventLoop和Thread为EventLoopThread,为了方便使用线程池 ...
- Twitter Storm源代码分析之ZooKeeper中的目录结构
徐明明博客:Twitter Storm源代码分析之ZooKeeper中的目录结构 我们知道Twitter Storm的所有的状态信息都是保存在Zookeeper里面,nimbus通过在zookeepe ...
- Android应用程序框架层和系统运行库层日志系统源代码分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6598703 在开发Android应用程序时,少 ...
- Twitter Storm源代码分析之Nimbus/Supervisor本地目录结构
storm集群里面工作机器分为两种一种是nimbus, 一种是supervisor, 他们通过zookeeper来进行交互,nimbus通过zookeeper来发布一些指令,supervisor去读z ...
- android开发源代码分析--多个activity调用多个jni库的方法
android开发源代码分析--多个activity调用多个jni库的方法 有时候,我们在开发android项目时会遇到须要调用多个native c/jni库文件,下面是本人以前实现过的方法,假设有知 ...
- [Android] Volley源代码分析(五岁以下儿童)Q \\ u0026一个
Volley源代码分析系列那里一段时间,告诉我,有许多私人留言,同时一些问题抛出.对于一些简单的问题,我们跳,这两天被连接到朋友@smali提出的问题.告诉我你不得不赞叹查看源代码时的详细程度,大家一 ...
- RTMPdump(libRTMP) 源代码分析 7: 建立一个流媒体连接 (NetStream部分 2)
===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...
- RTMPdump(libRTMP) 源代码分析 6: 建立一个流媒体连接 (NetStream部分 1)
===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...
随机推荐
- Android自己定义控件系列一:Android怎样实现老版优酷client三级环形菜单
转载请附上本文链接:http://blog.csdn.net/cyp331203/article/details/40423727 先来看看效果: 一眼看上去好像还挺炫的,感觉比較复杂...实际上并不 ...
- Windows Phone开发(6):处理屏幕方向的改变
原文:Windows Phone开发(6):处理屏幕方向的改变 俺们都知道,智能手机可以通过旋转手机来改变屏幕的显示方向,更多的时候,对于屏幕方向的改变,我们要做出相应的处理,例如,当手机屏幕方向从纵 ...
- Effective C++规定45 额外的代码
这部分是额外的代码博客,关键45术语思想已经实现. #include<iostream> using namespace std; template<typename T> c ...
- zigbee学习:示例程序SampleApp中通讯流程
zigbee学习:示例程序SampleApp中通讯流程 本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明. 参考链接: http://wjf88223.bl ...
- The app references non-public selectors in payload With Xcode6.1
猴子原创,欢迎转载.转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢! 原文地址: p=591" style="color: rgb(255, 97, 0 ...
- C# Windows Phone 8 WP8 开发,将WebClient的DownloadStringCompleted事件改成非同步的awiat方法。
原文:C# Windows Phone 8 WP8 开发,将WebClient的DownloadStringCompleted事件改成非同步的awiat方法. 一般我们在撰写Windows Phone ...
- OpenCV原则解读HAAR+Adaboost
因为人脸检测项目.用途OpenCV在旧分类中的训练效果.因此该检测方法中所使用的分类归纳.加上自己的一些理解.重印一些好文章记录. 文章http://www.61ic.com/Article/DaVi ...
- 【原创】ZOJ_1649 Rescue 解题报告
Rescue Time Limit: 2 Seconds Memory Limit: 65536 KB Angel was caught by the MOLIGPY! He was put ...
- JDK5什么是新的线程锁技术(两)
一个. Lock线程同步实现互斥 Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也是一个对象. 两个线程运行的代码片段要实现同步相互排斥的效果.他们必须用 ...
- 让Emeditor支持markdown编辑博客
让Emeditor支持markdown编辑博客 1. 关于高亮显示 2.生成HTML文件并预览 用惯了Emeditor,最近又开始学习用markdown写博客,怎么让Emeditor支持markdow ...