星际、魔兽和WOW里面都有一个非常大的.MPQ文件,这个文件存储了游戏中大部分的资源数据,比如对话中的文字等等。Blizzard使用了hash table来组织对这个庞大文件的读写。

在WOW中,如果我跑到荆棘谷和地精卫兵聊天,那些绿色小矮人头上冒出来的叽叽咕咕的文字,就是从那个奇大无比的资源文件里取出来的。嗯,小绿人有那么多台词,怎么从这么一个庞大的字符串数组里找出某个特定的呢?或者,给你一句话,怎么找出相同的那一句出来?

当然你可以从这个数组头开始,一个个字符串读过去,比较每一个,直到找到对应的。这样的方法当然也没问题,如果你的PC真的有那么快的话...

Blizzard的天才和牛人们当然不会这样做,他们用了更聪明的方法: 用某种算法,把一个字符串压缩成一个整数,比如把每个字符按ASCII码值相加,得到的这个整数,称为Hash。然后,根据这个整数值,直接得到此字符串在整个文件中的位置,从而直接读取之。

MPQ中使用这样的Hash算法:

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;
}

得到这个Hash值后,还需要某种算法来得出它对应的存储位置。通常构造一个称为Hash Table的数组来避免逐个的比较。Hash值通过模运算来得到表中的对应位置。

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
}

那么,如果两个字符串在表中对应的位置相同怎么办?这种可能性是极大的,解决的方法也很多。比较简单和常规的是使用链表,在Hash表每个值上挂一个链表来保存多个字符串。

en, Blizzard的牛人使用了更强的办法:他们使用三个Hash值(而不是通常的一个)来校验字符串。如果两个字符串用三个hash算法得出的入口点都一致,那几乎是不可能的事情。

Blizzard使用的hash表没有链表,看看这个算法:

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);
       int nHashA = HashString(lpszString, HASH_A);
       int nHashB = HashString(lpszString, HASH_B);
       int 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
}

只有在hash入口值不为空,并且两个校验Hash值也匹配的情况下,才算真正找到对应的字符串位置。非常简单清晰的算法,但非常非常的高效。

绝大部分的资料来自于Justin Olbrantz的Inside MoPaQ,非常感谢和佩服这位牛人

zz 一个Hash实例:Blizzard的MPQ文件相关推荐

  1. python判断文件是否存在、不存在则创建_python判断文件是否存在,不存在就创建一个的实例...

    python判断文件是否存在,不存在就创建一个的实例 如下所示: try: f =open("D:/1.txt",'r') f.close() except IOError: f ...

  2. python打开一个不存在的文件时-python判断文件是否存在,不存在就创建一个的实例...

    python判断文件是否存在,不存在就创建一个的实例 如下所示: try: f =open("D:/1.txt",'r') f.close() except IOError: f ...

  3. 小甲鱼 OllyDbg 教程系列 (二) :从一个简单的实例来了解PE文件

    小甲鱼视频讲解: https://www.bilibili.com/video/av6889190?p=6 https://www.bilibili.com/video/av6889190?p=7 从 ...

  4. Blizzard的MPQ文件格式搜索算法---来自Gameres bbs

    Blizzard的MPQ文件格式搜索算法 Blizzard被称为最卓越的游戏制作公司,不愧于此. 作者:ghost2006 最近学习了一下Blizzard的MPQ文件格式,颇有一些心得,其中一条就是对 ...

  5. JUnit基础及第一个单元测试实例(JUnit3.8)

    JUnit基础及第一个单元测试实例(JUnit3.8) 单元测试 单元测试(unit testing) ,是指对软件中的最小可测试单元进行检查和验证. 单元测试不是为了证明您是对的,而是为了证明您没有 ...

  6. 创建第一个vue实例

    一.vue安装与下载 1. 官网下载  下载地址 选择开发版本 2. 打开sublime,新建vue文件夹,将下载好的代码vue.js放入vue文件夹中. 3. 新建index.html文件,在hea ...

  7. python读取目录_Python读取一个目录下所有目录和文件

    本文实例讲述了Python读取一个目录下所有目录和文件的方法.分享给大家供大家参考,具体如下: 这里介绍的是刚学python时的一个读取目录的列子,给大家分享下: #!/usr/bin/python ...

  8. 将oracle冷备份恢复到另外一个数据库实例中

    因更换服务器需要将Oracle数据库转移到另外台Oracle中. 说明: 1.测试环境为:windows server2003 和 oracle 10g. 2.2台服务器安装的程序目录一样,数据目录不 ...

  9. C# WinForm只允许运行一个窗体实例

    大概看了看别人的方法,都是从语法的角度巧妙实现的. 我要实现的目的是dialogForm.Show(); 点击按钮显示对话框窗体,如果窗体没有关闭,再次点击,不重复显示. 我用了个笨方法,就是用一个静 ...

  10. 和我一起学Windows Workflow Foundation(1)-----创建和调试一个WF实例(转)

    和我一起学Windows Workflow Foundation(1)-----创建和调试一个WF实例 今天开始,我打算开始学习WWF,从网上搜索到了部分相关资料,也找到了一些文档和实验.但是,资料以 ...

最新文章

  1. JZOJ 5275. 水管
  2. Hbase2.1.0-CDH6.3.2 Region in Transition (永久RIT) 异常解决
  3. Android系统从驱动到上层服务再到应用的两种服务架构方式
  4. Angular应用里使用rxjs提供的观察者和发布者实现事件处理
  5. 做ppt用的小插图_如何用PPT做随机抽奖?
  6. linux下怎么编译运行C语言程序?
  7. 阿里云再降价 数据库产品降20%
  8. 【赠书】拨云见日 - 深入解析Oracle TX行锁(下)
  9. 苹果要做第一个吃螃蟹的人!将率先尝试台积电5nm工艺
  10. qregularexpression和qregexp的区别
  11. 给Chrome和Firefox添加js脚本作为插件的方法
  12. 使用ENVI下载雷达图像参考DEM的方法
  13. 十进制计算机算法,计算机知识--二进制,十进制,十六制算法
  14. 使用mongoTemplate进行Aggregation聚合查询
  15. TL-R473P-AC【搭配面板式AP组网设置方法】
  16. spring4开发SpringBatch 样例 -配置文件版
  17. SSM校园好货APP的设计与实现毕业设计源码121619
  18. 微信小程序授权登录(获取手机号及用户信息)
  19. 电子制造企业如何做好商机管理,促成更多交易
  20. 【GNN图神经网络】理解图特征之聚类系数(Clustering coefficient)

热门文章

  1. 浙江农林大学计算机分数线,浙江农林大学各专业录取分数线
  2. 【数学】通俗解释布丰投针实验过程、蒙特卡洛方法及python仿真代码
  3. /布丰投针实验/ /模拟三门 //模拟排队//求解有约束的非线性规划问题// 书店买书////用于模拟导弹追击问题// TSP(旅行商问题)
  4. 1-9 requests模块之爬取4k高清壁纸(xpath解析)
  5. python 爬取淘宝视频_识别假货有绝招,先用python抓淘宝评论(附视频教程)
  6. java通过qq邮箱发送_java通过qq邮箱发送邮件
  7. 商米D1S一体机设置搜狗手写输入法图解
  8. 电脑桌面简约时钟工具OneClock
  9. c++:std::dec, std::hex, std::oct
  10. 计算机辅助设计职业标准,计算机辅助设计绘图员国家职业标准.doc