在上一篇博文中,分析了魔兽争霸3录像W3G文件的Header部分的解析。Header部分占文件的前68个字节,紧接着Header之后,也就是从68字节之后,就是多个压缩数据块。压缩数据块的个数保存在Header中,也就是Header的45~48字节。
所有的压缩数据块中的数据实际上是一个整体,被分割成很多块。录像文件生成的时候,将原始的数据按8K(8192字节)分割成很多段,最后一段如果不足8K则补0,然后将每一段zlib压缩,生成一个压缩数据块,在每一段前面加上压缩数据块的Header(注意,这里所指的Header不是整个录像文件的Header,而是每个压缩数据块的Header)。
所以这里要做的就是遍历每个压缩数据块,解压缩后再合并,这样才能还原成原始的数据。
每个压缩数据块的结构:
每个压缩数据块,由Header和压缩数据组成。Header部分总共是8个字节,而压缩数据的字节数大小保存在每一个压缩数据块的Header中。
1~2字节(2个字节):压缩数据的字节数,小字节序。
3~4字节(2个字节):解压后数据的字节数,固定的值是8192(8KB),小字节序。
5~8字节(4个字节):未知。
9~(n-8)字节(n个字节):压缩数据(压缩数据的字节数n就是Header部分1~2字节中的字节数)。
Java处理压缩数据块:
定义一个CompressedDataBlock类用来处理每一个压缩数据库,包括解析压缩数据块的Header,解压缩数据。在Java中可以使用java.util.zip.Inflater类来解压缩zlib格式的压缩数据。
CompressedDataBlock.java
package com.xxg.w3gparser;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
public class CompressedDataBlock {
private int compressedDataSize;
private int uncompressedDataSize;
private byte[] uncompressedDataBytes;
/**
* @param fileBytes 录像文件转成的字节数组
* @param offset 压缩数据块的开始位置
* @throws DataFormatException
* @throws W3GException
*/
public CompressedDataBlock(byte[] fileBytes, int offset) throws DataFormatException, W3GException {
System.out.println("开始处理一个压缩数据块...");
// 压缩数据大小
compressedDataSize = LittleEndianTool.getUnsignedInt16(fileBytes, offset);
System.out.println("1-2字节:" + compressedDataSize);
// 解压缩后数据大小
uncompressedDataSize = LittleEndianTool.getUnsignedInt16(fileBytes, offset + 2);
System.out.println("3-4字节:" + uncompressedDataSize);
// 压缩数据,从第8个字节开始,长度为compressedDataSize,解压缩
uncompressedDataBytes = new byte[uncompressedDataSize];
Inflater inflater = new Inflater();
inflater.setInput(fileBytes, offset + 8, compressedDataSize);
int realUncompressedDataSize = inflater.inflate(uncompressedDataBytes);
if(realUncompressedDataSize != uncompressedDataSize) {
throw new W3GException("解压缩数据异常");
}
}
public int getCompressedDataSize() {
return compressedDataSize;
}
public int getUncompressedDataSize() {
return uncompressedDataSize;
}
public byte[] getUncompressedDataBytes() {
return uncompressedDataBytes;
}
}
在Replay类中,遍历每一个压缩数据块,将解压缩后的数据合并成一个字节数组。
Replay.java
package com.xxg.w3gparser;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.zip.DataFormatException;
public class Replay {
private Header header;
public Replay(File w3gFile) throws IOException, W3GException, DataFormatException {
// 将文件转为字节数组,方便处理
byte[] fileBytes = fileToByteArray(w3gFile);
// 解析Header
header = new Header(fileBytes);
// 遍历解析每个压缩数据块,解压缩,合并
long compressedDataBlockCount = header.getCompressedDataBlockCount();
byte[] uncompressedDataBytes = new byte[0]; // 所有压缩数据块中数据解压合并到这个数组中
int offset = 68;
for(int i = 0; i < compressedDataBlockCount; i++) {
CompressedDataBlock compressedDataBlock = new CompressedDataBlock(fileBytes, offset);
// 数组合并
byte[] blockUncompressedData = compressedDataBlock.getUncompressedDataBytes();
byte[] temp = new byte[uncompressedDataBytes.length + blockUncompressedData.length];
System.arraycopy(uncompressedDataBytes, 0, temp, 0, uncompressedDataBytes.length);
System.arraycopy(blockUncompressedData, 0, temp, uncompressedDataBytes.length, blockUncompressedData.length);
uncompressedDataBytes = temp;
int blockCompressedDataSize = compressedDataBlock.getCompressedDataSize();
offset += 8 + blockCompressedDataSize;
}
// 压缩数据块解压合并后结果就是字节数组uncompressedDataBytes
System.out.println("解压缩合并后的原始数据字节数:" + uncompressedDataBytes.length);
}
/**
* 将文件转换成字节数组
* @param w3gFile 文件
* @return 字节数组
* @throws IOException
*/
private byte[] fileToByteArray(File w3gFile) throws IOException {
FileInputStream fileInputStream = new FileInputStream(w3gFile);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int n;
try {
while((n = fileInputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, n);
}
} finally {
fileInputStream.close();
}
return byteArrayOutputStream.toByteArray();
}
public Header getHeader() {
return header;
}
}
运行程序,输出:
开始处理一个压缩数据块...
1-2字节:4122
3-4字节:8192
开始处理一个压缩数据块...
1-2字节:4218
3-4字节:8192
开始处理一个压缩数据块...
1-2字节:4145
3-4字节:8192
开始处理一个压缩数据块...
1-2字节:3849
3-4字节:8192
开始处理一个压缩数据块...
1-2字节:3958
3-4字节:8192
开始处理一个压缩数据块...
1-2字节:3877
3-4字节:8192
开始处理一个压缩数据块...
1-2字节:3783
3-4字节:8192
开始处理一个压缩数据块...
1-2字节:3996
3-4字节:8192
开始处理一个压缩数据块...
1-2字节:3962
3-4字节:8192
开始处理一个压缩数据块...
1-2字节:3987
3-4字节:8192
开始处理一个压缩数据块...
1-2字节:4169
3-4字节:8192
开始处理一个压缩数据块...
1-2字节:4062
3-4字节:8192
开始处理一个压缩数据块...
1-2字节:3828
3-4字节:8192
开始处理一个压缩数据块...
1-2字节:1323
3-4字节:8192
解压缩合并后的原始数据字节数:114688
参考文档:http://w3g.deepnode.de/files/w3g_format.txt
作者:叉叉哥 转载请注明出处:http://blog.csdn.net/xiao__gui/article/details/17993589
分享到:
相关推荐
C# 魔兽录像分析器 W3G格式分析器源代码
Java Warcraft Ⅲ Replay Parser(Java解析《魔兽争霸3》游戏录像工具).zip Java Warcraft Ⅲ Replay Parser(Java解析《魔兽争霸3》游戏录像工具).zip Java Warcraft Ⅲ Replay Parser(Java解析《魔兽争霸3》...
用于魔兽争霸3之冰封王座(War3)游戏、魔兽地图编辑器插件YDWE 动漫《犬夜叉》男猪脚模型下载 .mdx和.blp格式
魔兽争霸3模型查看器.zip
魔兽争霸3模型查看器,有意者自行下载
最新魔兽争霸录像 分析器 支持1.14~1.24版本
目前仅仅支持1.20E版 运行魔兽后,直接运行软件,点击“消除延迟”即可 适用的网络环境: 校园网、局域网。 软件默认设置为80ms。
魔兽争霸3地图 SLK编辑器,暂不支持保存功能
魔兽3地图文件及MPQ文件的查看和解压,c++源码
带源码,C# 编写,用于修改War3分辨率 zhangzhezh@gmail.com QQ 54424291
Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...
功能:灰色血条,伤害浮动显示,生命值魔法回复速率,蓝条,安全点击,人物装备关键物品提示
Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...
魔兽争霸地图加密,魔兽争霸地图算法加密辅助工具,亲测好用,欢迎看魔兽争霸地图加密教程
魔兽争霸1.27全图的源码 包括在大地图显示单位、小地图显示单位显示隐形单位的地址
Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...
.版本 2 .子程序 __启动窗口_创建完毕 _启用热键 (取窗口句柄 (), &abc) _注册热键 (2009, 取窗口句柄 (), 0, #Home键) _注册热键 (2008, 取窗口句柄 (), 0, #End键) ........................ .... .... .... ........返回 (0)
魔兽争霸平台地图ID提取工具