How would you go about gathing information from a warcraft replay file?
--Players, Map, Races, etc.
I have looked at this site http://photoduhavre.free.fr/notes.htm , but im not sure how to get to that stage :/
Any information about how to go about this would be helpful (a rough outline would be nice)
*Any code would need to be in Java of course
*If you can tell already im pretty much a noob with Java (pretty much all programming)
--So ignor my noobness or any misspellings :)
I found some more current information on the replay file format here (http://warcraft.kliegman.com/cgi-bin/cvsweb.cgi/src/replay/w3g_format.txt).
This does sound interesting so I might try and take on this project. If I do I will post my results when I get them.
Here is what I have so far. It reads the first part of the header and displays it. Tomorrow I will make it parse the sub header and data blocks. From there all you have to do is figure out the compression, and how to play the replay. HTH.
WC3RHeader.java:
package tuberload.wc3.replay;
import java.io.BufferedInputStream;
import java.io.IOException;
import tuberload.util.Array;
import tuberload.util.ByteBuffer;
/**
* Constructs the Warcraft III Replay header information.
*/
public class WC3RHeader
{
// Replay header information
private char[] file_desc = new char[26];
private int unknown_1; // always 0x1A00 so fatr
private int head_size;
private int file_size;
private int replay_ver;
private int decomp_size;
private int data_blocks; // number of data blockes
// Subheader information
private WC3RSubHeader sub_head;
/**
* Initialize the header.
*/
public WC3RHeader (BufferedInputStream bos) throws IOException
{
// create byte buffer
ByteBuffer bb = new ByteBuffer();
byte[] data = new byte[bos.available()];
bos.read (data, 0, bos.available());
bb.insertByte (data);
// get file description
data = new byte[26];
data = bb.getBytes (0, 26);
for (int i = 0; i < 26; i++)
file_desc[i] = (char)data[i];
// unknown 1
unknown_1 = bb.getWord (0x1a);
// header size/subheader location
head_size = bb.getDword (0x1c);
// file size
file_size = bb.getDword (0x20);
// replay version
replay_ver = bb.getDword (0x24);
// decompressed file size
decomp_size = bb.getDword (0x28);
// the number of compressed data blocks
data_blocks = bb.getDword (0x2c);
// get the subheader information
if (replay_ver == 0x00)
{
// load sub header
}
else if (replay_ver == 0x01)
{
// load sub header 1
}
else
System.out.println ("Unknown replay version!");
}
/**
* Get the number of compressed data blocks.
*/
public int getDataBlocksCount()
{
return data_blocks;
}
/**
* Get the file size after decompression.
*/
public int getDecompressedSize()
{
return decomp_size;
}
/**
* Get the file size.
*/
public int getFileSize()
{
return file_size;
}
/**
* Get the replay version.
*/
public int getReplayVersion()
{
return replay_ver;
}
/**
* Get the header size.
*/
public int getHeaderSize()
{
return head_size;
}
/**
* Get the file description.
*/
public char[] getFileDescription()
{
return file_desc;
}
}
WC3RSubHeader.java
package tuberload.wc3.replay;
import java.io.BufferedInputStream;
import java.io.IOException;
public abstract class WC3RSubHeader
{
protected int unknown_1; // uknown_1 and patch_ver switch in header1
protected int patch_ver;
protected int build_num;
protected int players_flag; // single/multiplayer
protected int replay_len; // milliseconds
protected int checksum;
}
WC3RSubHeader1.java
package tuberload.wc3.replay;
public class WC3RSubHeader1 extends WC3RSubHeader
{
protected int ver_ident; // Classic/Expansion
}
WC3RMain.java
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import tuberload.wc3.replay.WC3RHeader;
import tuberload.wc3.replay.WC3RSubHeader;
public class WC3Main
{
public static void main (String[] args)
{
try
{
BufferedInputStream bos = new BufferedInputStream (
new FileInputStream ("Jeremy1.w3g"));
WC3RHeader header = new WC3RHeader (bos);
System.out.println ("File Description: "
+ String.valueOf (header.getFileDescription()));
System.out.println ("Header Size: " + header.getHeaderSize());
System.out.println ("File Size: " + header.getFileSize());
System.out.println ("Decompressed Size: "
+ header.getDecompressedSize());
System.out.println ("Replay Version: " + header.getReplayVersion());
System.out.println ("Number of Compressed Data Blocks: "
+ header.getDataBlocksCount());
}
catch (IOException exc) { System.out.println (exc.getStackTrace()); }
}
}
It would be quite an interesting project to write a program to view replays.
If anyone has a replay from version 1.06 or below it would be very helpful for testing purposes, otherwise I will just reinstall WC3 tonight and make my own.
TIA
Quote from: Tuberload on July 11, 2004, 02:18 PM
If anyone has a replay from version 1.06 or below it would be very helpful for testing purposes, otherwise I will just reinstall WC3 tonight and make my own.
TIA
Why 1.06? I have plenty of 1.
16 if you'd like some.
Edit: Nevermind, I see on that document it mentions about 1.06 and below, is different than 1.07 and up.
Quote from: UserLoser. on July 11, 2004, 04:00 PMWhy 1.06? I have plenty of 1.16 if you'd like some.
The replay file format changes after 1.07 and I wanted to make it backwards compatible. I have 1.16 installed right now so I have no way of making an old replay.
Code update: it now fully reads the header information and displays it. It is setup to handle old replay files, but I have been unable to test it so far so it might not work correctly.
WC3RHeader.java:
package tuberload.wc3.replay;
import java.io.BufferedInputStream;
import java.io.IOException;
import tuberload.util.Array;
import tuberload.util.ByteBuffer;
/**
* Constructs the Warcraft III Replay header information.
*/
public class WC3RHeader
{
// Replay header information
private char[] file_desc = new char[26];
private int unknown_1; // always 0x1A00 so far
private int head_size;
private int file_size;
private int replay_ver;
private int decomp_size;
private int data_blocks; // number of data blocks
// Subheader information
private WC3RSubHeader sub_head; // created based on replay version
/**
* Initialize the header.
*/
public WC3RHeader (BufferedInputStream bos) throws IOException
{
// create byte buffer
ByteBuffer bb = new ByteBuffer();
byte[] data = new byte[bos.available()];
bos.read (data, 0, bos.available());
bb.insertByte (data);
// get file description
data = new byte[26];
data = bb.getBytes (0, 26);
for (int i = 0; i < 26; i++)
file_desc[i] = (char)data[i];
// unknown 1
unknown_1 = bb.getWord (0x1a);
// header size/subheader location
head_size = bb.getDword (0x1c);
// file size
file_size = bb.getDword (0x20);
// replay version
replay_ver = bb.getDword (0x24);
// decompressed file size
decomp_size = bb.getDword (0x28);
// the number of compressed data blocks
data_blocks = bb.getDword (0x2c);
// get the subheader information
if (replay_ver == 0x00)
{
sub_head = new WC3RSubHeader (bb);
}
else if (replay_ver == 0x01)
{
sub_head = new WC3RSubHeader1 (bb);
}
else
System.out.println ("Unknown replay version!");
}
/**
* Get the subheader information.
*/
public WC3RSubHeader getSubHeader()
{
return sub_head;
}
/**
* Get the number of compressed data blocks.
*/
public int getDataBlocksCount()
{
return data_blocks;
}
/**
* Get the file size after decompression.
*/
public int getDecompressedSize()
{
return decomp_size;
}
/**
* Get the file size.
*/
public int getFileSize()
{
return file_size;
}
/**
* Get the replay version.
*/
public int getReplayVersion()
{
return replay_ver;
}
/**
* Get the header size.
*/
public int getHeaderSize()
{
return head_size;
}
/**
* Get the file description.
*/
public char[] getFileDescription()
{
return file_desc;
}
}
WC3RSubHeader.java
package tuberload.wc3.replay;
import tuberload.util.ByteBuffer;
public class WC3RSubHeader
{
protected int unknown_1; // uknown_1 and patch_ver switch in sub header1
protected int patch_ver;
protected int build_num;
protected int players_flag; // single/multiplayer
protected int replay_len; // milliseconds
protected int checksum;
/**
* Initialize the subheader.
*/
public WC3RSubHeader (ByteBuffer bb)
{
unknown_1 = bb.getWord (0x30);
patch_ver = bb.getWord (0x32);
build_num = bb.getWord (0x34);
players_flag = bb.getWord (0x36);
replay_len = bb.getDword (0x38);
checksum = bb.getDword (0x3c);
}
/**
* Get the replay length.
*/
public int getReplayLength()
{
return replay_len;
}
/**
* Get the replay checksum.
*/
public int getChecksum()
{
return checksum;
}
/**
* Get the patch version.
*/
public int getGamePatchVersion()
{
return patch_ver;
}
/**
* Get the build number.
*/
public int getGameBuildNumber()
{
return build_num;
}
/**
* Get the number of players flag.
*/
public boolean isMultiplayer()
{
boolean multi = false;
if (players_flag == 0x8000)
multi = true;
return multi;
}
/**
* Get the game type.
*/
public boolean isExpansion()
{
return false;
}
}
WC3RSubHeader1.java
package tuberload.wc3.replay;
import tuberload.util.ByteBuffer;
public class WC3RSubHeader1 extends WC3RSubHeader
{
protected int ver_ident; // Classic/Expansion
/**
* Initialize the subheader v1
*/
public WC3RSubHeader1 (ByteBuffer bb)
{
super (bb);
ver_ident = bb.getDword (0x30);
patch_ver = bb.getDword (0x34);
build_num = bb.getWord (0x38);
players_flag = bb.getWord (0x3a);
replay_len = bb.getDword (0x3c);
checksum = bb.getDword (0x40);
}
/**
* Get the game type.
*/
public boolean isExpansion()
{
boolean expansion = false;
if (ver_ident != 0x57415233)
expansion = true;
return expansion;
}
}
WC3Main.java
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import tuberload.wc3.replay.WC3RHeader;
import tuberload.wc3.replay.WC3RSubHeader;
public class WC3Main
{
public static void main (String[] args)
{
try
{
BufferedInputStream bos = new BufferedInputStream (
new FileInputStream ("Jeremy1.w3g"));
WC3RHeader header = new WC3RHeader (bos);
WC3RSubHeader sub_head = header.getSubHeader();
System.out.println ("File Description: "
+ String.valueOf (header.getFileDescription()));
System.out.println ("Header Size: " + header.getHeaderSize());
System.out.println ("File Size: " + header.getFileSize());
System.out.println ("Decompressed Size: "
+ header.getDecompressedSize());
System.out.println ("Replay Version: " + header.getReplayVersion());
System.out.println ("Number of Compressed Data Blocks: "
+ header.getDataBlocksCount());
System.out.println ("Game Patch Version: "
+ sub_head.getGamePatchVersion());
System.out.println ("Game Build: " + sub_head.getGameBuildNumber());
System.out.println ("Replay Length: " + sub_head.getReplayLength());
System.out.println ("Expansion Game:: " + sub_head.isExpansion());
System.out.println ("Multiplayer: " + sub_head.isMultiplayer());
System.out.println ("Checksum: " + sub_head.getChecksum());
}
catch (IOException exc) { System.out.println (exc.getStackTrace()); }
}
}
I believe the replay data is compressed using zlib, so I will work on decompressing and displaying the data blocks next.
What is the reasoning behind creating your own ByteBuffer?
Quote from: dxoigmn on July 11, 2004, 04:38 PM
What is the reasoning behind creating your own ByteBuffer?
I have been working on my own library for quite some time now. No real reasoning behind it other than learning. Basically I do it for shits and giggles and an occasional brain cell addition. ;)
Quote from: Tuberload on July 11, 2004, 04:40 PM
Quote from: dxoigmn on July 11, 2004, 04:38 PM
What is the reasoning behind creating your own ByteBuffer?
I have been working on my own library for quite some time now. No real reasoning behind it other than learning. Basically I do it for shits and giggles and an occasional brain cell addition. ;)
Heh. I was going to say the ByteBuffer in java.nio is pretty robust; but whatever floats your boat ;)
If you know where to get the 1.06 patch from or if 1.00 will work I'll reinstall and run a game for you.
NVM I think I found the patch on the blizzard FTP I'm gonna uninstall and reinstall now.
Quote from: muert0 on July 11, 2004, 05:30 PM
If you know where to get the 1.06 patch from or if 1.00 will work I'll reinstall and run a game for you.
NVM I think I found the patch on the blizzard FTP I'm gonna uninstall and reinstall now.
1.00 will work, and thanks.
Thanks to meurt0 I was able to test it on versions previous to 1.07 and it works fine.
On a side note to anyone who may be interested, while that documentation is a very good start I have been running a crossed errors and inconsistency in the data so you may have to figure a few things out.
I will be writing my own documentation for the file format as I figure it out. I figure it is a good enough place to start learning such a thing.
This is a bump, but did you ever get around to making the viewer? Some people on my forum were asking for one today.
I did, but imcomplete.
Are you still working on it?
Slowly but surely
I am able to parse the headers (code is available here), and I was able to decompress the data blocks and read in their headers as well. From there is a bunch of Actions and such that you use to display a replay. From what I understand there is no actual movie formatted data saved anywhere?
I will release an updated version in Java shortly. Maybe from there I can make my own simple 2D player.
from what I understand the replays only excute the action which is why the 1.07+ are diffrent.
So unless you drew from the game I have no idea how you would make a movie maker. I am not a very smart person though so I have no idea.
What I meant is there is no compressed movie data present in the replay files so you can not just pull out the data and pass it to a media player. You have to produce the movie yourself based on the actions present in the replay I believe.
I am by no means an expert on this subject, but during the brief period I worked on it that is what I discovered.
Although there could possibly be a library present that will play the movie when the actions are passed to it. This is of course complete speculation due to the fact that I have not looked into it, and currently do not posses the knowledge required to find out.
Correct, if you want to have a movie, then you'll have to come up with the graphics to cooperate with the actions in the replay file (Which would be a lot of work).
Why not just make something to capture movies from War3?
Couldn't you just display the units from the MPQ files?
Quote from: Noodlez on September 14, 2004, 10:01 PM
Couldn't you just display the units from the MPQ files?
If you feel like figuring that out and sharing it with us, ok :)
Quote from: Noodlez on September 14, 2004, 10:01 PM
Couldn't you just display the units from the MPQ files?
At that point, how much harder is it to just make another Warcraft-III game? :P
Quote from: MyndFyre on September 15, 2004, 07:36 PM
Quote from: Noodlez on September 14, 2004, 10:01 PM
Couldn't you just display the units from the MPQ files?
At that point, how much harder is it to just make another Warcraft-III game? :P
If I were to try and make my own replay player it would use crude 2D images that looked nothing like they should. It would be up to the user and my documentation to figure the rest out. :) Other than that I think Adron has the best solution.
It's not hard at all... there are open source programs that do it already.
Quote from: Noodlez on September 18, 2004, 05:31 PM
It's not hard at all... there are open source programs that do it already.
Such as?
By the way, my suggestion in re: to the comment, "Couldn't you just display the units from the MPQ files?" was that, if you're going to go through the trouble to display the fully-3D graphics extracted from the MPQ files, you might as well make the full game, because you're already practically there.
But that's dumb. :P
Minus all of the AI algorithms and such.