diff --git a/doomsday/plugins/common/include/p_savedef.h b/doomsday/plugins/common/include/p_savedef.h index 9c1f96e663..64ca0e18d4 100644 --- a/doomsday/plugins/common/include/p_savedef.h +++ b/doomsday/plugins/common/include/p_savedef.h @@ -28,7 +28,7 @@ #if __JDOOM__ # define MY_SAVE_MAGIC 0x1DEAD666 # define MY_CLIENT_SAVE_MAGIC 0x2DEAD666 -# define MY_SAVE_VERSION 9 +# define MY_SAVE_VERSION 10 # define SAVESTRINGSIZE 24 # define CONSISTENCY 0x2c # define SAVEGAMENAME "DoomSav" @@ -41,7 +41,7 @@ #elif __JDOOM64__ # define MY_SAVE_MAGIC 0x1D6420F4 # define MY_CLIENT_SAVE_MAGIC 0x2D6420F4 -# define MY_SAVE_VERSION 8 +# define MY_SAVE_VERSION 10 # define SAVESTRINGSIZE 24 # define CONSISTENCY 0x2c # define SAVEGAMENAME "D64Sav" @@ -54,7 +54,7 @@ #elif __JHERETIC__ # define MY_SAVE_MAGIC 0x7D9A12C5 # define MY_CLIENT_SAVE_MAGIC 0x1062AF43 -# define MY_SAVE_VERSION 8 +# define MY_SAVE_VERSION 10 # define SAVESTRINGSIZE 24 # define CONSISTENCY 0x9d # define SAVEGAMENAME "HticSav" @@ -94,24 +94,7 @@ typedef struct targetplraddress_s { #endif #if !__JHEXEN__ -typedef struct saveheader_s { - int magic; - int version; - int gameMode; - char name[SAVESTRINGSIZE]; - byte skill; - byte episode; - byte map; - byte deathmatch; - byte noMonsters; - byte respawnMonsters; - int mapTime; - byte players[MAXPLAYERS]; - unsigned int gameId; -} saveheader_t; - #define PRE_VER5_END_SPECIALS 7 - #endif #endif diff --git a/doomsday/plugins/common/include/p_saveio.h b/doomsday/plugins/common/include/p_saveio.h index 8941432f9b..98b0de7456 100644 --- a/doomsday/plugins/common/include/p_saveio.h +++ b/doomsday/plugins/common/include/p_saveio.h @@ -29,6 +29,24 @@ #include "lzss.h" #include "p_savedef.h" +#if !__JHEXEN__ +typedef struct saveheader_s { + int magic; + int version; + int gameMode; + char name[SAVESTRINGSIZE]; + byte skill; + byte episode; + byte map; + byte deathmatch; + byte noMonsters; + byte respawnMonsters; + int mapTime; + byte players[MAXPLAYERS]; + unsigned int gameId; +} saveheader_t; +#endif + typedef struct gamesaveinfo_s { ddstring_t filePath; ddstring_t name; @@ -142,6 +160,11 @@ void SV_CopySaveSlot(int sourceSlot, int destSlot); saveptr_t* SV_HxSavePtr(void); #endif // __JHEXEN__ +/** + * Seek forward @a offset bytes in the save file. + */ +void SV_Seek(uint offset); + /* * Writing and reading values */ @@ -165,6 +188,11 @@ short SV_ReadShort(void); long SV_ReadLong(void); float SV_ReadFloat(void); +#if !__JHEXEN__ +void SV_Header_Write(saveheader_t* hdr); +void SV_Header_Read(saveheader_t* hdr); +#endif + void SV_MaterialArchive_Write(MaterialArchive* arc); void SV_MaterialArchive_Read(MaterialArchive* arc, int version); diff --git a/doomsday/plugins/common/src/p_saveg.c b/doomsday/plugins/common/src/p_saveg.c index af2237a318..97ec701ba4 100644 --- a/doomsday/plugins/common/src/p_saveg.c +++ b/doomsday/plugins/common/src/p_saveg.c @@ -4563,7 +4563,7 @@ static boolean readSaveHeader(saveheader_t *hdr, LZFILE *savefile) randomClassParm = SV_ReadByte(); #else - lzRead(hdr, sizeof(*hdr), SV_File()); + SV_Header_Read(hdr); if(hdr->magic != MY_SAVE_MAGIC) { @@ -4577,46 +4577,6 @@ static boolean readSaveHeader(saveheader_t *hdr, LZFILE *savefile) return false; // A future version. } -#if __JDOOM__ || __JHERETIC__ -# if __JDOOM__ //|| __JHEXEN__ - if(hdr->version < 9) -# elif __JHERETIC__ - if(hdr->version < 8) -# endif - { - static const gamemode_t oldGameModes[] = { -# if __JDOOM__ - doom_shareware, - doom, - doom2, - doom_ultimate -# elif __JHERETIC__ - heretic_shareware, - heretic, - heretic_extended -# elif __JHEXEN__ - hexen_demo, - hexen, - hexen_deathkings -# endif - }; - hdr->gameMode = oldGameModes[(int)hdr->gameMode]; -# if __JDOOM__ - /** - * \kludge Older versions did not differentiate between versions of - * Doom2 (i.e., Plutonia and TNT are marked as Doom2). If we detect - * that this save is from some version of Doom2, replace the marked - * gamemode with the current gamemode. - */ - if(hdr->gameMode == doom2 && (gameModeBits & GM_ANY_DOOM2)) - { - hdr->gameMode = gameMode; - } - /// kludge end. -# endif - } -#endif - if(hdr->gameMode != gameMode) { Con_Message("SV_LoadGame: Game Mode missmatch (%i!=%i), aborting load.\n", (int)gameMode, (int)hdr->gameMode); @@ -4914,7 +4874,7 @@ void SV_SaveClient(uint gameId) hdr.respawnMonsters = respawnMonsters; hdr.mapTime = mapTime; hdr.gameId = gameId; - SV_Write(&hdr, sizeof(hdr)); + SV_Header_Write(&hdr); // Some important information. // Our position and look angles. @@ -4970,7 +4930,7 @@ void SV_LoadClient(uint gameId) } Str_Free(&gameSavePath); - SV_Read(&hdr, sizeof(hdr)); + SV_Header_Read(&hdr); if(hdr.magic != MY_CLIENT_SAVE_MAGIC) { SV_CloseFile(); @@ -5133,7 +5093,7 @@ int SV_SaveGameWorker(void* ptr) hdr.gameId = SV_GenerateGameId(); for(i = 0; i < MAXPLAYERS; i++) hdr.players[i] = players[i].plr->inGame; - lzWrite(&hdr, sizeof(hdr), SV_File()); + SV_Header_Write(&hdr); // In netgames the server tells the clients to save their games. NetSv_SaveGame(hdr.gameId); diff --git a/doomsday/plugins/common/src/p_saveio.c b/doomsday/plugins/common/src/p_saveio.c index c265338bb0..5b8605f0df 100644 --- a/doomsday/plugins/common/src/p_saveio.c +++ b/doomsday/plugins/common/src/p_saveio.c @@ -371,9 +371,10 @@ static boolean readGameSaveHeaderFromFile(const ddstring_t* savePath, ddstring_t if(SV_OpenFile(Str_Text(savePath), "rp")) { saveheader_t* hdr = SV_SaveHeader(); - // Read the header. - lzRead(hdr, sizeof(*hdr), SV_File()); + + SV_Header_Read(hdr); SV_CloseFile(); + if(MY_SAVE_MAGIC == hdr->magic) { Str_Set(name, hdr->name); @@ -683,6 +684,16 @@ void SV_WriteFloat(float val) lzPutL(temp, savefile); } +void SV_Seek(uint offset) +{ + errorIfNotInited("SV_SetPos"); +#if __JHEXEN__ + saveptr.b += offset; +#else + lzSeek(savefile, offset); +#endif +} + void SV_Read(void *data, int len) { errorIfNotInited("SV_Read"); @@ -772,6 +783,39 @@ static void swd(Writer* w, const char* data, int len) SV_Write(data, len); } +#if !__JHEXEN__ +void SV_Header_Write(saveheader_t* hdr) +{ + Writer* svWriter = Writer_NewWithCallbacks(swi8, swi16, swi32, swf, swd); + ddstring_t name; + + Writer_WriteInt32(svWriter, hdr->magic); + Writer_WriteInt32(svWriter, hdr->version); + Writer_WriteInt32(svWriter, hdr->gameMode); + + Str_InitStatic(&name, hdr->name); + Str_Write(&name, svWriter); + + Writer_WriteByte(svWriter, hdr->skill); + Writer_WriteByte(svWriter, hdr->episode); + Writer_WriteByte(svWriter, hdr->map); + Writer_WriteByte(svWriter, hdr->deathmatch); + Writer_WriteByte(svWriter, hdr->noMonsters); + Writer_WriteByte(svWriter, hdr->respawnMonsters); + Writer_WriteInt32(svWriter, hdr->mapTime); + + { int i; + for(i = 0; i < MAXPLAYERS; ++i) + { + Writer_WriteByte(svWriter, hdr->players[i]); + }} + + Writer_WriteInt32(svWriter, hdr->gameId); + + Writer_Delete(svWriter); +} +#endif + void SV_MaterialArchive_Write(MaterialArchive* arc) { Writer* svWriter = Writer_NewWithCallbacks(swi8, swi16, swi32, swf, swd); @@ -809,6 +853,93 @@ static void srd(Reader* r, char* data, int len) SV_Read(data, len); } +#if !__JHEXEN__ +void SV_Header_Read(saveheader_t* hdr) +{ + Reader* svReader = Reader_NewWithCallbacks(sri8, sri16, sri32, srf, srd); + + hdr->magic = Reader_ReadInt32(svReader); + hdr->version = Reader_ReadInt32(svReader); + hdr->gameMode = Reader_ReadInt32(svReader); + + if(hdr->version >= 10) + { + ddstring_t buf; + Str_InitStd(&buf); + Str_Read(&buf, svReader); + memcpy(hdr->name, Str_Text(&buf), SAVESTRINGSIZE); + hdr->name[SAVESTRINGSIZE] = '\0'; + Str_Free(&buf); + } + else + { + // Older formats use a fixed-length name (24 characters). + Reader_Read(svReader, hdr->name, SAVESTRINGSIZE); + } + hdr->skill = Reader_ReadByte(svReader); + hdr->episode = Reader_ReadByte(svReader); + hdr->map = Reader_ReadByte(svReader); + hdr->deathmatch = Reader_ReadByte(svReader); + hdr->noMonsters = Reader_ReadByte(svReader); + hdr->respawnMonsters = Reader_ReadByte(svReader); + + // Older formats serialize the unpacked header struct; skip the junk values (alignment). + if(hdr->version < 10) SV_Seek(2); + + hdr->mapTime = Reader_ReadInt32(svReader); + + { int i; + for(i = 0; i < MAXPLAYERS; ++i) + { + hdr->players[i] = Reader_ReadByte(svReader); + }} + hdr->gameId = Reader_ReadInt32(svReader); + + Reader_Delete(svReader); + + // Translate gameMode identifiers from older save versions. +#if __JDOOM__ || __JHERETIC__ +# if __JDOOM__ //|| __JHEXEN__ + if(hdr->version < 9) +# elif __JHERETIC__ + if(hdr->version < 8) +# endif + { + static const gamemode_t oldGameModes[] = { +# if __JDOOM__ + doom_shareware, + doom, + doom2, + doom_ultimate +# elif __JHERETIC__ + heretic_shareware, + heretic, + heretic_extended +# elif __JHEXEN__ + hexen_demo, + hexen, + hexen_deathkings +# endif + }; + hdr->gameMode = oldGameModes[(int)hdr->gameMode]; +# if __JDOOM__ + /** + * \kludge Older versions did not differentiate between versions of + * Doom2 (i.e., Plutonia and TNT are marked as Doom2). If we detect + * that this save is from some version of Doom2, replace the marked + * gamemode with the current gamemode. + */ + if(hdr->gameMode == doom2 && (gameModeBits & GM_ANY_DOOM2)) + { + hdr->gameMode = gameMode; + } + /// kludge end. +# endif + } +#endif +} +#endif + void SV_MaterialArchive_Read(MaterialArchive* arc, int version) { Reader* svReader = Reader_NewWithCallbacks(sri8, sri16, sri32, srf, srd);