Impaler[WrG] said:
Theirs been some discussion on if and how we will maintain backward savegame compatability. Ofcorse were all for backwards compatability so long as its doable. I've resently spent some time beating my head against the problem made no progress and started wondering if anyone else has done work on it.
I have some Mods which add new data to the game which realy must be saved, I can manage to do this with the pStream->Write/Read functions. Bute these crash any game that was saved under a different DLL. A flag for skipping reads needs to be included for each variable which isn't hard to do, the trick is ofcorse getting what dll version a game was saved under. I dont think Civ saves any kind of DLL version number in its save games but it definatly saves information about Mods, thus alowing it to throw warnings when theirs a mismatch.
So what should our strategy for backwards compatability be, piggybacking the DLL on the Mod name and requiring both to match before loading. Or recording the dll version in the game so pStream->read can be conditionaly skipped and Mod options properly configued when you load up an old save. I would like to see the DLL version used but how to read it without crashing the game too? Any Ideas?
I alread implemented some backward compatible save system.
it will read original (Firaxis) save file, Moded save file of curret mod version and previous mod version (backward by only one version only)
This is done by following method.
It has a C struct to store new variables added to the mod.
First, before I read my struct from file, I save the file position in the stream.
Then I read the struct from file stream and check if it is modded save file or original firaxis save file. A magic maker is used to do this..
If the magic marker is missing, it is firaxis save file, so I rewind file stream to file position saved before. I stop reading stream and the C struct is filled with default values.
If the magic marker is found, it is modded savefile.
Then I check version number, it version number matches with current version, it is OK to go.
If it version number maches with old version number, then rewind to saved file position like in case of Friaxis, and re-read with old version C struct. and old date is converted to current C struct.
If both version failes, this save file is too old version, then the loading is aborted and exit the game.
Here is code..
Code:
// This is called by CvGame::read() of "CvGame.cpp": time to load global data.
void CvExt_read(FDataStreamBase* pStream)
{
struct old_global_ext_st // global variables saved in savefile.
{
int iGlobalExtSize; // size of this struct global_ext_st
int iSaveVersion; // version
int iSaveMagic; // save file magic maker
int iSaveFlags; // Save flags : unused
int reserved1[4]; // for future use
int iExtSizeTable[8]; // size of various struct ext_st
int iNumUnitCreated; // Total created unit count; used for UID counting
int reserved2[15]; // for future use
} old_Ext;
FAssertMsg(sizeof g_Ext == EXTSAVE_GLOBALEXT_SIZE,
__FUNCTION__ ": Global data extension save size" );
FAssertMsg(sizeof CvUnit::unit_ext_st == EXTSAVE_UNITEXT_SIZE,
__FUNCTION__": Unit data extension save size" );
srand((int)time(NULL));
memset(&g_Ext, 0, sizeof g_Ext);
size_t SavePos = pStream->GetPosition();
pStream->Read( sizeof g_Ext, (byte *) &g_Ext);
if ( g_Ext.iSaveMagic != EXTSAVE_MAGIC ) // Sanity check
{ // Firaxis original save
#ifdef SIMCUTIE_UNITEXT
CvUnit::cm_eExtLoad = EXLD_BASE;
#endif // SIMCUTIE_UNITEXT
pStream->SetPosition(SavePos); // rewind
g_Ext.iNumUnitCreated = 0; // *TODO*
}
else
{
FAssertMsg( sizeof g_Ext >= g_Ext.iGlobalExtSize,
__FUNCTION__ ": Loaded Global data extension size" );
#ifdef SIMCUTIE_UNITEXT
FAssertMsg( sizeof CvUnit::unit_ext_st >= g_Ext.EXTSAVESIZE(UNIT),
__FUNCTION__ ": Loaded CvUnit extension data size" );
#endif // SIMCUTIE_UNITEXT
if ( g_Ext.iGlobalExtSize == sizeof(g_Ext)
&& g_Ext.iSaveVersion == EXTSAVE_VERSION )
{ // OK. Extension save file, current version.
#ifdef SIMCUTIE_UNITEXT
CvUnit::cm_eExtLoad = EXLD_LOAD;
#endif // SIMCUTIE_UNITEXT
}
else if ( g_Ext.iGlobalExtSize == sizeof(old_Ext)
&& g_Ext.iSaveVersion == EXTSAVE_VERSION_OLD )
{ // old version.
pStream->SetPosition(SavePos);
pStream->Read( sizeof old_Ext, (byte *) &old_Ext);
g_Ext.iNumUnitCreated = old_Ext.iNumUnitCreated;
#ifdef SIMCUTIE_UNITEXT
CvUnit::cm_eExtLoad = EXLD_CONVERT;
#endif // SIMCUTIE_UNITEXT
}
else
{ // unsupported save version, die...
FAssertMsg( 0, __FUNCTION__ ": Unsuppoted save version" );
ReportError( "Unsupported save version: %d ", g_Ext.iSaveVersion);
// gDLL->getInterfaceIFace()->exitingToMainMenu();
// GC.getGameINLINE().doControl(CONTROL_LOAD_GAME);
// gDLL->MessageBox( "Loading Save file failed: version mismatc", "Loading Error" );
throw std::runtime_error("Loading Save file failed: version mismatch");
// exit(1);
return;
}
}
}