diff --git a/src/common/engine/i_interface.cpp b/src/common/engine/i_interface.cpp index 338f36c6c..a78b5b30f 100644 --- a/src/common/engine/i_interface.cpp +++ b/src/common/engine/i_interface.cpp @@ -6,6 +6,7 @@ #include "gstrings.h" #include "version.h" #include "m_argv.h" +#include "m_random.h" static_assert(sizeof(void*) == 8, "Only LP64/LLP64 builds are officially supported. " @@ -182,3 +183,12 @@ int FStartupSelectionInfo::SaveInfo() return DefaultIWAD; } +FString GameUUID; +static FRandom pr_uuid("GameUUID"); + +FString GenerateUUID() +{ + FString uuid; + uuid.AppendFormat("%08X-%04X-4%03X-9%03X-%08X%04X", pr_uuid.GenRand32(), pr_uuid(UINT16_MAX), pr_uuid(4095), pr_uuid(4095), pr_uuid.GenRand32(), pr_uuid(UINT16_MAX)); + return uuid; +} diff --git a/src/common/engine/i_interface.h b/src/common/engine/i_interface.h index e901705f2..ecff3f6b1 100644 --- a/src/common/engine/i_interface.h +++ b/src/common/engine/i_interface.h @@ -13,6 +13,9 @@ class FConfigFile; class FArgs; struct FTranslationID; +extern FString GameUUID; +FString GenerateUUID(); + struct SystemCallbacks { bool (*G_Responder)(event_t* ev); // this MUST be set, otherwise nothing will work diff --git a/src/common/menu/savegamemanager.cpp b/src/common/menu/savegamemanager.cpp index 961a01ea6..ac041d622 100644 --- a/src/common/menu/savegamemanager.cpp +++ b/src/common/menu/savegamemanager.cpp @@ -48,6 +48,7 @@ #include "savegamemanager.h" #include "m_argv.h" #include "i_specialpaths.h" +#include "i_interface.h" CVAR(String, save_dir, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_SYSTEM_ONLY); FString SavegameFolder; @@ -191,6 +192,7 @@ void FSavegameManagerBase::NotifyNewSave(const FString &file, const FString &tit { node->SaveTitle = title; node->CreationTime = myasctime(); + node->UUID = GameUUID; node->bOldVersion = false; node->bMissingWads = false; @@ -210,6 +212,7 @@ void FSavegameManagerBase::NotifyNewSave(const FString &file, const FString &tit auto node = new FSaveGameNode; node->SaveTitle = title; node->CreationTime = myasctime(); + node->UUID = GameUUID; node->Filename = file; node->bOldVersion = false; node->bMissingWads = false; @@ -560,6 +563,27 @@ bool FSavegameManagerBase::RemoveNewSaveNode() return false; } +int FSavegameManagerBase::RemoveUUIDSaveSlots() +{ + if (GameUUID.IsEmpty()) + return -1; + + // Make sure there's any saves in the list first. + if (!SaveGames.Size()) + ReadSaveStrings(); + + int index = -1; + for (int i = SaveGames.Size() - 1; i >= 0; --i) + { + auto& save = SaveGames[i]; + if (save == &NewSaveNode || save->UUID.Compare(GameUUID)) + continue; + + index = RemoveSaveSlot(i); + } + return index; +} + DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveNewSaveNode) { PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); @@ -581,9 +605,15 @@ DEFINE_ACTION_FUNCTION(FSavegameManager, ExtractSaveData) ACTION_RETURN_INT(self->ExtractSaveData(sel)); } +DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveUUIDSaveSlots) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); + ACTION_RETURN_INT(self->RemoveUUIDSaveSlots()); +} DEFINE_FIELD(FSaveGameNode, SaveTitle); DEFINE_FIELD(FSaveGameNode, Filename); +DEFINE_FIELD(FSaveGameNode, UUID); DEFINE_FIELD(FSaveGameNode, bOldVersion); DEFINE_FIELD(FSaveGameNode, bMissingWads); DEFINE_FIELD(FSaveGameNode, bNoDelete); diff --git a/src/common/menu/savegamemanager.h b/src/common/menu/savegamemanager.h index 4dd2eb69d..a5d84d212 100644 --- a/src/common/menu/savegamemanager.h +++ b/src/common/menu/savegamemanager.h @@ -12,6 +12,7 @@ struct FSaveGameNode FString SaveTitle; FString Filename; FString CreationTime; + FString UUID; bool bOldVersion = false; bool bMissingWads = false; bool bNoDelete = false; @@ -57,6 +58,7 @@ public: FSaveGameNode *GetSavegame(int i); void InsertNewSaveNode(); bool RemoveNewSaveNode(); + int RemoveUUIDSaveSlots(); }; diff --git a/src/g_game.cpp b/src/g_game.cpp index 9c38bd5aa..1bad3c24c 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -2009,6 +2009,7 @@ void G_DoLoadGame () arc("Save Version", SaveVersion); arc("Engine", engine); arc("Current Map", map); + arc("GameUUID", GameUUID); if (engine.CompareNoCase(GAMESIG) != 0) { @@ -2386,6 +2387,7 @@ void G_DoSaveGame (bool okForQuicksave, bool forceQuicksave, FString filename, c savegameinfo.AddString("Software", buf) .AddString("Engine", GAMESIG) ("Save Version", ver) + ("GameUUID", GameUUID) .AddString("Title", description) .AddString("Current Map", primaryLevel->MapName.GetChars()); diff --git a/src/g_level.cpp b/src/g_level.cpp index 825979d4d..11d06c844 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -636,6 +636,7 @@ void G_InitNew (const char *mapname, bool bTitleLevel) players[i].playerstate = PST_ENTER; // [BC] STAT_StartNewGame(mapname); + GameUUID = GenerateUUID(); } usergame = !bTitleLevel; // will be set false if a demo diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index f701083fc..429d1c60b 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -102,6 +102,7 @@ void FSavegameManager::ReadSaveStrings() FString iwad = arc.GetString("Game WAD"); FString title = arc.GetString("Title"); FString creationtime = arc.GetString("Creation Time"); + FString uuid = arc.GetString("GameUUID"); if (engine.Compare(GAMESIG) != 0 || savever > SAVEVER) @@ -128,6 +129,7 @@ void FSavegameManager::ReadSaveStrings() FSaveGameNode *node = new FSaveGameNode; node->Filename = entry.FilePath.c_str(); + node->UUID = uuid; node->bOldVersion = oldVer; node->bMissingWads = missing; node->SaveTitle = title; diff --git a/wadsrc/static/zscript/engine/ui/menu/loadsavemenu.zs b/wadsrc/static/zscript/engine/ui/menu/loadsavemenu.zs index 82b5ea4bf..589951b2e 100644 --- a/wadsrc/static/zscript/engine/ui/menu/loadsavemenu.zs +++ b/wadsrc/static/zscript/engine/ui/menu/loadsavemenu.zs @@ -38,6 +38,7 @@ struct SaveGameNode native { native String SaveTitle; native readonly String Filename; + native readonly String UUID; native bool bOldVersion; native bool bMissingWads; native bool bNoDelete; @@ -68,6 +69,7 @@ struct SavegameManager native ui native SaveGameNode GetSavegame(int i); native void InsertNewSaveNode(); native bool RemoveNewSaveNode(); + native int RemoveUUIDSaveSlots(); }