What is the SaveGame (ZDS) Format?
Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. This forum is archived - please use this set of forums to ask new questions.
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. This forum is archived - please use this set of forums to ask new questions.
- Bio Hazard
- Posts: 4019
- Joined: Fri Aug 15, 2003 8:15 pm
- Location: ferret ~/C/ZDL $
- Contact:
What is the SaveGame (ZDS) Format?
I know it's just a PNG with some extra stuff, but what is this "extra stuff"?
What data is it possible to extract? I'd like to get as much as I can (health, ammo, pwads, etc...)
I want to write a savegame viewer/manager but I'm totally stumped with this format thing.
Also, could someone point me in the direction of info about decoding and displaying PNG's in an application? I've never used 3rd-party libraries with VCC yet.
What data is it possible to extract? I'd like to get as much as I can (health, ammo, pwads, etc...)
I want to write a savegame viewer/manager but I'm totally stumped with this format thing.
Also, could someone point me in the direction of info about decoding and displaying PNG's in an application? I've never used 3rd-party libraries with VCC yet.
- Graf Zahl
- Lead GZDoom+Raze Developer
- Posts: 49230
- Joined: Sat Jul 19, 2003 10:19 am
- Location: Germany
Most of the data will be rather hard to extract. It uses a standard serializing method that makes it nearly impossible to get all the stuff back out without using ZDoom's original code. You'd practically have to rip out all of ZDoom's savegame code and put it into your program to be able to analyze it.
As for PNG decoding, there's libpng but to get a start I recommend to take a look at ZDoom's m_png.cpp file but it is limited to 8 bit graphics.
As for PNG decoding, there's libpng but to get a start I recommend to take a look at ZDoom's m_png.cpp file but it is limited to 8 bit graphics.
Perhaps you could switch to a new method, saving each structure as a sequence of key/values pairs, in a chunked format where each global array has its own chunk (mobjs, sectors, active buttons, etc).Graf Zahl wrote:Most of the data will be rather hard to extract. It uses a standard serializing method that makes it nearly impossible to get all the stuff back out without using ZDoom's original code.
This would make savegames fairly future-proof, as it can handle structure fields being added/removed, even whole arrays. Lot of work though (speaking from experience

- Graf Zahl
- Lead GZDoom+Raze Developer
- Posts: 49230
- Joined: Sat Jul 19, 2003 10:19 am
- Location: Germany
But the current system works differently. If it serializes a pointer to an actor or thinker it doesn't just save an index. If the object being referenced hasn't been saved yet it is serialized recursively, otherwise a reference in the savegame's index list is created. When loaded back the objects are being created when they are read from the savegame. This also implies that an object that serializes a pointer to another object can be certain that the pointer is valid immediately including all other objects it directly and indirectly references.
Large parts of the savegame code had to be altered completely if you wanted to do it by index alone.
For stuff like sectors, lindefs etc only an index is stored because those are static data.
Large parts of the savegame code had to be altered completely if you wanted to do it by index alone.
For stuff like sectors, lindefs etc only an index is stored because those are static data.
ZDoom savegame specs
There is some information you can extract from the file without needing to parse the level snapshots. First, there are several tEXt chunks:
- Software
ZDoom <Version> - ZDoom Save Version
ZDOOMSAVExxx - Title
<Savegame name> - Current Map
<MAPxx> or <ExMy> - Game WAD
<The IWAD that was loaded when this savegame was made> - Map WAD
<The WAD that this map is from> - Creation Time
<The time this savegame was made> - Comment
<Map name>
time: <Time spent playing this map or hub> - Important CVARs
<A backslash delimited set of key/value pairs for some cvars and their settings>
- ptIc
4 bytes: The tic rate
4 bytes: The time spent on this level or hub, in tics - viSt
A list of maps that have been visited in this save:
1 byte: The length of the map's lump name
<x> bytes: The map's name
...Repeat...
1 byte: Zero terminates the list. - pcLs
A list of the classes each player is playing as:
1 byte: The player number
<x> bytes: A token stream that represents the player's class. For the first player, this will always be a byte with the value 1, a byte with the length of the class name, and the class's name. For successive players, it is either a 1 byte as described if the player is a new class. Otherwise it is a zero byte and another byte that indexes a class that was already loaded. (This is the class's index, not the player's index!)
...Repeat...
1 byte: 255 terminates the list. - wvAr (optional)
Non-zero world variables. - gvAr (optional)
Non-zero global variables. - waRr (optional)
Non-zero world arrays. - gaRr (optional)
Non-zero global arrays. - raNd
The state of all random number generators that have been used since the game was started. This is an array of CRC/Seed pairs. They don't have any particular meaning outside of the game. - snAp
Finally, this is the chunk that makes these actual savegames. There is one of these for each level you have visited in the hub (or just one if you aren't in a hub). It is usually compressed, although this can be disabled from the console. Its structure is subject to change with each version and pretty hairy to parse if you don't recreate all of the snapshot code from the game. If you're feeling adventurous, the tokens that can be present in this data stream are described in comments at the top of farchive.cpp, and the serialization process starts at G_SnapshotLevel() in g_level.cpp.
Well, it's an interesting system, but unnecessarily complex. I think the system I wrote for EDGE could have been simpler, but it does the "future proofing" I described above. If you're interested, a description (partly out-of-date) is here:Graf Zahl wrote:But the current system works differently. If it serializes a pointer to an actor or thinker it doesn't just save an index. If the object being referenced hasn't been saved yet it is serialized recursively, otherwise a reference in the savegame's index list is created.
http://cvs.sourceforge.net/viewcvs.py/e ... xt?rev=1.2
EDIT: the above idea (using key/value pairs in chunks) seemed promising since it was exactly the same as the new map format (discussed elsewhere). I was thinking it might be possible to create a common savegame spec (used by multiple ports). Pie in the sky?
- Graf Zahl
- Lead GZDoom+Raze Developer
- Posts: 49230
- Joined: Sat Jul 19, 2003 10:19 am
- Location: Germany
I agree. But I can't deny that once set up it is really easy to use because all the dirty stuff is done deep inside the savegame code!Ajapted wrote:Well, it's an interesting system, but unnecessarily complex. I think the system I wrote for EDGE could have been simpler, but it does the "future proofing" I described above. If you're interested, a description (partly out-of-date) is here:Graf Zahl wrote:But the current system works differently. If it serializes a pointer to an actor or thinker it doesn't just save an index. If the object being referenced hasn't been saved yet it is serialized recursively, otherwise a reference in the savegame's index list is created.
- Bio Hazard
- Posts: 4019
- Joined: Fri Aug 15, 2003 8:15 pm
- Location: ferret ~/C/ZDL $
- Contact:
Re: ZDoom savegame specs
How do I read them? are they zero-teminated? length byte? fixed length?randy wrote:there are several tEXt chunks:
Hmm, I'll have to play with this a bit...randy wrote:Then there are some binary chunks:
Does all this stuff happen before or after the image data?