On router i have forwarded the ports zero hour use, firewall still up. Ingame options i have no port number in the firewall port override box, because it's already fixed in router and windows firewall. This fixed the problem on my win7 and on my brothers win10.
i dont get it if i already forwarded the ports in my router and firewall allows the 4 generals that i have..why it still disconnecting..i guess ill need to insert the ports in the advanced settings in firewall..altho i have it in options showing the port 29900 and everything looks fine...i used to play before once every 6 7 games, now i cannot play at all! very weird!
TL/DR: In this post we'll examine C&C Generals replay (.rep) files in detail, figure out how they work, and then look at a very early implementation of a replay viewer that I've added to OpenSAGE.
Have you ever saved a replay from [insert your favourite video game here], and wondered exactly what the replay file contains? I don't know how many people will answer "yes" to that question, but I'm one of them. For efficiency and IP-protection reasons, replay files are usually stored in a binary format, so if you open one of them in Notepad, all you'll see are a bunch of garbled characters. It's often a long road from there, to fully understanding and being able to parse one of these files.
One of the features I hope to support in OpenSAGE is being able to use replay files from the original Command & Conquer: Generals and Zero Hour games. Since no official description of Generals' replay file format has been published, I have to do it the hard way, and figure out the file format... but fortunately, I'm not the first to want to do this.
I should note here that replay files vary wildly between games, so what applies to one specific RTS game won't apply directly to other games, but the general principles should be similar, at least across RTS games.
Let's load C&C Generals, and play a quick skirmish game. For the purposes of this blog post, I want to keep the replay file really simple, so all I'll do is start a single-player skirmish game, build one building, and exit. Here's what that looks like:
It was at this point, when I first looked at Generals' replay files, that I wondered whether anybody else had figured out the file format before. So I Googled and found the cnc-replayreaders repo on GitHub, which contains at least some work on parsing replay files for many SAGE games, including, happily, C&C Generals. Without this information, proceeding further would have been much harder. Here's the C++ code for a prototype version of a Generals replay parser. There's even a document with the beginnings of a spec for these replay files. This is great stuff! Many thanks to louisdx, who I believe also goes by the name R Schneider on gamereplays.org. I'll duplicate the Generals part of it here:
The replay format of the games Generals, Zero Hour, Battle for Middle Earth, Battle for Middle Earth 2, and its expansion Rise of the Witch King is considerably simpler than that of the later games. Most notably, the chunks do not have length information, so one must know their sizes by other means. Also, there is no footer, just a final chunk.
In the simplest case, number_of_commands is zero and signature and arguments are empty. Otherwise, there are number_of_commands many pairs of bytes cmd,nargs, where cmd refers to one of about ten commands and nargs is the number of arguments. The size of an argument depends on the command type. Finally, arguments consists of all the arguments of all the commands, simply one after the other.The meaning of the chunk codes is unknown in general. Only a handful of values seem to occur, all in the range 0x300 - 0x500, and 0x1B, 0x1D. Many codes only ever seem to appear with a fixed, specific signature of commands.These are the known commands and their argument sizes.
Armed with that information, let's get started with parsing the header. I'll use C# here, because that's what we're using in the OpenSAGE project, but really any language that gives you the ability to read from a byte stream will work.
So, the header spec in the cnc-replayreaders project looks pretty accurate. I've worked out that one of the unknowns is the game speed. I don't know about the others, but by comparing enough replays with known game states, it should be possible to work them out.
This already gives us quite a lot of interesting data, such as the map, player details, start and end times, and even the date that the version of Generals that I'm using was compiled (September 6th 2012, apparently).
There are many ways game replay data can be saved. If there aren't too many "things" in the world to keep track of, you could store the entire state of the world at every timestep. For RTS games with potentially hundreds or thousands of units, this wouldn't scale - and so these games usually only save player inputs, and use those inputs to recreate the entire simulation. (This is also why you often can't rewind replays; you'd have to start from the beginning again, and simulate every frame up to the one you're interested in, in order to get the correct state for that frame.)
Knowing that, what I'm expecting to see in the body of the replay file is a sequence of commands (or "orders", as I've chosen to call them), roughly corresponding to the actions I performed in the game. For example, "select unit" and "create power plant". Each of those orders would have arguments relevant to the order type. For example, "select unit" would have some sort of identifier to store which unit was selected.
The body of a replay file is composed of n chunks. There's no way to know how many chunks there are from the header - after reading the header, you just have to keep reading chunks until you get to the end of the file. Like this:
argumentCounts stores the number of arguments for each argument type. So there might be 2 integer arguments, 1 boolean argument, and 1 float argument, for example. We can calculate the total number of arguments by summing the counts of each argument type.
There's nothing in the chunk header that directly tells us how big the argument data array is. We need to derive it from the number and type of arguments. I worked out the argument types from looking at a few replay files in a hex viewer and making some educated guesses. This fills in the gaps in the table of commands that I quoted above. Here's what I've found so far, although I'm sure there are more:
Armed with all that code, we can finally parse our sample replay file. Let's do that, and write out all the replay chunks from our sample replay file. I've trimmed it down, because the replay file contains 733 lines - i.e. 733 orders - most of which are duplicate 1092 orders. I've marked the trimmed lines with "...".
As I mentioned at the beginning, my motivation for parsing Generals replay files is being to play them in OpenSAGE. So, I implemented just enough in OpenSAGE to be able to parse and "play" this simple replay file, and here is the result. It's obviously extremely unfinished, but hopefully you can see the connection with the original replay. You can also see our work on the main menu, replay menu, map rendering, etc...
The code for "playing" a replay iterates through the chunks, with the appropriate timing, and executes each chunk's order - for example, creating a building or setting the camera's position. There's a huge amount still to do. There are some mysteries that will have to be solved, such as the random number generator (RNG) used by Generals, so there are no guarantees that we'll really be able to do it - but if I worried too much about that, I wouldn't have started OpenSAGE in the first place. On the bright side, much of the code for running through a replay will carry over, as-is, to actually playing single-player and multi-player games.
Hopefully that gives a rough picture of how one goes about parsing a binary file format where you know roughly what it should contain, but you don't know the exact structure. Again, many thanks to louisdx for the cnc-replayreaders project. If I wasn't able to build on that existing knowledge, this would have been a lot more difficult.
OpenSAGE is not associated with Electronic Arts or any other company. All trademarks are property of their respective owners. Screenshots of copyrighted works are only used for demonstration purposes.
b1e95dc632