Hello there,
I would like to make a tool to look at save files. Rather than try to reverse-engineer the file, it would be so much easier to just see the code that generates the save file. I found:
virtual void SaveGame(CvString& szFilename, SaveGameTypes eType = SAVEGAME_NORMAL) = 0;
in CvDLLEngineIFaceBase.h, but I can't seem to find the code for this function. I have the steam version, so I'm relying on downloaded files (from here:
https://forums.civfanatics.com/resources/civ4colonization-cvgamecoredll.23339/). I may not have the necessary file at the moment. Finding this for the base game would be a good start to get the general format of the file, but I would eventually like to figure this out for a modded version of Colonization (RaR specifically).
If someone could point me in the right direction, I would really appreciate it.
Thanks,
Grant
CvDLLEngineIFaceBase.h contains functions located in the exe file, meaning we do not actually have the save function itself. Luckily it's not that difficult to figure out with debugging and reading the savegame with a hex editor. Even better, there is no encryption, compression or anything like that.
The savegame is as follows:
Some header containing name of mod, exe version and possibly something else. The good part about this is that it appears to have a fixed length. For your purpose you can likely get away with ignoring the first X bytes.
CvInitCore
CvGameAI
CvMap
CvTeamAI for each team (MAX_PLAYERS)
CvPlayerAI for each player (MAX_PLAYERS)
Each of those classes have functions like
PHP:
void CvInitCore::read(FDataStreamBase* pStream)
void CvInitCore::write(FDataStreamBase* pStream)
Those functions do call read/write in other classes, like cities are saved inside the owner, meaning CvPlayerAI::read will call CvCityAI::read.
pStream has a bunch of overloaded Read() and Write() functions. They do what you would expect, which is to read or write variables. If the argument is an int, it will read 4 bytes. If it's a short, it will read 2 bytes and so on. When given two arguments, it will read an array. First argument is length and the second is a pointer to where it should be stored in memory. In other words the byte size for the array element depends on the type of pointer.
There are also strings, which are stored as plain text. CvString use one byte for each character while CvWString use 2 bytes for each character.
I think that's pretty much it. Since we have access to the savegame generating functions, we can modify the savegame pretty much as we like, including adding tags for you to hook on to. In fact we can change the format completely and save as xml if we feel like it. The tradeoff from doing that would be slower saving and bigger files.
I don't think it will be possible to make a savegame tool, which fits all mods because the savegame is just a bunch of bytes. The meaning of each byte is hardcoded in the DLL file using functions, which are often modified in all mods with a custom DLL.
EDIT: the format is actually also depending on xml files. It stores ints like unit is of UnitType 4 and then it requires looking in the xml file what type 4 is. Also in some cases there is an array of length (length of xml file).