From 723b210c9539b6af6007ac4eab624977ae1ad054 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 27 Nov 2019 00:41:26 +0100 Subject: [PATCH] - major work on savegame code Not tested yet! * Added a JSON-based header to the savegames so that the unified menu can read from a common data source. * moved loading and saving of frontend independent data to the wrapper so that support is automatic. --- source/blood/src/blood.h | 2 + source/blood/src/loadsave.cpp | 13 +- source/blood/src/loadsave.h | 4 +- source/blood/src/menu.cpp | 30 ++-- source/build/include/baselayer.h | 8 + source/common/initfs.cpp | 2 +- source/common/menu/loadsavemenu.cpp | 48 +++--- source/common/savegamehelp.cpp | 211 +++++++++++++++++++++++++- source/common/savegamehelp.h | 10 ++ source/common/version.h | 21 ++- source/duke3d/src/d_menu.cpp | 7 + source/duke3d/src/duke3d.h | 1 + source/duke3d/src/game.cpp | 4 +- source/duke3d/src/menus.cpp | 66 +------- source/duke3d/src/osdcmds.cpp | 8 - source/duke3d/src/savegame.cpp | 223 +++------------------------- source/duke3d/src/savegame.h | 5 - source/rr/src/duke3d.h | 1 + source/rr/src/menus.cpp | 15 +- source/rr/src/osdcmds.cpp | 9 -- source/rr/src/savegame.cpp | 203 +++---------------------- source/rr/src/savegame.h | 3 - source/sw/src/game.h | 1 + source/sw/src/menus.cpp | 6 + source/sw/src/save.cpp | 9 ++ 25 files changed, 371 insertions(+), 539 deletions(-) diff --git a/source/blood/src/blood.h b/source/blood/src/blood.h index 166f05f61..71de1cbc5 100644 --- a/source/blood/src/blood.h +++ b/source/blood/src/blood.h @@ -92,6 +92,8 @@ struct GameInterface : ::GameInterface void set_hud_scale(int size) override; bool mouseInactiveConditional(bool condition) override; FString statFPS() override; + FSavegameInfo GetSaveSig() override; + }; END_BLD_NS diff --git a/source/blood/src/loadsave.cpp b/source/blood/src/loadsave.cpp index 9ea75dd63..0c0ab264c 100644 --- a/source/blood/src/loadsave.cpp +++ b/source/blood/src/loadsave.cpp @@ -46,8 +46,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "sound.h" #include "i_specialpaths.h" #include "view.h" -#include "statistics.h" -#include "secrets.h" #include "savegamehelp.h" BEGIN_BLD_NS @@ -102,7 +100,7 @@ void LoadSave::Write(void *pData, int nSize) ThrowError("File error #%d writing save file.", errno); } -void LoadSave::LoadGame(char *pzFile) +void LoadSave::LoadGame(const char *pzFile) { bool demoWasPlayed = gDemo.at1; if (gDemo.at1) @@ -128,8 +126,6 @@ void LoadSave::LoadGame(char *pzFile) rover->Load(); rover = rover->next; } - if (!ReadStatistics() || !SECRET_Load()) // read the rest... - ThrowError("Error loading save file."); hLFile.Close(); FinishSavegameRead(); @@ -194,7 +190,7 @@ void LoadSave::LoadGame(char *pzFile) //sndPlaySong(gGameOptions.zLevelSong, 1); } -void LoadSave::SaveGame(char *pzFile) +void LoadSave::SaveGame(const char *pzFile) { OpenSaveGameForWrite(pzFile); hSFile = WriteSavegameChunk("snapshot.bld"); @@ -211,8 +207,9 @@ void LoadSave::SaveGame(char *pzFile) dword_27AA38 = 0; rover = rover->next; } - SaveStatistics(); - SECRET_Save(); + auto & li = gEpisodeInfo[gGameOptions.nEpisode].at28[gGameOptions.nLevel]; + G_WriteSaveHeader(gGameOptions.szUserGameName, li.at0, li.at90); + FinishSavegameWrite(); hSFile = NULL; } diff --git a/source/blood/src/loadsave.h b/source/blood/src/loadsave.h index 3da580a62..e8d4cf00a 100644 --- a/source/blood/src/loadsave.h +++ b/source/blood/src/loadsave.h @@ -49,8 +49,8 @@ public: virtual void Load(void); void Read(void *, int); void Write(void *, int); - static void LoadGame(char *); - static void SaveGame(char *); + static void LoadGame(const char *); + static void SaveGame(const char *); }; extern unsigned int gSavedOffset; diff --git a/source/blood/src/menu.cpp b/source/blood/src/menu.cpp index 2cd2194bf..bd74f2ca7 100644 --- a/source/blood/src/menu.cpp +++ b/source/blood/src/menu.cpp @@ -43,6 +43,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "view.h" #include "cmdlib.h" #include "i_specialpaths.h" +#include "savegamehelp.h" EXTERN_CVAR(Bool, hud_powerupduration) @@ -2069,7 +2070,6 @@ short gQuickSaveSlot = -1; void SaveGame(CGameMenuItemZEditBitmap *pItem, CGameMenuEvent *event) { - char strSaveGameName[BMAX_PATH]; int nSlot = pItem->at28; if (gGameOptions.nGameType > 0 || !gGameStarted) return; @@ -2078,21 +2078,21 @@ void SaveGame(CGameMenuItemZEditBitmap *pItem, CGameMenuEvent *event) gGameMenuMgr.Deactivate(); return; } - snprintf(strSaveGameName, BMAX_PATH, "%sgame00%02d.sav", M_GetSavegamesPath().GetChars(), nSlot); + FStringf basename("save%04d", nSlot); + auto strSaveGameName = G_BuildSaveName(basename); strcpy(gGameOptions.szUserGameName, strRestoreGameStrings[nSlot]); - sprintf(gGameOptions.szSaveGameName, "%s", strSaveGameName); + sprintf(gGameOptions.szSaveGameName, "%s", strSaveGameName.GetChars()); gGameOptions.nSaveGameSlot = nSlot; viewLoadingScreen(2518, "Saving", "Saving Your Game", strRestoreGameStrings[nSlot]); videoNextPage(); gSaveGameNum = nSlot; - LoadSave::SaveGame(strSaveGameName); + LoadSave::SaveGame(strSaveGameName.GetChars()); gQuickSaveSlot = nSlot; gGameMenuMgr.Deactivate(); } void QuickSaveGame(void) { - char strSaveGameName[BMAX_PATH]; if (gGameOptions.nGameType > 0 || !gGameStarted) return; /*if (strSaveGameName[0]) @@ -2100,9 +2100,11 @@ void QuickSaveGame(void) gGameMenuMgr.Deactivate(); return; }*/ - snprintf(strSaveGameName, BMAX_PATH, "%sgame00%02d.sav", M_GetSavegamesPath().GetChars(), gQuickSaveSlot); + FStringf basename("save%04d", gQuickSaveSlot); + auto strSaveGameName = G_BuildSaveName(basename); + strcpy(gGameOptions.szUserGameName, strRestoreGameStrings[gQuickSaveSlot]); - sprintf(gGameOptions.szSaveGameName, "%s", strSaveGameName); + sprintf(gGameOptions.szSaveGameName, "%s", strSaveGameName.GetChars()); gGameOptions.nSaveGameSlot = gQuickSaveSlot; viewLoadingScreen(2518, "Saving", "Saving Your Game", strRestoreGameStrings[gQuickSaveSlot]); videoNextPage(); @@ -2116,11 +2118,11 @@ void QuickSaveGame(void) void LoadGame(CGameMenuItemZEditBitmap *pItem, CGameMenuEvent *event) { UNREFERENCED_PARAMETER(event); - char strLoadGameName[BMAX_PATH]; int nSlot = pItem->at28; if (gGameOptions.nGameType > 0) return; - snprintf(strLoadGameName, BMAX_PATH, "%sgame00%02d.sav", M_GetSavegamesPath().GetChars(), nSlot); + FStringf basename("save%04d", nSlot); + auto strLoadGameName = G_BuildSaveName(basename); if (!FileExists(strLoadGameName)) return; viewLoadingScreen(2518, "Loading", "Loading Saved Game", strRestoreGameStrings[nSlot]); @@ -2132,10 +2134,11 @@ void LoadGame(CGameMenuItemZEditBitmap *pItem, CGameMenuEvent *event) void QuickLoadGame(void) { - char strLoadGameName[BMAX_PATH]; + if (gGameOptions.nGameType > 0) return; - snprintf(strLoadGameName, BMAX_PATH, "%sgame00%02d.sav", M_GetSavegamesPath().GetChars(), gQuickLoadSlot); + FStringf basename("save%04d", gQuickSaveSlot); + auto strLoadGameName = G_BuildSaveName(basename); if (!FileExists(strLoadGameName)) return; viewLoadingScreen(2518, "Loading", "Loading Saved Game", strRestoreGameStrings[gQuickLoadSlot]); @@ -2270,4 +2273,9 @@ void drawLoadingScreen(void) viewLoadingScreen(2049, buffer, levelGetTitle(), NULL); } +FSavegameInfo GameInterface::GetSaveSig() +{ + return { SAVESIG_BLD, MINSAVEVER_BLD, SAVEVER_BLD }; +} + END_BLD_NS diff --git a/source/build/include/baselayer.h b/source/build/include/baselayer.h index 4bc2bac9f..dec688c0a 100644 --- a/source/build/include/baselayer.h +++ b/source/build/include/baselayer.h @@ -177,6 +177,13 @@ struct FGameStartup int CustomLevel2; }; +struct FSavegameInfo +{ + const char *savesig; + int minsavever; + int currentsavever; +}; + struct GameInterface { enum EMenuSounds @@ -202,6 +209,7 @@ struct GameInterface virtual bool CanSave() { return true; } virtual void CustomMenuSelection(int menu, int item) {} virtual void StartGame(FGameStartup& gs) {} + virtual FSavegameInfo GetSaveSig() { return { "", 0, 0}; } }; extern GameInterface* gi; diff --git a/source/common/initfs.cpp b/source/common/initfs.cpp index 4a93a87e0..cb9bda281 100644 --- a/source/common/initfs.cpp +++ b/source/common/initfs.cpp @@ -267,7 +267,7 @@ static void D_AddDirectory (TArray &wadfiles, const char *dir) { skindir[stuffstart++] = '/'; int savedstart = stuffstart; - const char* validexts[] = { "*.grp", "*.zip", "*.pk3", "*.pk4", "*.7z", "*.pk7" }; + const char* validexts[] = { "*.grp", "*.zip", "*.pk3", "*.pk4", "*.7z", "*.pk7", "*.dat" }; for (auto ext : validexts) { stuffstart = savedstart; diff --git a/source/common/menu/loadsavemenu.cpp b/source/common/menu/loadsavemenu.cpp index 12f527aef..7930d1606 100644 --- a/source/common/menu/loadsavemenu.cpp +++ b/source/common/menu/loadsavemenu.cpp @@ -42,6 +42,11 @@ #include "gstrings.h" #include "d_gui.h" #include "v_draw.h" +#include "files.h" +#include "resourcefile.h" +#include "sjson.h" +#include "savegamehelp.h" +#include "i_specialpaths.h" #include "../../platform/win32/i_findfile.h" // This is a temporary direct path. Needs to be fixed when stuff gets cleaned up. @@ -207,7 +212,7 @@ void DLoadSaveMenu::ReadSaveStrings () LastSaved = LastAccessed = -1; quickSaveSlot = NULL; - filter = "";// G_BuildSaveName("*.zds", -1); + filter = G_BuildSaveName("*"); filefirst = I_FindFirst (filter.GetChars(), &c_file); if (filefirst != ((void *)(-1))) { @@ -215,25 +220,30 @@ void DLoadSaveMenu::ReadSaveStrings () { // I_FindName only returns the file's name and not its full path FString filepath = "";// G_BuildSaveName(I_FindName(&c_file), -1); - FILE *file = fopen (filepath, "rb"); - - if (file != NULL) + + FResourceFile *savegame = FResourceFile::OpenResourceFile(filepath, true, true); + if (savegame != nullptr) { - //PNGHandle *png; - //char sig[16]; + FResourceLump *info = savegame->FindLump("info.json"); + if (info == nullptr) + { + // savegame info not found. This is not a savegame so leave it alone. + delete savegame; + continue; + } + auto fr = info->NewReader(); FString title; - bool oldVer = true; - bool addIt = false; - bool missing = false; - - // ZDoom 1.23 betas 21-33 have the savesig first. - // Earlier versions have the savesig second. - // Later versions have the savegame encapsulated inside a PNG. - // - // Old savegame versions are always added to the menu so - // the user can easily delete them if desired. - - // Todo: Identify savegames here. + int check = G_ValidateSavegame(fr, &title); + delete savegame; + if (check != 0) + { + FSaveGameNode *node = new FSaveGameNode; + node->Filename = filepath; + node->bOldVersion = check == -1; + node->bMissingWads = check == -2; + node->Title = title; + InsertSaveNode(node); + } } } while (I_FindNext (filefirst, &c_file) == 0); I_FindClose (filefirst); @@ -691,7 +701,7 @@ bool DLoadSaveMenu::Responder (event_t *ev) { FString EndString; EndString.Format("%s" TEXTCOLOR_WHITE "%s" TEXTCOLOR_NORMAL "?\n\n%s", - GStrings("MNU_DELETESG"), SaveGames[Selected]->Title, GStrings("PRESSYN")); + GStrings("MNU_DELETESG"), SaveGames[Selected]->Title.GetChars(), GStrings("PRESSYN")); M_StartMessage (EndString, 0); } return true; diff --git a/source/common/savegamehelp.cpp b/source/common/savegamehelp.cpp index 3a3cdffa7..57602da34 100644 --- a/source/common/savegamehelp.cpp +++ b/source/common/savegamehelp.cpp @@ -1,5 +1,7 @@ /* ** savegame.cpp +** +** common savegame utilities for all front ends. ** **--------------------------------------------------------------------------- ** Copyright 2019 Christoph Oelckers @@ -29,21 +31,36 @@ ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** -** This is for keeping my sanity while working with the horrible mess -** that is the savegame code in Duke Nukem. -** Without handling this in global variables it is a losing proposition -** to save custom data along with the regular snapshot. :( -** With this the savegame code can mostly pretend to load from and write -** to files while really using a composite archive. -*/ +*/ #include "compositesaveame.h" #include "savegamehelp.h" - +#include "sjson.h" +#include "baselayer.h" +#include "gstrings.h" +#include "i_specialpaths.h" +#include "cmdlib.h" +#include "filesystem/filesystem.h" +#include "statistics.h" +#include "secrets.h" static CompositeSavegameWriter savewriter; static FResourceFile *savereader; +//============================================================================= +// +// This is for keeping my sanity while working with the horrible mess +// that is the savegame code in Duke Nukem. +// Without handling this in global variables it is a losing proposition +// to save custom data along with the regular snapshot. :( +// With this the savegame code can mostly pretend to load from and write +// to files while really using a composite archive. +// +// All global non-game dependent state is also saved right here for convenience. +// +//============================================================================= + + void OpenSaveGameForWrite(const char *name) { savewriter.Clear(); @@ -54,6 +71,13 @@ bool OpenSaveGameForRead(const char *name) { if (savereader) delete savereader; savereader = FResourceFile::OpenResourceFile(name, true, true); + + if (savereader != nullptr) + { + ReadStatistics(); + SECRET_Load(); + } + return savereader != nullptr; } @@ -80,3 +104,174 @@ void FinishSavegameRead() delete savereader; savereader = nullptr; } + +//============================================================================= +// +// Writes the header which is used to display the savegame in the menu. +// +//============================================================================= + +void G_WriteSaveHeader(const char *name, const char*mapname, const char *maptitle) +{ + sjson_context* ctx = sjson_create_context(0, 0, NULL); + if (!ctx) + { + return; + } + sjson_node* root = sjson_mkobject(ctx); + auto savesig = gi->GetSaveSig(); + sjson_put_int(ctx, root, "Save Version", savesig.currentsavever); + sjson_put_string(ctx, root, "Engine", savesig.savesig); + sjson_put_string(ctx, root, "Game Resource", fileSystem.GetResourceFileName(1)); + sjson_put_string(ctx, root, "map", mapname); + sjson_put_string(ctx, root, "Title", maptitle); + if (*mapname == '/') mapname++; + sjson_put_string(ctx, root, "Map Resource", mapname); + + char* encoded = sjson_stringify(ctx, root, " "); + + FileWriter* fil = WriteSavegameChunk("info.json"); + if (!fil) + { + sjson_destroy_context(ctx); + return; + } + + fil->Write(encoded, strlen(encoded)); + + sjson_free_string(ctx, encoded); + sjson_destroy_context(ctx); + + SaveStatistics(); + SECRET_Save(); +} + +//============================================================================= +// +// +// +//============================================================================= + +static bool CheckSingleFile (const char *name, bool &printRequires, bool printwarn) +{ + if (name == NULL) + { + return true; + } + if (fileSystem.CheckIfResourceFileLoaded(name) < 0) + { + if (printwarn) + { + if (!printRequires) + { + Printf ("%s:\n%s", GStrings("TXT_SAVEGAMENEEDS"), name); + } + else + { + Printf (", %s", name); + } + } + printRequires = true; + return false; + } + return true; +} + +//============================================================================= +// +// Return false if not all the needed wads have been loaded. +// +//============================================================================= + +bool G_CheckSaveGameWads (sjson_node* root, bool printwarn) +{ + bool printRequires = false; + auto text = sjson_get_string(root, "Game Resource", ""); + CheckSingleFile (text, printRequires, printwarn); + text = sjson_get_string(root, "MAP Resource", ""); + CheckSingleFile (text, printRequires, printwarn); + + if (printRequires) + { + if (printwarn) + { + Printf ("\n"); + } + return false; + } + + return true; +} + +//============================================================================= +// +// Checks if the savegame is valid. Gets a reader to the included info.json +// Returns 1 if valid, 0 if invalid and -1 if old and -2 if content missing +// +//============================================================================= + +int G_ValidateSavegame(FileReader &fr, FString *savetitle) +{ + auto data = fr.ReadPadded(1); + + sjson_context* ctx = sjson_create_context(0, 0, NULL); + if (ctx) + { + sjson_node* root = sjson_decode(ctx, (const char*)data.Data()); + + + int savever = sjson_get_int(root, "Save Version", -1); + FString engine = sjson_get_string(root, "Engine", ""); + FString gamegrp = sjson_get_string(root, "Game Resource", ""); + FString title = sjson_get_string(root, "Title", ""); + auto savesig = gi->GetSaveSig(); + + sjson_destroy_context(ctx); + + if (savetitle) *savetitle = title; + if (engine.Compare(savesig.savesig) != 0 || savever > savesig.currentsavever) + { + // different engine or newer version: + // not our business. Leave it alone. + return 0; + } + + if (savever < savesig.minsavever) + { + // old, incompatible savegame. List as not usable. + return -1; + } + else if (gamegrp.CompareNoCase(fileSystem.GetResourceFileName(1)) == 0) + { + return G_CheckSaveGameWads(root, false)? 0 : -2; + } + else + { + // different game. Skip this. + return 0; + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +FString G_BuildSaveName (const char *prefix) +{ + FString name = M_GetSavegamesPath(); + size_t len = name.Len(); + if (name[0] != '\0' && name[len-1] != '\\' && name[len-1] != '/') + { + name << "/"; + } + name << prefix; + if (!strchr(prefix, '.')) name << SAVEGAME_EXT; // only add an extension if the prefix doesn't have one already. + name = NicePath(name); + name.Substitute("\\", "/"); + CreatePath(name); + return name; +} + diff --git a/source/common/savegamehelp.h b/source/common/savegamehelp.h index 9dec6d617..12e56cc3d 100644 --- a/source/common/savegamehelp.h +++ b/source/common/savegamehelp.h @@ -10,3 +10,13 @@ FileReader ReadSavegameChunk(const char *name); bool FinishSavegameWrite(); void FinishSavegameRead(); + +// Savegame utilities +class FileReader; + +FString G_BuildSaveName (const char *prefix); +bool G_CheckSaveGameWads (struct sjson_node* root, bool printwarn); +int G_ValidateSavegame(FileReader &fr, FString *savetitle); +void G_WriteSaveHeader(const char *name, const char*mapname, const char *title); + +#define SAVEGAME_EXT ".dsave" diff --git a/source/common/version.h b/source/common/version.h index 388737dbf..c9908ab51 100644 --- a/source/common/version.h +++ b/source/common/version.h @@ -41,11 +41,11 @@ const char *GetVersionString(); /** Lots of different version numbers **/ -#define VERSIONSTR "0.0.1" +#define VERSIONSTR "0.1.0" // The version as seen in the Windows resource -#define RC_FILEVERSION 0,0,1,0 -#define RC_PRODUCTVERSION 0,0,1,0 +#define RC_FILEVERSION 0,1,0,0 +#define RC_PRODUCTVERSION 0,1,0,0 #define RC_PRODUCTVERSION2 VERSIONSTR // These are for content versioning. #define VER_MAJOR 0 @@ -59,6 +59,21 @@ const char *GetVersionString(); #define FORUM_URL "http://forum.zdoom.org/" //#define BUGS_FORUM_URL "http://forum.zdoom.org/viewforum.php?f=2" +#define SAVESIG_DN3D "Demolition.Duke" +#define SAVESIG_BLD "Demolition.Blood" +#define SAVESIG_RR "Demolition.Redneck" +#define SAVESIG_SW "Demolition.SW" + +#define MINSAVEVER_DN3D 1 +#define MINSAVEVER_BLD 1 +#define MINSAVEVER_RR 1 +#define MINSAVEVER_SW 1 + +#define SAVEVER_DN3D 1 +#define SAVEVER_BLD 1 +#define SAVEVER_RR 1 +#define SAVEVER_SW 1 + #if defined(__APPLE__) || defined(_WIN32) #define GAME_DIR GAMENAME #else diff --git a/source/duke3d/src/d_menu.cpp b/source/duke3d/src/d_menu.cpp index 40fbc6a04..e51a8f022 100644 --- a/source/duke3d/src/d_menu.cpp +++ b/source/duke3d/src/d_menu.cpp @@ -36,6 +36,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "c_bind.h" #include "menu/menu.h" #include "gstrings.h" +#include "version.h" #include "../../glbackend/glbackend.h" BEGIN_DUKE_NS @@ -465,6 +466,12 @@ void GameInterface::StartGame(FGameStartup& gs) } +FSavegameInfo GameInterface::GetSaveSig() +{ + return { SAVESIG_DN3D, MINSAVEVER_DN3D, SAVEVER_DN3D }; +} + + END_DUKE_NS static TMenuClassDescriptor _mm("Duke.MainMenu"); diff --git a/source/duke3d/src/duke3d.h b/source/duke3d/src/duke3d.h index d1e864766..ebf34735c 100644 --- a/source/duke3d/src/duke3d.h +++ b/source/duke3d/src/duke3d.h @@ -163,6 +163,7 @@ struct GameInterface : ::GameInterface bool CanSave() override; void CustomMenuSelection(int menu, int item) override; void StartGame(FGameStartup& gs) override; + FSavegameInfo GetSaveSig() override; }; diff --git a/source/duke3d/src/game.cpp b/source/duke3d/src/game.cpp index aeac1605c..082385980 100644 --- a/source/duke3d/src/game.cpp +++ b/source/duke3d/src/game.cpp @@ -5497,7 +5497,7 @@ static void G_FreeHashAnim(const char * /*string*/, intptr_t key) static void G_Cleanup(void) { - ReadSaveGameHeaders(); // for culling + //ReadSaveGameHeaders(); // for culling int32_t i; @@ -6189,8 +6189,6 @@ int GameInterface::app_main() Menu_Init(); } - ReadSaveGameHeaders(); - FX_StopAllSounds(); S_ClearSoundLocks(); diff --git a/source/duke3d/src/menus.cpp b/source/duke3d/src/menus.cpp index b2a17664c..7af81181a 100644 --- a/source/duke3d/src/menus.cpp +++ b/source/duke3d/src/menus.cpp @@ -2543,31 +2543,6 @@ static void Menu_PreDraw(MenuID_t cm, MenuEntry_t *entry, const vec2_t origin) } -static void Menu_ReadSaveGameHeaders(); - -static void Menu_LoadReadHeaders() -{ - Menu_ReadSaveGameHeaders(); - - for (int i = 0; i < g_nummenusaves; ++i) - { - menusave_t const & msv = g_menusaves[i]; - // MenuEntry_LookDisabledOnCondition(&ME_LOAD[i], msv.isOldVer && msv.brief.isExt); - MenuEntry_DisableOnCondition(&ME_LOAD[i], msv.isOldVer && !msv.brief.isExt); - } -} - -static void Menu_SaveReadHeaders() -{ - Menu_ReadSaveGameHeaders(); - - for (int i = 0; i < g_nummenusaves; ++i) - { - menusave_t const & msv = g_menusaves[i]; - MenuEntry_LookDisabledOnCondition(&ME_SAVE[i], msv.isOldVer && !msv.brief.isExt); - } -} - static void Menu_PreInput(MenuEntry_t *entry) { switch (g_currentMenu) @@ -2862,7 +2837,6 @@ static void Menu_EntryLinkActivate(MenuEntry_t *entry) } else if (entry == &ME_SAVESETUP_CLEANUP) { - g_oldSaveCnt = G_CountOldSaves(); Menu_Change(MENU_SAVECLEANVERIFY); } else if (entry == &ME_NETHOST_LAUNCH) @@ -3247,15 +3221,13 @@ static void Menu_Verify(int32_t input) case MENU_LOADDELVERIFY: if (input) { - G_DeleteSave(g_menusaves[M_LOAD.currentEntry].brief); - Menu_LoadReadHeaders(); + Menu_LoadReadHeaders(); M_LOAD.currentEntry = clamp(M_LOAD.currentEntry, 0, (int32_t)g_nummenusaves-1); } break; case MENU_SAVEDELVERIFY: if (input) { - G_DeleteSave(g_menusaves[M_SAVE.currentEntry-1].brief); Menu_SaveReadHeaders(); M_SAVE.currentEntry = clamp(M_SAVE.currentEntry, 0, (int32_t)g_nummenusaves); } @@ -3506,42 +3478,6 @@ static void Menu_FileSelect(int32_t input) } -static void Menu_ReadSaveGameHeaders() -{ - ReadSaveGameHeaders(); - - int const numloaditems = max(g_nummenusaves, 1), numsaveitems = g_nummenusaves+1; - ME_LOAD = (MenuEntry_t *)Xrealloc(ME_LOAD, g_nummenusaves * sizeof(MenuEntry_t)); - MEL_LOAD = (MenuEntry_t **)Xrealloc(MEL_LOAD, numloaditems * sizeof(MenuEntry_t *)); - MEO_SAVE = (MenuString_t *)Xrealloc(MEO_SAVE, g_nummenusaves * sizeof(MenuString_t)); - ME_SAVE = (MenuEntry_t *)Xrealloc(ME_SAVE, g_nummenusaves * sizeof(MenuEntry_t)); - MEL_SAVE = (MenuEntry_t **)Xrealloc(MEL_SAVE, numsaveitems * sizeof(MenuEntry_t *)); - - MEL_SAVE[0] = &ME_SAVE_NEW; - ME_SAVE_NEW.name = s_NewSaveGame; - for (int i = 0; i < g_nummenusaves; ++i) - { - MEL_LOAD[i] = &ME_LOAD[i]; - MEL_SAVE[i+1] = &ME_SAVE[i]; - ME_LOAD[i] = ME_LOAD_TEMPLATE; - ME_SAVE[i] = ME_SAVE_TEMPLATE; - ME_SAVE[i].entry = &MEO_SAVE[i]; - MEO_SAVE[i] = MEO_SAVE_TEMPLATE; - - ME_LOAD[i].name = g_menusaves[i].brief.name; - MEO_SAVE[i].variable = g_menusaves[i].brief.name; - } - - if (g_nummenusaves == 0) - MEL_LOAD[0] = &ME_LOAD_EMPTY; - - M_LOAD.entrylist = MEL_LOAD; - M_LOAD.numEntries = numloaditems; - M_SAVE.entrylist = MEL_SAVE; - M_SAVE.numEntries = numsaveitems; - - // lexicographical sorting? -} static void Menu_AboutToStartDisplaying(Menu_t * m) { diff --git a/source/duke3d/src/osdcmds.cpp b/source/duke3d/src/osdcmds.cpp index 215fb3d9a..6f951cab4 100644 --- a/source/duke3d/src/osdcmds.cpp +++ b/source/duke3d/src/osdcmds.cpp @@ -913,12 +913,6 @@ static int osdcmd_kickban(osdcmdptr_t parm) } #endif -static int osdcmd_purgesaves(osdcmdptr_t UNUSED(parm)) -{ - UNREFERENCED_CONST_PARAMETER(parm); - G_DeleteOldSaves(); - return OSDCMD_OK; -} static int osdcmd_printtimes(osdcmdptr_t UNUSED(parm)) { @@ -1025,8 +1019,6 @@ int32_t registerosdcommands(void) OSD_RegisterFunction("printtimes", "printtimes: prints VM timing statistics", osdcmd_printtimes); - OSD_RegisterFunction("purgesaves", "purgesaves: deletes obsolete and unreadable save files", osdcmd_purgesaves); - OSD_RegisterFunction("quicksave","quicksave: performs a quick save", osdcmd_quicksave); OSD_RegisterFunction("quickload","quickload: performs a quick load", osdcmd_quickload); diff --git a/source/duke3d/src/savegame.cpp b/source/duke3d/src/savegame.cpp index 2695d3d03..7b997f2d5 100644 --- a/source/duke3d/src/savegame.cpp +++ b/source/duke3d/src/savegame.cpp @@ -30,8 +30,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "i_specialpaths.h" #include "gamecontrol.h" #include "version.h" -#include "statistics.h" -#include "secrets.h" #include "savegamehelp.h" #include "menu/menu.h" @@ -147,11 +145,6 @@ int32_t g_lastAutoSaveArbitraryID = -1; bool g_saveRequested; savebrief_t * g_quickload; -menusave_t * g_menusaves; -uint16_t g_nummenusaves; - -static menusave_t * g_internalsaves; -static uint16_t g_numinternalsaves; static FileReader *OpenSavegame(const char *fn) { @@ -159,12 +152,17 @@ static FileReader *OpenSavegame(const char *fn) { return nullptr; } - auto file = ReadSavegameChunk("DEMOLITION_ED"); + auto file = ReadSavegameChunk("info.json"); if (!file.isOpen()) { FinishSavegameRead(); return nullptr; } + if (G_ValidateSavegame(file, nullptr) <= 0) + { + FinishSavegameRead(); + return nullptr; + } file = ReadSavegameChunk("snapshot.dat"); if (!file.isOpen()) { @@ -174,145 +172,6 @@ static FileReader *OpenSavegame(const char *fn) return new FileReader(std::move(file)); } -static void ReadSaveGameHeaders_CACHE1D(TArray &saves) -{ - savehead_t h; - - for (FString &save : saves) - { - auto fil = OpenSavegame(save); - if (!fil) - continue; - - menusave_t & msv = g_internalsaves[g_numinternalsaves]; - - msv.brief.isExt = 0; - - int32_t k = sv_loadheader(*fil, 0, &h); - delete fil; - if (k) - { - if (k < 0) - msv.isUnreadable = 1; - else - { - if (FURY) - { - auto extfil = ReadSavegameChunk("ext.json"); - if (extfil.isOpen()) - { - msv.brief.isExt = 1; - } - } - } - msv.isOldVer = 1; - } - else - msv.isOldVer = 0; - - msv.isAutoSave = h.isAutoSave(); - - strncpy(msv.brief.path, save.GetChars(), ARRAY_SIZE(msv.brief.path)); - ++g_numinternalsaves; - - if (k >= 0 && h.savename[0] != '\0') - { - memcpy(msv.brief.name, h.savename, ARRAY_SIZE(msv.brief.name)); - } - else - msv.isUnreadable = 1; - - } - FinishSavegameRead(); -} - -static void ReadSaveGameHeaders_Internal(void) -{ - FString pattern = M_GetSavegamesPath() + "*.bsv"; - TArray saves; - D_AddWildFile(saves, pattern); - // potentially overallocating but programmatically simple - int const numfiles = saves.Size(); - size_t const internalsavesize = sizeof(menusave_t) * numfiles; - - g_internalsaves = (menusave_t *)Xrealloc(g_internalsaves, internalsavesize); - - for (int x = 0; x < numfiles; ++x) - g_internalsaves[x].clear(); - - g_numinternalsaves = 0; - ReadSaveGameHeaders_CACHE1D(saves); - - g_nummenusaves = 0; - for (int x = g_numinternalsaves-1; x >= 0; --x) - { - menusave_t & msv = g_internalsaves[x]; - if (!msv.isUnreadable) - { - ++g_nummenusaves; - } - } - size_t const menusavesize = sizeof(menusave_t) * g_nummenusaves; - - g_menusaves = (menusave_t *)Xrealloc(g_menusaves, menusavesize); - - for (int x = 0; x < g_nummenusaves; ++x) - g_menusaves[x].clear(); - - for (int x = g_numinternalsaves-1, y = 0; x >= 0; --x) - { - menusave_t & msv = g_internalsaves[x]; - if (!msv.isUnreadable) - { - g_menusaves[y++] = msv; - } - } - - for (int x = g_numinternalsaves-1; x >= 0; --x) - { - char const * const path = g_internalsaves[x].brief.path; - int const pathlen = Bstrlen(path); - if (pathlen < 12) - continue; - char const * const fn = path + (pathlen-12); - if (fn[0] == 's' && fn[1] == 'a' && fn[2] == 'v' && fn[3] == 'e' && - isdigit(fn[4]) && isdigit(fn[5]) && isdigit(fn[6]) && isdigit(fn[7])) - { - char number[5]; - memcpy(number, fn+4, 4); - number[4] = '\0'; - savecounter.count = Batoi(number)+1; - break; - } - } -} - -void ReadSaveGameHeaders(void) -{ - ReadSaveGameHeaders_Internal(); - - if (!cl_autosavedeletion) - return; - - bool didDelete = false; - int numautosaves = 0; - for (int x = 0; x < g_nummenusaves; ++x) - { - menusave_t & msv = g_menusaves[x]; - if (!msv.isAutoSave) - continue; - if (numautosaves >= cl_maxautosaves) - { - G_DeleteSave(msv.brief); - didDelete = true; - } - ++numautosaves; - } - - if (didDelete) - ReadSaveGameHeaders_Internal(); -} - int32_t G_LoadSaveHeaderNew(char const *fn, savehead_t *saveh) { FileReader ssfil; @@ -681,7 +540,7 @@ int32_t G_LoadPlayer(savebrief_t & sv) if (status == 2) G_NewGame_EnterLevel(); - else if ((status = sv_loadsnapshot(*fil, 0, &h)) || !ReadStatistics() || !SECRET_Load()) // read the rest... + else if ((status = sv_loadsnapshot(*fil, 0, &h))) // read the rest... { // in theory, we could load into an initial dump first and trivially // recover if things go wrong... @@ -722,49 +581,6 @@ static void G_RestoreTimers(void) ////////// -void G_DeleteSave(savebrief_t const & sv) -{ - if (!sv.isValid()) - return; - - char temp[BMAX_PATH]; - - if (snprintf(temp, sizeof(temp), "%s%s", M_GetSavegamesPath().GetChars(), sv.path)) - { - OSD_Printf("G_SavePlayer: file name \"%s\" too long\n", sv.path); - return; - } - - remove(temp); -} - -void G_DeleteOldSaves(void) -{ - ReadSaveGameHeaders(); - - for (int x = 0; x < g_numinternalsaves; ++x) - { - menusave_t const & msv = g_internalsaves[x]; - if (msv.isOldVer || msv.isUnreadable) - G_DeleteSave(msv.brief); - } -} - -uint16_t G_CountOldSaves(void) -{ - ReadSaveGameHeaders(); - - int bad = 0; - for (int x = 0; x < g_numinternalsaves; ++x) - { - menusave_t const & msv = g_internalsaves[x]; - if (msv.isOldVer || msv.isUnreadable) - ++bad; - } - - return bad; -} - int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave) { #ifdef __ANDROID__ @@ -783,29 +599,28 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave) if (sv.isValid()) { - fn.Format("%s%s", M_GetSavegamesPath().GetChars(), sv.path); + fn = G_BuildSaveName(sv.path); OpenSaveGameForWrite(fn); fil = WriteSavegameChunk("snapshot.dat"); } else { - static char const SaveName[] = "save0000.bsv"; - fn.Format("%s%s", M_GetSavegamesPath().GetChars(), SaveName); + fn = G_BuildSaveName("save0000"); auto fnp = fn.LockBuffer(); - char* zeros = fnp + (fn.Len() - 8); - fil = savecounter.opennextfile(fnp, zeros); + char* zeros = strstr(fnp, "0000"); + fil = savecounter.opennextfile(fnp, zeros); // fixme: Rewrite this so that it won't create the file. + fn.UnlockBuffer(); if (fil) { delete fil; - remove(fnp); - OpenSaveGameForWrite(fnp); + remove(fn); + OpenSaveGameForWrite(fn); fil = WriteSavegameChunk("snapshot.dat"); } - fn.UnlockBuffer(); savecounter.count++; - // don't copy the mod dir into sv.path - Bstrcpy(sv.path, fn + (fn.Len() - (ARRAY_SIZE(SaveName) - 1))); + // don't copy the mod dir into sv.path (G_BuildSaveName guarantees the presence of a slash.) + Bstrcpy(sv.path, strrchr(fn, '/') + 1); } if (!fil) @@ -821,7 +636,6 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave) } else { - WriteSavegameChunk("DEMOLITION_ED"); auto& fw = *fil; sv.isExt = 0; @@ -835,8 +649,7 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave) // SAVE! sv_saveandmakesnapshot(fw, sv.name, 0, 0, 0, 0, isAutoSave); - SaveStatistics(); - SECRET_Save(); + fw.Close(); FinishSavegameWrite(); @@ -1712,6 +1525,8 @@ int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot, i Bstrncpyz(h.savename, name, sizeof(h.savename)); auto fw = WriteSavegameChunk("header.dat"); fw->Write(&h, sizeof(savehead_t)); + + G_WriteSaveHeader(name, currentboardfilename, g_mapInfo[(MAXLEVELS * ud.volume_number) + ud.level_number].name); } else { diff --git a/source/duke3d/src/savegame.h b/source/duke3d/src/savegame.h index d66e837f9..6f34329ac 100644 --- a/source/duke3d/src/savegame.h +++ b/source/duke3d/src/savegame.h @@ -114,8 +114,6 @@ extern int32_t g_lastAutoSaveArbitraryID; extern bool g_saveRequested; extern savebrief_t * g_quickload; -extern menusave_t * g_menusaves; -extern uint16_t g_nummenusaves; int32_t sv_updatestate(int32_t frominit); int32_t sv_readdiff(FileReader& fil); @@ -124,9 +122,6 @@ int32_t sv_loadheader(FileReader &fil, int32_t spot, savehead_t *h); int32_t sv_loadsnapshot(FileReader &fil, int32_t spot, savehead_t *h); int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot, int8_t recdiffsp, int8_t diffcompress, int8_t synccompress, bool isAutoSave = false); void sv_freemem(); -void G_DeleteSave(savebrief_t const & sv); -void G_DeleteOldSaves(void); -uint16_t G_CountOldSaves(void); int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave); int32_t G_LoadPlayer(savebrief_t & sv); int32_t G_LoadSaveHeaderNew(char const *fn, savehead_t *saveh); diff --git a/source/rr/src/duke3d.h b/source/rr/src/duke3d.h index 48462e06d..28d426db3 100644 --- a/source/rr/src/duke3d.h +++ b/source/rr/src/duke3d.h @@ -158,6 +158,7 @@ struct GameInterface : ::GameInterface bool mouseInactiveConditional(bool condition) override; FString statFPS() override; GameStats getStats() override; + FSavegameInfo GetSaveSig() override; }; END_RR_NS diff --git a/source/rr/src/menus.cpp b/source/rr/src/menus.cpp index eea4a87e8..34dcd480a 100644 --- a/source/rr/src/menus.cpp +++ b/source/rr/src/menus.cpp @@ -31,6 +31,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "cheats.h" #include "gamecvars.h" #include "menu/menu.h" +#include "version.h" #include "../../glbackend/glbackend.h" BEGIN_RR_NS @@ -3446,7 +3447,6 @@ static void Menu_EntryLinkActivate(MenuEntry_t *entry) } else if (entry == &ME_SAVESETUP_CLEANUP) { - g_oldSaveCnt = G_CountOldSaves(); Menu_Change(MENU_SAVECLEANVERIFY); } else if (entry == &ME_COLCORR_RESET) @@ -3768,10 +3768,6 @@ static void Menu_Verify(int32_t input) switch (g_currentMenu) { case MENU_SAVECLEANVERIFY: - if (input) - { - G_DeleteOldSaves(); - } break; case MENU_RESETPLAYER: @@ -3839,7 +3835,7 @@ static void Menu_Verify(int32_t input) case MENU_LOADDELVERIFY: if (input) { - G_DeleteSave(g_menusaves[M_LOAD.currentEntry].brief); + //G_DeleteSave(g_menusaves[M_LOAD.currentEntry].brief); Menu_LoadReadHeaders(); M_LOAD.currentEntry = clamp(M_LOAD.currentEntry, 0, (int32_t)g_nummenusaves-1); } @@ -3847,7 +3843,7 @@ static void Menu_Verify(int32_t input) case MENU_SAVEDELVERIFY: if (input) { - G_DeleteSave(g_menusaves[M_SAVE.currentEntry-1].brief); + //G_DeleteSave(g_menusaves[M_SAVE.currentEntry-1].brief); Menu_SaveReadHeaders(); M_SAVE.currentEntry = clamp(M_SAVE.currentEntry, 0, (int32_t)g_nummenusaves); } @@ -7462,4 +7458,9 @@ bool GameInterface::mouseInactiveConditional(bool condition) return MOUSEINACTIVECONDITIONAL(condition); } +FSavegameInfo GameInterface::GetSaveSig() +{ + return { SAVESIG_RR, MINSAVEVER_RR, SAVEVER_RR }; +} + END_RR_NS diff --git a/source/rr/src/osdcmds.cpp b/source/rr/src/osdcmds.cpp index c380a897d..0c1f02cb9 100644 --- a/source/rr/src/osdcmds.cpp +++ b/source/rr/src/osdcmds.cpp @@ -791,13 +791,6 @@ static int osdcmd_kickban(osdcmdptr_t parm) #endif #endif -static int osdcmd_purgesaves(osdcmdptr_t UNUSED(parm)) -{ - UNREFERENCED_CONST_PARAMETER(parm); - G_DeleteOldSaves(); - return OSDCMD_OK; -} - static int osdcmd_printtimes(osdcmdptr_t UNUSED(parm)) { UNREFERENCED_CONST_PARAMETER(parm); @@ -881,8 +874,6 @@ int32_t registerosdcommands(void) OSD_RegisterFunction("printtimes", "printtimes: prints VM timing statistics", osdcmd_printtimes); - OSD_RegisterFunction("purgesaves", "purgesaves: deletes obsolete and unreadable save files", osdcmd_purgesaves); - OSD_RegisterFunction("quicksave","quicksave: performs a quick save", osdcmd_quicksave); OSD_RegisterFunction("quickload","quickload: performs a quick load", osdcmd_quickload); diff --git a/source/rr/src/savegame.cpp b/source/rr/src/savegame.cpp index 0501c6f7a..3b35aac73 100644 --- a/source/rr/src/savegame.cpp +++ b/source/rr/src/savegame.cpp @@ -28,8 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "i_specialpaths.h" #include "gamecontrol.h" #include "version.h" -#include "statistics.h" -#include "secrets.h" + #include "savegamehelp.h" BEGIN_RR_NS @@ -155,12 +154,17 @@ static FileReader *OpenSavegame(const char *fn) { return nullptr; } - auto file = ReadSavegameChunk("DEMOLITION_RN"); + auto file = ReadSavegameChunk("info.json"); if (!file.isOpen()) { FinishSavegameRead(); return nullptr; } + if (G_ValidateSavegame(file, nullptr) <= 0) + { + FinishSavegameRead(); + return nullptr; + } file = ReadSavegameChunk("snapshot.dat"); if (!file.isOpen()) { @@ -170,130 +174,9 @@ static FileReader *OpenSavegame(const char *fn) return new FileReader(std::move(file)); } -static void ReadSaveGameHeaders_CACHE1D(TArray& saves) -{ - savehead_t h; - - for (FString &save : saves) - { - auto fil = OpenSavegame(save); - if (!fil) - continue; - - menusave_t & msv = g_internalsaves[g_numinternalsaves]; - - int32_t k = sv_loadheader(*fil, 0, &h); - delete fil; - if (k) - { - if (k < 0) - msv.isUnreadable = 1; - msv.isOldVer = 1; - } - else - msv.isOldVer = 0; - - msv.isAutoSave = h.isAutoSave(); - - strncpy(msv.brief.path, save.GetChars(), ARRAY_SIZE(msv.brief.path)); - ++g_numinternalsaves; - - if (k >= 0 && h.savename[0] != '\0') - { - memcpy(msv.brief.name, h.savename, ARRAY_SIZE(msv.brief.name)); - } - else - msv.isUnreadable = 1; - } - FinishSavegameRead(); -} - -static void ReadSaveGameHeaders_Internal(void) -{ - FString pattern = M_GetSavegamesPath() + "*.bsv"; - TArray saves; - D_AddWildFile(saves, pattern); - - // potentially overallocating but programmatically simple - int const numfiles = saves.Size(); - size_t const internalsavesize = sizeof(menusave_t) * numfiles; - - g_internalsaves = (menusave_t *)Xrealloc(g_internalsaves, internalsavesize); - - for (int x = 0; x < numfiles; ++x) - g_internalsaves[x].clear(); - - g_numinternalsaves = 0; - ReadSaveGameHeaders_CACHE1D(saves); - - g_nummenusaves = 0; - for (int x = g_numinternalsaves-1; x >= 0; --x) - { - menusave_t & msv = g_internalsaves[x]; - if (!msv.isUnreadable) - { - ++g_nummenusaves; - } - } - size_t const menusavesize = sizeof(menusave_t) * g_nummenusaves; - - g_menusaves = (menusave_t *)Xrealloc(g_menusaves, menusavesize); - - for (int x = 0; x < g_nummenusaves; ++x) - g_menusaves[x].clear(); - - for (int x = g_numinternalsaves-1, y = 0; x >= 0; --x) - { - menusave_t & msv = g_internalsaves[x]; - if (!msv.isUnreadable) - { - g_menusaves[y++] = msv; - } - } - - for (int x = g_numinternalsaves-1; x >= 0; --x) - { - char const * const path = g_internalsaves[x].brief.path; - int const pathlen = Bstrlen(path); - if (pathlen < 12) - continue; - char const * const fn = path + (pathlen-12); - if (fn[0] == 's' && fn[1] == 'a' && fn[2] == 'v' && fn[3] == 'e' && - isdigit(fn[4]) && isdigit(fn[5]) && isdigit(fn[6]) && isdigit(fn[7])) - { - char number[5]; - memcpy(number, fn+4, 4); - number[4] = '\0'; - savecounter.count = Batoi(number)+1; - break; - } - } -} void ReadSaveGameHeaders(void) { - ReadSaveGameHeaders_Internal(); - - if (!cl_autosavedeletion) - return; - - bool didDelete = false; - int numautosaves = 0; - for (int x = 0; x < g_nummenusaves; ++x) - { - menusave_t & msv = g_menusaves[x]; - if (!msv.isAutoSave) - continue; - if (numautosaves >= cl_maxautosaves) - { - G_DeleteSave(msv.brief); - didDelete = true; - } - ++numautosaves; - } - - if (didDelete) - ReadSaveGameHeaders_Internal(); } int32_t G_LoadSaveHeaderNew(char const *fn, savehead_t *saveh) @@ -415,7 +298,7 @@ int32_t G_LoadPlayer(savebrief_t & sv) if (status == 2) G_NewGame_EnterLevel(); - else if ((status = sv_loadsnapshot(*fil, 0, &h)) || !ReadStatistics() || !SECRET_Load()) // read the rest... + else if ((status = sv_loadsnapshot(*fil, 0, &h))) // read the rest... { // in theory, we could load into an initial dump first and trivially // recover if things go wrong... @@ -455,52 +338,6 @@ static void G_RestoreTimers(void) lockclock = g_timers.lockclock; } -////////// - - -void G_DeleteSave(savebrief_t const & sv) -{ - if (!sv.isValid()) - return; - - char temp[BMAX_PATH]; - - if (snprintf(temp, sizeof(temp), "%s%s", M_GetSavegamesPath().GetChars(), sv.path)) - { - OSD_Printf("G_SavePlayer: file name \"%s\" too long\n", sv.path); - return; - } - - remove(temp); -} - -void G_DeleteOldSaves(void) -{ - ReadSaveGameHeaders(); - - for (int x = 0; x < g_numinternalsaves; ++x) - { - menusave_t const & msv = g_internalsaves[x]; - if (msv.isOldVer || msv.isUnreadable) - G_DeleteSave(msv.brief); - } -} - -uint16_t G_CountOldSaves(void) -{ - ReadSaveGameHeaders(); - - int bad = 0; - for (int x = 0; x < g_numinternalsaves; ++x) - { - menusave_t const & msv = g_internalsaves[x]; - if (msv.isOldVer || msv.isUnreadable) - ++bad; - } - - return bad; -} - int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave) { #ifdef __ANDROID__ @@ -519,29 +356,28 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave) if (sv.isValid()) { - fn.Format("%s%s", M_GetSavegamesPath().GetChars(), sv.path); + fn = G_BuildSaveName(sv.path); OpenSaveGameForWrite(fn); fil = WriteSavegameChunk("snapshot.dat"); } else { - static char const SaveName[] = "save0000.bsv"; - fn.Format("%s%s", M_GetSavegamesPath().GetChars(), SaveName); + fn = G_BuildSaveName("save0000"); auto fnp = fn.LockBuffer(); - char* zeros = fnp + (fn.Len() - 8); - fil = savecounter.opennextfile(fnp, zeros); + char* zeros = strstr(fnp, "0000"); + fil = savecounter.opennextfile(fnp, zeros); // fixme: Rewrite this so that it won't create the file. + fn.UnlockBuffer(); if (fil) { delete fil; - remove(fnp); - OpenSaveGameForWrite(fnp); + remove(fn); + OpenSaveGameForWrite(fn); fil = WriteSavegameChunk("snapshot.dat"); } - fn.UnlockBuffer(); savecounter.count++; - // don't copy the mod dir into sv.path - Bstrcpy(sv.path, fn + (fn.Len() - (ARRAY_SIZE(SaveName) - 1))); + // don't copy the mod dir into sv.path (G_BuildSaveName guarantees the presence of a slash.) + Bstrcpy(sv.path, strrchr(fn, '/') + 1); } if (!fil) @@ -567,8 +403,7 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave) // SAVE! sv_saveandmakesnapshot(fw, sv.name, 0, 0, 0, 0, isAutoSave); - SaveStatistics(); - SECRET_Save(); + fw.Close(); FinishSavegameWrite(); @@ -1385,6 +1220,8 @@ int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot, i Bstrncpyz(h.savename, name, sizeof(h.savename)); auto fw = WriteSavegameChunk("header.dat"); fw->Write(&h, sizeof(savehead_t)); + + G_WriteSaveHeader(name, currentboardfilename, g_mapInfo[(MAXLEVELS * ud.volume_number) + ud.level_number].name); } else { diff --git a/source/rr/src/savegame.h b/source/rr/src/savegame.h index d4f912af6..705698488 100644 --- a/source/rr/src/savegame.h +++ b/source/rr/src/savegame.h @@ -117,9 +117,6 @@ int32_t sv_loadheader(FileReader &fil, int32_t spot, savehead_t *h); int32_t sv_loadsnapshot(FileReader &fil, int32_t spot, savehead_t *h); int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot, int8_t recdiffsp, int8_t diffcompress, int8_t synccompress, bool isAutoSave = false); void sv_freemem(); -void G_DeleteSave(savebrief_t const & sv); -void G_DeleteOldSaves(void); -uint16_t G_CountOldSaves(void); int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave); int32_t G_LoadPlayer(savebrief_t & sv); int32_t G_LoadSaveHeaderNew(char const *fn, savehead_t *saveh); diff --git a/source/sw/src/game.h b/source/sw/src/game.h index 111ac1878..9a60d6fc3 100644 --- a/source/sw/src/game.h +++ b/source/sw/src/game.h @@ -2382,6 +2382,7 @@ struct GameInterface : ::GameInterface void set_hud_layout(int size) override; void set_hud_scale(int size) override; bool mouseInactiveConditional(bool condition) override; + FSavegameInfo GetSaveSig() override; }; diff --git a/source/sw/src/menus.cpp b/source/sw/src/menus.cpp index 0144da70d..b4ab5e12b 100644 --- a/source/sw/src/menus.cpp +++ b/source/sw/src/menus.cpp @@ -53,6 +53,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms #include "fx_man.h" #include "music.h" #include "text.h" +#include "version.h" #include "colormap.h" #include "config.h" @@ -4737,5 +4738,10 @@ void ResetPalette(PLAYERp pp) // vim:ts=4:sw=4:enc=utf-8: +FSavegameInfo GameInterface::GetSaveSig() +{ + return { SAVESIG_SW, MINSAVEVER_SW, SAVEVER_SW }; +} + END_SW_NS diff --git a/source/sw/src/save.cpp b/source/sw/src/save.cpp index 7aafcf519..cb61d11ff 100644 --- a/source/sw/src/save.cpp +++ b/source/sw/src/save.cpp @@ -245,6 +245,15 @@ int SaveGame(short save_num) OrgTileP otp, next_otp; Saveable_Init(); + + +#if 0 // A lot of work is needed here... (Thank God for all the macros around the load/save functions. :) ) + FStringf base("save%04d", save_num); + auto game_name = G_BuildSaveName(base); + OpenSaveGameForWrite(game_name); + G_WriteSaveHeader(SaveGameDescr[save_num], LevelInfo[Level].LevelName, LevelInfo[Level].Description); + auto fil = WriteSavegameChunk("snapshot.sw"); +#endif snprintf(game_name, 256, "%sgame%d.sav", M_GetSavegamesPath().GetChars(), save_num); if ((fil = MOPEN_WRITE(game_name)) == MOPEN_WRITE_ERR)