日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java解析魔兽争霸3录像W3G文件(一):Header

發布時間:2024/8/1 java 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java解析魔兽争霸3录像W3G文件(一):Header 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

魔獸爭霸3是一款非常著名的即時戰略游戲。相信很多人都聽過sky、moon、grubby這些名字,還有塔魔infi、中國的鬼王ted、剛猛的fly、飄逸的th000等選手。遺憾的是WCG2013是魔獸爭霸3的最后一屆,我自己也去現場觀看了魔獸的總決賽。此外,還有DOTA、真三、澄海3C等著名的地圖。

魔獸爭霸的錄像大家都知道,是用來回放的,文件后綴名是.w3g,保存在魔獸爭霸下的REPLAY目錄下。現在很多軟件可以分析魔獸爭霸錄像,直接可以查看錄像的玩家、地圖,以及玩家的APM等信息。

最近在YY對戰平臺打魔獸,經常能遇到Java程序員,說明Java程序員中有很多魔獸爭霸3的玩家,這里將Java解析魔獸爭霸3錄像的方法貢獻給同是WAR3玩家的小伙伴們。

魔獸爭霸3錄像文件由一個頭部(Header)多個壓縮數據塊(compressed data blocks)組成。本文主要內容是解析Header部分,壓縮數據塊部分的解析會在后續的博文中詳細介紹。

Header結構:

Header部分包含了錄像的最基本的信息,大小是固定的前68個字節,此后的全部是壓縮數據塊。對于1.06版本及之前的錄像,Header部分大小是64字節,由于版本太古老這里就不考慮了。下面的代碼中也不再支持老版本的錄像。

Header中每個部分的意義:

1~28字節(28個字符):固定的字符串"Warcraft III recorded game\0x1A\0"。
29~32字節(4個字節):Header部分的總字節數,對于1.07版本及之后,是68(0x44),對于1.06版本及之前是64(0x40)。
33~36字節(4個字節):壓縮數據塊的壓縮數據總字節數,即解壓前。
37~40字節(4個字節):錄像版本標識(0表示1.06版本及之前版本,1表示1.07版本及之后版本)。
41~44字節(4個字節):壓縮數據塊解壓縮后的總字節數。
45~48字節(4個字節):壓縮數據塊的個數。
49~52字節(4個字節):一個字符串標識,"WAR3"表示非冰封王座,"W3XP"表示冰封王座。
53~56字節(4個字節):版本號(例如24即是1.24版本)。
57~58字節(2個字節):構建號(build number)。
59~60字節(2個字節):0x0000表示單人游戲,0x8000(十進制32768)表示多人游戲。
61~64字節(4個字節):錄像時長(毫秒數),需要注意的是,這個時長不包括游戲中暫停的時長。
65~68字節(4個字節):Header部分CRC32校驗碼(包含這四個字節但是要都設為0)。

可以使用EditPlus的Hex Viewer方式打開w3g文件查看Header部分。



在這里可以發現一個問題,除了第一個字符串"Warcraft III recorded game\0x1A\0"以外,其他每個部分的字節順序都是倒過來的。例如Header部分總字節數是0x44000000,"W3XP"字符串順序是"PX3W"。這是因為這里使用的是小字節序(Little-Endian),也就是字節順序和正常的順序完全相反,所以在讀取的時候應該將其倒過來讀。



Java解析Header:

知道了Header部分的結構,下面就可以用Java語言來解析Header了。

首先定義一個Replay類,表示一場錄像,構造函數傳入錄像文件File。為了方便,將文件轉換成字節數組,再將字節數組傳給Header類進行處理。

Replay.java

package com.xxg.w3gparser;import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException;public class Replay {private Header header;public Replay(File w3gFile) throws IOException, W3GException {byte[] fileBytes = fileToByteArray(w3gFile);header = new Header(fileBytes);}/*** 將文件轉換成字節數組* @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;}}
在Header類中,按小字節序讀取所有的Header信息。Header的最后四個字節是CRC32循環冗余檢驗碼,Java中可以使用java.util.zip.CRC32類來計算,下面的代碼中校驗了計算結果和Header中是否一致。有關CRC32的介紹可以查看相關資料,這里不再介紹。

Header.java

package com.xxg.w3gparser;import java.util.zip.CRC32;public class Header {public static final String BEGIN_TITLE = "Warcraft III recorded game\u001A\0";private long headerSize;private long compressedDataSize;private long headerVersion;private long uncompressedDataSize;private long compressedDataBlockCount;private String versionIdentifier;private long versionNumber;private int buildNumber;private int flag;private long duration;public Header(byte[] fileBytes) throws W3GException {// 讀取開頭的字符串"Warcraft III recorded game\u001A\0"String beginTitle = new String(fileBytes, 0, 28);System.out.println("1-28字節:" + beginTitle);if (!BEGIN_TITLE.equals(beginTitle)) {throw new W3GException("錄像格式不正確。");}// header部分總大小(版本小于或等于V1.06是0x40(64),版本大于或等于V1.07是0x44(68))headerSize = LittleEndianTool.getUnsignedInt32(fileBytes, 28);System.out.println("29-32字節:" + headerSize);if (headerSize != 0x44) {throw new W3GException("不支持V1.06及以下版本的錄像。");}// 壓縮文件大小compressedDataSize = LittleEndianTool.getUnsignedInt32(fileBytes, 32);System.out.println("33-36字節:" + compressedDataSize);// header版本(版本小于或等于V1.06是0,版本大于或等于V1.07是1)headerVersion = LittleEndianTool.getUnsignedInt32(fileBytes, 36);System.out.println("37-40字節:" + headerVersion);if (headerVersion != 1) {throw new W3GException("不支持V1.06及以下版本的錄像。");}// 解壓縮數據大小uncompressedDataSize = LittleEndianTool.getUnsignedInt32(fileBytes, 40);System.out.println("41-44字節:" + uncompressedDataSize);// 壓縮數據塊數量compressedDataBlockCount = LittleEndianTool.getUnsignedInt32(fileBytes, 44);System.out.println("45-48字節:" + compressedDataBlockCount);// WAR3:非冰封王座錄像,W3XP冰封王座錄像versionIdentifier = LittleEndianTool.getString(fileBytes, 48, 4);System.out.println("49-52字節:" + versionIdentifier);// 版本號(例如1.24版本對應的值是24)versionNumber = LittleEndianTool.getUnsignedInt32(fileBytes, 52);System.out.println("53-56字節:" + versionNumber);// Build號buildNumber = LittleEndianTool.getUnsignedInt16(fileBytes, 56);System.out.println("57-58字節:" + buildNumber);// 單人游戲(0x0000) 多人游戲(0x8000,對應十進制32768)flag = LittleEndianTool.getUnsignedInt16(fileBytes, 58);System.out.println("59-60字節:" + flag);// 錄像時長(毫秒)duration = LittleEndianTool.getUnsignedInt32(fileBytes, 60);System.out.println("61-64字節:" + duration);// CRC32校驗碼long crc32 = LittleEndianTool.getUnsignedInt32(fileBytes, 64);System.out.println("65-68字節:" + crc32);// 這里來校驗CRC32,將最后四位也就是CRC32所在的四個字節設為0后計算CRC32的值CRC32 crc32Tool = new CRC32();crc32Tool.update(fileBytes, 0, 64);crc32Tool.update(0);crc32Tool.update(0);crc32Tool.update(0);crc32Tool.update(0);System.out.println("計算CRC32:" + crc32Tool.getValue());// 判斷Header中后四位讀取的CRC32的值和計算得到的值比較,看是否一致if (crc32 != crc32Tool.getValue()) {throw new W3GException("Header部分CRC32校驗不通過。");}}public long getHeaderSize() {return headerSize;}public long getCompressedDataSize() {return compressedDataSize;}public long getHeaderVersion() {return headerVersion;}public long getUncompressedDataSize() {return uncompressedDataSize;}public long getCompressedDataBlockCount() {return compressedDataBlockCount;}public String getVersionIdentifier() {return versionIdentifier;}public long getVersionNumber() {return versionNumber;}public int getBuildNumber() {return buildNumber;}public int getFlag() {return flag;}public long getDuration() {return duration;}}
Header中用到了LittleEndianTool是用來按小字節序讀取數據的工具類。

LittleEndianTool.java

package com.xxg.w3gparser;/*** Little-Endian(小字節序)工具類* @author 叉叉哥(806223819@qq.com)*/ public class LittleEndianTool {/*** 以Little-Endian(小字節序)方式讀取字節數組中的一個16位(2個字節)無符號整數* @param bytes 字節數組* @param offset 開始字節的位置索引* @return 16位(2個字節)無符號整數*/public static int getUnsignedInt16(byte[] bytes, int offset) {int b0 = bytes[offset] & 0xFF;int b1 = bytes[offset + 1] & 0xFF;return b0 + (b1 << 8);}/*** 以Little-Endian(小字節序)方式讀取字節數組中的一個32位(4個字節)無符號整數* @param bytes 字節數組* @param offset 開始字節的位置索引* @return 32位(4個字節)無符號整數*/public static long getUnsignedInt32(byte[] bytes, int offset) {long b0 = bytes[offset] & 0xFFl;long b1 = bytes[offset + 1] & 0xFFl;long b2 = bytes[offset + 2] & 0xFFl;long b3 = bytes[offset + 3] & 0xFFl;return b0 + (b1 << 8) + (b2 << 16) + (b3 << 24);}/*** 以Little-Endian(小字節序)方式讀取字節數組中的字符串* @param bytes 字節數組* @param offset 開始字節的位置索引* @param length 需要讀取的長度* @return 讀取的字符串*/public static String getString(byte[] bytes, int offset, int length) {byte[] temp = new byte[length];for(int i = 0; i < length; i++) {temp[i] = bytes[offset + length - i - 1];}return new String(temp);}}
這里需要注意的是,Java中int類型4個字節大小,但是由于是有符號的整數,補碼的最高位是符號位,所以對于Header中的4個字節的無符號整數,必須要用long類型才足夠。2個字節的無符號整數需要使用Java中的int而不能是short。

另外,Header中用到了W3GException異常。

W3GException.java

package com.xxg.w3gparser;public class W3GException extends Exception {public W3GException(String message) {super(message);}}
最后用main方法調用這些代碼來測試。

Test.java

package com.xxg.w3gparser;import java.io.File; import java.io.IOException;public class Test {public static void main(String[] args) throws IOException, W3GException {Replay replay = new Replay(new File("E:/魔獸爭霸3冰封王座/REPLAY/100729_[NE]EHOME.ReMinD_VS_[ORC]WemadeFOX_Lyn_EchoIsles_RN.w3g"));Header header = replay.getHeader();System.out.println("WAR3錄像基本信息為:");System.out.println("版本:1." + header.getVersionNumber() + "." + header.getBuildNumber());long duration = header.getDuration();long second = (duration / 1000) % 60;long minite = (duration / 1000) / 60;if (second < 10) {System.out.println("時長:" + minite + ":0" + second);} else {System.out.println("時長:" + minite + ":" + second);}}}
輸出結果:

1-28字節:Warcraft III recorded game ?
29-32字節:68
33-36字節:125736
37-40字節:1
41-44字節:311296
45-48字節:38
49-52字節:W3XP
53-56字節:24
57-58字節:6059
59-60字節:32768
61-64字節:783600
65-68字節:1414752232
計算CRC32:1414752232
WAR3錄像基本信息為:
版本:1.24.6059
時長:13:03


參考文檔:http://w3g.deepnode.de/files/w3g_format.txt


作者:叉叉哥 ? 轉載請注明出處:http://blog.csdn.net/xiao__gui/article/details/17882303



總結

以上是生活随笔為你收集整理的Java解析魔兽争霸3录像W3G文件(一):Header的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。