diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index afc9cf540..2017fe075 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -932,6 +932,7 @@ set (PCH_SOURCES common/menu/optionmenu.cpp common/menu/resolutionmenu.cpp common/menu/menudef.cpp + common/menu/savegamemanager.cpp common/rendering/v_framebuffer.cpp common/rendering/v_video.cpp diff --git a/source/blood/src/blood.h b/source/blood/src/blood.h index fd7cb6f51..80a49bd88 100644 --- a/source/blood/src/blood.h +++ b/source/blood/src/blood.h @@ -80,8 +80,8 @@ struct GameInterface : ::GameInterface void MenuClosed() override; bool CanSave() override; bool StartGame(FNewGameStartup& gs) override; - bool SaveGame(FSaveGameNode*) override; - bool LoadGame(FSaveGameNode*) override; + bool SaveGame() override; + bool LoadGame() override; void QuitToTitle() override; FString GetCoordString() override; ReservedSpace GetReservedScreenSpace(int viewsize) override; diff --git a/source/blood/src/loadsave.cpp b/source/blood/src/loadsave.cpp index a4f6c68af..77924749b 100644 --- a/source/blood/src/loadsave.cpp +++ b/source/blood/src/loadsave.cpp @@ -474,7 +474,7 @@ void LoadSave::Write(void *pData, int nSize) ThrowError("File error #%d writing save file.", errno); } -bool GameInterface::LoadGame(FSaveGameNode* node) +bool GameInterface::LoadGame() { sndKillAllSounds(); sfxKillAllSounds(); @@ -534,7 +534,7 @@ bool GameInterface::LoadGame(FSaveGameNode* node) return true; } -bool GameInterface::SaveGame(FSaveGameNode* node) +bool GameInterface::SaveGame() { LoadSave::hSFile = WriteSavegameChunk("snapshot.bld"); diff --git a/source/blood/src/loadsave.h b/source/blood/src/loadsave.h index e0a598d2c..732a41763 100644 --- a/source/blood/src/loadsave.h +++ b/source/blood/src/loadsave.h @@ -41,8 +41,6 @@ public: virtual void Load(void); void Read(void *, int); void Write(void *, int); - static void LoadGame(FSavegameNode *); - static void SaveGame(FSavegameNode*); }; void LoadSaveSetup(void); diff --git a/source/common/2d/v_draw.cpp b/source/common/2d/v_draw.cpp index c7dcd83d4..c6ca5188b 100644 --- a/source/common/2d/v_draw.cpp +++ b/source/common/2d/v_draw.cpp @@ -1529,6 +1529,19 @@ void DrawFrame(F2DDrawer* twod, PalEntry color, int left, int top, int width, in twod->AddColorOnlyQuad(right, top - offset, offset, height + 2 * offset, color); } +DEFINE_ACTION_FUNCTION(_Screen, DrawLineFrame) +{ + PARAM_PROLOGUE; + PARAM_COLOR(color); + PARAM_INT(left); + PARAM_INT(top); + PARAM_INT(width); + PARAM_INT(height); + PARAM_INT(thickness); + DrawFrame(twod, color, left, top, width, height, thickness); + return 0; +} + void V_CalcCleanFacs(int designwidth, int designheight, int realwidth, int realheight, int* cleanx, int* cleany, int* _cx1, int* _cx2) { if (designheight < 240 && realheight >= 480) designheight = 240; diff --git a/source/common/menu/savegamemanager.cpp b/source/common/menu/savegamemanager.cpp new file mode 100644 index 000000000..935b6dedd --- /dev/null +++ b/source/common/menu/savegamemanager.cpp @@ -0,0 +1,541 @@ +/* +** loadsavemenu.cpp +** The load game and save game menus +** +**--------------------------------------------------------------------------- +** Copyright 2001-2010 Randy Heit +** Copyright 2010-2020 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "menu.h" +#include "version.h" +#include "m_png.h" +#include "filesystem.h" +#include "v_text.h" +#include "gstrings.h" +#include "serializer.h" +#include "vm.h" +#include "i_system.h" +#include "v_video.h" +#include "findfile.h" +#include "v_draw.h" +#include "savegamemanager.h" + + + +//============================================================================= +// +// Save data maintenance +// +//============================================================================= + +void FSavegameManagerBase::ClearSaveGames() +{ + for (unsigned i = 0; ibNoDelete) + delete SaveGames[i]; + } + SaveGames.Clear(); +} + +FSavegameManagerBase::~FSavegameManagerBase() +{ + ClearSaveGames(); +} + +//============================================================================= +// +// Save data maintenance +// +//============================================================================= + +int FSavegameManagerBase::RemoveSaveSlot(int index) +{ + int listindex = SaveGames[0]->bNoDelete ? index - 1 : index; + if (listindex < 0) return index; + + remove(SaveGames[index]->Filename.GetChars()); + UnloadSaveData(); + + FSaveGameNode *file = SaveGames[index]; + + if (quickSaveSlot == SaveGames[index]) + { + quickSaveSlot = nullptr; + } + if (!file->bNoDelete) delete file; + + if (LastSaved == listindex) LastSaved = -1; + else if (LastSaved > listindex) LastSaved--; + if (LastAccessed == listindex) LastAccessed = -1; + else if (LastAccessed > listindex) LastAccessed--; + + SaveGames.Delete(index); + if ((unsigned)index >= SaveGames.Size()) index--; + ExtractSaveData(index); + return index; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveSaveSlot) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); + PARAM_INT(sel); + ACTION_RETURN_INT(self->RemoveSaveSlot(sel)); +} + +//============================================================================= +// +// +// +//============================================================================= + +int FSavegameManagerBase::InsertSaveNode(FSaveGameNode *node) +{ + if (SaveGames.Size() == 0) + { + return SaveGames.Push(node); + } + + if (node->bOldVersion) + { // Add node at bottom of list + return SaveGames.Push(node); + } + else + { // Add node at top of list + unsigned int i = 0; + //if (SaveGames[0] == &NewSaveNode) i++; // To not insert above the "new savegame" dummy entry. + for (; i < SaveGames.Size(); i++) + { + if (SaveGames[i]->bOldVersion || node->SaveTitle.CompareNoCase(SaveGames[i]->SaveTitle) <= 0) + { + break; + } + } + SaveGames.Insert(i, node); + return i; + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void FSavegameManagerBase::NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave, bool forceQuicksave) +{ + FSaveGameNode *node; + + if (file.IsEmpty()) + return; + + ReadSaveStrings(); + + // See if the file is already in our list + for (unsigned i = 0; iFilename.Compare(file) == 0) +#else + if (node->Filename.CompareNoCase(file) == 0) +#endif + { + node->SaveTitle = title; + node->bOldVersion = false; + node->bMissingWads = false; + if (okForQuicksave) + { + if (quickSaveSlot == nullptr || quickSaveSlot == (FSaveGameNode*)1 || forceQuicksave) quickSaveSlot = node; + LastAccessed = LastSaved = i; + } + return; + } + } + + node = new FSaveGameNode; + node->SaveTitle = title; + node->Filename = file; + node->bOldVersion = false; + node->bMissingWads = false; + int index = InsertSaveNode(node); + + if (okForQuicksave) + { + if (quickSaveSlot == nullptr || quickSaveSlot == (FSaveGameNode*)1 || forceQuicksave) quickSaveSlot = node; + LastAccessed = LastSaved = index; + } + else + { + LastAccessed = ++LastSaved; + } +} + + +//============================================================================= +// +// Loads the savegame +// +//============================================================================= + +void FSavegameManagerBase::LoadSavegame(int Selected) +{ + PerformLoadGame(SaveGames[Selected]->Filename.GetChars(), true); + if (quickSaveSlot == (FSaveGameNode*)1) + { + quickSaveSlot = SaveGames[Selected]; + } + M_ClearMenus(); + LastAccessed = Selected; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, LoadSavegame) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); + PARAM_INT(sel); + self->LoadSavegame(sel); + return 0; +} + +//============================================================================= +// +// +// +//============================================================================= + +void FSavegameManagerBase::DoSave(int Selected, const char *savegamestring) +{ + if (Selected != 0) + { + auto node = SaveGames[Selected]; + PerformSaveGame(node->Filename.GetChars(), savegamestring); + } + else + { + // Find an unused filename and save as that + FString filename; + int i; + + for (i = 0;; ++i) + { + filename = BuildSaveName("save", i); + if (!FileExists(filename)) + { + break; + } + } + PerformSaveGame(filename, savegamestring); + } + M_ClearMenus(); +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, DoSave) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); + PARAM_INT(sel); + PARAM_STRING(name); + self->DoSave(sel, name); + return 0; +} + +//============================================================================= +// +// +// +//============================================================================= + +unsigned FSavegameManagerBase::ExtractSaveData(int index) +{ + FResourceFile *resf; + FSaveGameNode *node; + + if (index == -1) + { + if (SaveGames.Size() > 0 && SaveGames[0]->bNoDelete) + { + index = LastSaved + 1; + } + else + { + index = LastAccessed < 0? 0 : LastAccessed; + } + } + + UnloadSaveData(); + + if ((unsigned)index < SaveGames.Size() && + (node = SaveGames[index]) && + !node->Filename.IsEmpty() && + !node->bOldVersion && + (resf = FResourceFile::OpenResourceFile(node->Filename.GetChars(), true)) != nullptr) + { + FResourceLump *info = resf->FindLump("info.json"); + if (info == nullptr) + { + // this should not happen because the file has already been verified. + return index; + } + + void* data = info->Lock(); + FSerializer arc; + if (!arc.OpenReader((const char*)data, info->LumpSize)) + { + info->Unlock(); + return index; + } + info->Unlock(); + + SaveCommentString = ExtractSaveComment(arc); + + FResourceLump *pic = resf->FindLump("savepic.png"); + if (pic != nullptr) + { + FileReader picreader; + + picreader.OpenMemoryArray([=](TArray &array) + { + auto cache = pic->Lock(); + array.Resize(pic->LumpSize); + memcpy(&array[0], cache, pic->LumpSize); + pic->Unlock(); + return true; + }); + PNGHandle *png = M_VerifyPNG(picreader); + if (png != nullptr) + { + SavePic = PNGTexture_CreateFromFile(png, node->Filename); + delete png; + if (SavePic && SavePic->GetDisplayWidth() == 1 && SavePic->GetDisplayHeight() == 1) + { + delete SavePic; + SavePic = nullptr; + } + } + } + delete resf; + } + return index; +} + +//============================================================================= +// +// +// +//============================================================================= + +void FSavegameManagerBase::UnloadSaveData() +{ + if (SavePic != nullptr) + { + delete SavePic; + } + + SaveCommentString = ""; + SavePic = nullptr; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, UnloadSaveData) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); + self->UnloadSaveData(); + return 0; +} + +//============================================================================= +// +// +// +//============================================================================= + +void FSavegameManagerBase::ClearSaveStuff() +{ + UnloadSaveData(); + if (quickSaveSlot == (FSaveGameNode*)1) + { + quickSaveSlot = nullptr; + } +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, ClearSaveStuff) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); + self->ClearSaveStuff(); + return 0; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool FSavegameManagerBase::DrawSavePic(int x, int y, int w, int h) +{ + if (SavePic == nullptr) return false; + DrawTexture(twod, SavePic, x, y, DTA_DestWidth, w, DTA_DestHeight, h, DTA_Masked, false, TAG_DONE); + return true; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, DrawSavePic) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); + PARAM_INT(x); + PARAM_INT(y); + PARAM_INT(w); + PARAM_INT(h); + ACTION_RETURN_BOOL(self->DrawSavePic(x, y, w, h)); +} + +//============================================================================= +// +// +// +//============================================================================= + +void FSavegameManagerBase::SetFileInfo(int Selected) +{ + if (!SaveGames[Selected]->Filename.IsEmpty()) + { + SaveCommentString.Format("File on disk:\n%s", SaveGames[Selected]->Filename.GetChars()); + } +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, SetFileInfo) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); + PARAM_INT(i); + self->SetFileInfo(i); + return 0; +} + + +//============================================================================= +// +// +// +//============================================================================= + +unsigned FSavegameManagerBase::SavegameCount() +{ + return SaveGames.Size(); +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, SavegameCount) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); + ACTION_RETURN_INT(self->SavegameCount()); +} + +//============================================================================= +// +// +// +//============================================================================= + +FSaveGameNode *FSavegameManagerBase::GetSavegame(int i) +{ + return SaveGames[i]; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, GetSavegame) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); + PARAM_INT(i); + ACTION_RETURN_POINTER(self->GetSavegame(i)); +} + +//============================================================================= +// +// +// +//============================================================================= + +void FSavegameManagerBase::InsertNewSaveNode() +{ + NewSaveNode.SaveTitle = GStrings("NEWSAVE"); + NewSaveNode.bNoDelete = true; + SaveGames.Insert(0, &NewSaveNode); +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, InsertNewSaveNode) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); + self->InsertNewSaveNode(); + return 0; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool FSavegameManagerBase::RemoveNewSaveNode() +{ + if (SaveGames[0] == &NewSaveNode) + { + SaveGames.Delete(0); + return true; + } + return false; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveNewSaveNode) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); + ACTION_RETURN_INT(self->RemoveNewSaveNode()); +} + + +DEFINE_ACTION_FUNCTION(FSavegameManager, ReadSaveStrings) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); + self->ReadSaveStrings(); + return 0; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, ExtractSaveData) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase); + PARAM_INT(sel); + ACTION_RETURN_INT(self->ExtractSaveData(sel)); +} + + +DEFINE_FIELD(FSaveGameNode, SaveTitle); +DEFINE_FIELD(FSaveGameNode, Filename); +DEFINE_FIELD(FSaveGameNode, bOldVersion); +DEFINE_FIELD(FSaveGameNode, bMissingWads); +DEFINE_FIELD(FSaveGameNode, bNoDelete); + +DEFINE_FIELD_X(SavegameManager, FSavegameManagerBase, WindowSize); +DEFINE_FIELD_X(SavegameManager, FSavegameManagerBase, quickSaveSlot); +DEFINE_FIELD_X(SavegameManager, FSavegameManagerBase, SaveCommentString); + diff --git a/source/common/menu/savegamemanager.h b/source/common/menu/savegamemanager.h new file mode 100644 index 000000000..754ddce23 --- /dev/null +++ b/source/common/menu/savegamemanager.h @@ -0,0 +1,61 @@ +#pragma once + +#include "zstring.h" +#include "tarray.h" + +class FGameTexture; +class FSerializer; + +// The savegame manager contains too much code that is game specific. Parts are shareable but need more work first. +struct FSaveGameNode +{ + FString SaveTitle; + FString Filename; + bool bOldVersion = false; + bool bMissingWads = false; + bool bNoDelete = false; +}; + +struct FSavegameManagerBase +{ +protected: + TArray SaveGames; + FSaveGameNode NewSaveNode; + int LastSaved = -1; + int LastAccessed = -1; + FGameTexture *SavePic = nullptr; + +public: + int WindowSize = 0; + FString SaveCommentString; + FSaveGameNode *quickSaveSlot = nullptr; + virtual ~FSavegameManagerBase(); + +protected: + int InsertSaveNode(FSaveGameNode *node); + virtual void PerformSaveGame(const char *fn, const char *sgdesc) = 0; + virtual void PerformLoadGame(const char *fn, bool) = 0; + virtual FString ExtractSaveComment(FSerializer &arc) = 0; + virtual FString BuildSaveName(const char* prefix, int slot) = 0; +public: + void NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave, bool forceQuicksave); + void ClearSaveGames(); + + virtual void ReadSaveStrings() = 0; + void UnloadSaveData(); + + int RemoveSaveSlot(int index); + void LoadSavegame(int Selected); + void DoSave(int Selected, const char *savegamestring); + unsigned ExtractSaveData(int index); + void ClearSaveStuff(); + bool DrawSavePic(int x, int y, int w, int h); + void DrawSaveComment(FFont *font, int cr, int x, int y, int scalefactor); + void SetFileInfo(int Selected); + unsigned SavegameCount(); + FSaveGameNode *GetSavegame(int i); + void InsertNewSaveNode(); + bool RemoveNewSaveNode(); + +}; + diff --git a/source/core/gamecontrol.h b/source/core/gamecontrol.h index db8b0d09d..13a96e46c 100644 --- a/source/core/gamecontrol.h +++ b/source/core/gamecontrol.h @@ -22,10 +22,8 @@ extern cycle_t drawtime, actortime, thinktime, gameupdatetime; extern bool r_NoInterpolate; struct MapRecord; -struct FSaveGameNode; extern MapRecord* g_nextmap; extern int g_nextskill; -extern FSaveGameNode* g_savenode; extern FMemArena dump; // this is for memory blocks than cannot be deallocated without some huge effort. Put them in here so that they do not register on shutdown. diff --git a/source/core/gamestate.h b/source/core/gamestate.h index a18921542..6072f8168 100644 --- a/source/core/gamestate.h +++ b/source/core/gamestate.h @@ -40,7 +40,8 @@ enum gameaction_t : int ga_savegame, // save the game ga_autosave, // autosave the game (for triggering a save from within the game.) ga_completed, // Level was exited. - ga_nextlevel // Actually start the next level. + ga_nextlevel, // Actually start the next level. + ga_loadgamehidecon }; extern gamestate_t gamestate; extern gameaction_t gameaction; diff --git a/source/core/gamestruct.h b/source/core/gamestruct.h index dc95c80ac..e7e05a2e3 100644 --- a/source/core/gamestruct.h +++ b/source/core/gamestruct.h @@ -33,21 +33,6 @@ struct FSavegameInfo int currentsavever; }; -struct FSaveGameNode -{ - FString SaveTitle; - FString Filename; - bool bOldVersion = false; - bool bMissingWads = false; - bool bNoDelete = false; - bool bIsExt = false; - - bool isValid() const - { - return Filename.IsNotEmpty() && !bOldVersion && !bMissingWads; - } -}; - struct ReservedSpace { int top; @@ -83,8 +68,8 @@ struct GameInterface virtual bool DrawSpecialScreen(const DVector2 &origin, int tilenum) { return false; } virtual void DrawCenteredTextScreen(const DVector2& origin, const char* text, int position, bool withbg = true); virtual double SmallFontScale() { return 1; } - virtual bool SaveGame(FSaveGameNode*) { return true; } - virtual bool LoadGame(FSaveGameNode*) { return true; } + virtual bool SaveGame() { return true; } + virtual bool LoadGame() { return true; } virtual void SerializeGameState(FSerializer& arc) {} virtual void DrawPlayerSprite(const DVector2& origin, bool onteam) {} virtual void QuitToTitle() {} diff --git a/source/core/menu/loadsavemenu.cpp b/source/core/menu/loadsavemenu.cpp index e2e560cb5..8e3a025c0 100644 --- a/source/core/menu/loadsavemenu.cpp +++ b/source/core/menu/loadsavemenu.cpp @@ -4,7 +4,7 @@ ** **--------------------------------------------------------------------------- ** Copyright 2001-2010 Randy Heit -** Copyright 2010-2017 Christoph Oelckers +** Copyright 2010-2020 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -45,103 +45,7 @@ #include "v_video.h" #include "findfile.h" #include "v_draw.h" - -// Save name length limit for old binary formats. -#define OLDSAVESTRINGSIZE 24 - -//============================================================================= -// -// Save data maintenance -// -//============================================================================= - -void FSavegameManager::ClearSaveGames() -{ - for (unsigned i = 0; ibNoDelete) - delete SaveGames[i]; - } - SaveGames.Clear(); -} - -FSavegameManager::~FSavegameManager() -{ - ClearSaveGames(); -} - -//============================================================================= -// -// Save data maintenance -// -//============================================================================= - -int FSavegameManager::RemoveSaveSlot(int index) -{ - int listindex = SaveGames[0]->bNoDelete ? index - 1 : index; - if (listindex < 0) return index; - - remove(SaveGames[index]->Filename.GetChars()); - UnloadSaveData(); - - FSaveGameNode *file = SaveGames[index]; - - if (quickSaveSlot == SaveGames[index]) - { - quickSaveSlot = nullptr; - } - if (!file->bNoDelete) delete file; - - if (LastSaved == listindex) LastSaved = -1; - else if (LastSaved > listindex) LastSaved--; - if (LastAccessed == listindex) LastAccessed = -1; - else if (LastAccessed > listindex) LastAccessed--; - - SaveGames.Delete(index); - if ((unsigned)index >= SaveGames.Size()) index--; - ExtractSaveData(index); - return index; -} - -DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveSaveSlot) -{ - PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); - PARAM_INT(sel); - ACTION_RETURN_INT(self->RemoveSaveSlot(sel)); -} - - -//============================================================================= -// -// -// -//============================================================================= - -int FSavegameManager::InsertSaveNode(FSaveGameNode *node) -{ - if (SaveGames.Size() == 0) - { - return SaveGames.Push(node); - } - - if (node->bOldVersion) - { // Add node at bottom of list - return SaveGames.Push(node); - } - else - { // Add node at top of list - unsigned int i; - for (i = 0; i < SaveGames.Size(); i++) - { - if (SaveGames[i]->bOldVersion || node->SaveTitle.CompareNoCase(SaveGames[i]->SaveTitle) <= 0) - { - break; - } - } - SaveGames.Insert(i, node); - return i; - } -} +#include "savegamehelp.h" //============================================================================= // @@ -153,7 +57,6 @@ int FSavegameManager::InsertSaveNode(FSaveGameNode *node) void FSavegameManager::ReadSaveStrings() { -#if 0 if (SaveGames.Size() == 0) { void *filefirst; @@ -162,235 +65,46 @@ void FSavegameManager::ReadSaveStrings() LastSaved = LastAccessed = -1; quickSaveSlot = nullptr; - filter = G_BuildSaveName("*." SAVEGAME_EXT, -1); + filter = G_BuildSaveName("*"); filefirst = I_FindFirst(filter.GetChars(), &c_file); if (filefirst != ((void *)(-1))) { do { // I_FindName only returns the file's name and not its full path - FString filepath = G_BuildSaveName(I_FindName(&c_file), -1); + FString filepath = G_BuildSaveName(I_FindName(&c_file)); - std::unique_ptr savegame(FResourceFile::OpenResourceFile(filepath, true, true)); + FResourceFile *savegame = FResourceFile::OpenResourceFile(filepath, true, true); if (savegame != nullptr) { - bool oldVer = false; - bool missing = false; 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; } - void *data = info->Lock(); - FSerializer arc; - if (arc.OpenReader((const char *)data, info->LumpSize)) + auto fr = info->NewReader(); + FString title; + int check = G_ValidateSavegame(fr, &title, true); + fr.Close(); + delete savegame; + if (check != 0) { - int savever = 0; - arc("Save Version", savever); - FString engine = arc.GetString("Engine"); - FString iwad = arc.GetString("Game WAD"); - FString title = arc.GetString("Title"); - - - if (engine.Compare(GAMESIG) != 0 || savever > SAVEVER) - { - // different engine or newer version: - // not our business. Leave it alone. - continue; - } - - if (savever < MINSAVEVER) - { - // old, incompatible savegame. List as not usable. - oldVer = true; - } - else if (iwad.CompareNoCase(fileSystem.GetResourceFileName(fileSystem.GetIwadNum())) == 0) - { - missing = !G_CheckSaveGameWads(arc, false); - } - else - { - // different game. Skip this. - continue; - } - FSaveGameNode *node = new FSaveGameNode; node->Filename = filepath; - node->bOldVersion = oldVer; - node->bMissingWads = missing; + node->bOldVersion = check == -1; + node->bMissingWads = check == -2; node->SaveTitle = title; InsertSaveNode(node); } - } - else // check for old formats. - { - FileReader file; - if (file.OpenFile(filepath)) - { - PNGHandle *png; - char sig[16]; - char title[OLDSAVESTRINGSIZE + 1]; - 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. - - title[OLDSAVESTRINGSIZE] = 0; - - if (nullptr != (png = M_VerifyPNG(file))) - { - char *ver = M_GetPNGText(png, "ZDoom Save Version"); - if (ver != nullptr) - { - // An old version - if (!M_GetPNGText(png, "Title", title, OLDSAVESTRINGSIZE)) - { - strncpy(title, I_FindName(&c_file), OLDSAVESTRINGSIZE); - } - addIt = true; - delete[] ver; - } - delete png; - } - else - { - file.Seek(0, FileReader::SeekSet); - if (file.Read(sig, 16) == 16) - { - - if (strncmp(sig, "ZDOOMSAVE", 9) == 0) - { - if (file.Read(title, OLDSAVESTRINGSIZE) == OLDSAVESTRINGSIZE) - { - addIt = true; - } - } - else - { - memcpy(title, sig, 16); - if (file.Read(title + 16, OLDSAVESTRINGSIZE - 16) == OLDSAVESTRINGSIZE - 16 && - file.Read(sig, 16) == 16 && - strncmp(sig, "ZDOOMSAVE", 9) == 0) - { - addIt = true; - } - } - } - } - - if (addIt) - { - FSaveGameNode *node = new FSaveGameNode; - node->Filename = filepath; - node->bOldVersion = true; - node->bMissingWads = false; - node->SaveTitle = title; - InsertSaveNode(node); - } - } - } - } while (I_FindNext(filefirst, &c_file) == 0); - I_FindClose(filefirst); + } while (I_FindNext (filefirst, &c_file) == 0); + I_FindClose (filefirst); } } -#endif } -DEFINE_ACTION_FUNCTION(FSavegameManager, ReadSaveStrings) -{ - PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); - self->ReadSaveStrings(); - return 0; -} - - -//============================================================================= -// -// -// -//============================================================================= - -void FSavegameManager::NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave, bool forceQuicksave) -{ - FSaveGameNode *node; - - if (file.IsEmpty()) - return; - - ReadSaveStrings(); - - // See if the file is already in our list - for (unsigned i = 0; iFilename.Compare(file) == 0) -#else - if (node->Filename.CompareNoCase(file) == 0) -#endif - { - node->SaveTitle = title; - node->bOldVersion = false; - node->bMissingWads = false; - if (okForQuicksave) - { - if (quickSaveSlot == nullptr || quickSaveSlot == (FSaveGameNode*)1 || forceQuicksave) quickSaveSlot = node; - LastAccessed = LastSaved = i; - } - return; - } - } - - node = new FSaveGameNode; - node->SaveTitle = title; - node->Filename = file; - node->bOldVersion = false; - node->bMissingWads = false; - int index = InsertSaveNode(node); - - if (okForQuicksave) - { - if (quickSaveSlot == nullptr || quickSaveSlot == (FSaveGameNode*)1 || forceQuicksave) quickSaveSlot = node; - LastAccessed = LastSaved = index; - } - else - { - LastAccessed = ++LastSaved; - } -} - -//============================================================================= -// -// Loads the savegame -// -//============================================================================= - -void FSavegameManager::LoadSavegame(int Selected) -{ - //G_LoadGame(SaveGames[Selected]->Filename.GetChars(), true); - if (quickSaveSlot == (FSaveGameNode*)1) - { - quickSaveSlot = SaveGames[Selected]; - } - M_ClearMenus(); - LastAccessed = Selected; -} - -DEFINE_ACTION_FUNCTION(FSavegameManager, LoadSavegame) -{ - PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); - PARAM_INT(sel); - self->LoadSavegame(sel); - return 0; -} //============================================================================= // @@ -398,41 +112,19 @@ DEFINE_ACTION_FUNCTION(FSavegameManager, LoadSavegame) // //============================================================================= -void FSavegameManager::DoSave(int Selected, const char *savegamestring) +void FSavegameManager::PerformLoadGame(const char *f, bool s) { -#if 0 - if (Selected != 0) - { - auto node = SaveGames[Selected]; - G_SaveGame(node->Filename.GetChars(), savegamestring); - } - else - { - // Find an unused filename and save as that - FString filename; - int i; - - for (i = 0;; ++i) - { - filename = G_BuildSaveName("save", i); - if (!FileExists(filename)) - { - break; - } - } - G_SaveGame(filename, savegamestring); - } -#endif - M_ClearMenus(); + G_LoadGame(f); } -DEFINE_ACTION_FUNCTION(FSavegameManager, DoSave) +void FSavegameManager::PerformSaveGame(const char *f, const char *s) { - PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); - PARAM_INT(sel); - PARAM_STRING(name); - self->DoSave(sel, name); - return 0; + G_SaveGame(f, s, false, false); +} + +FString FSavegameManager::BuildSaveName(const char* fn, int slot) +{ + return G_BuildSaveName(FStringf("%s%04d", fn, slot)); } //============================================================================= @@ -441,259 +133,19 @@ DEFINE_ACTION_FUNCTION(FSavegameManager, DoSave) // //============================================================================= -unsigned FSavegameManager::ExtractSaveData(int index) +FString FSavegameManager::ExtractSaveComment(FSerializer& arc) { - FResourceFile *resf; - FSaveGameNode *node; + FString comment, fcomment, ncomment, mtime; - if (index == -1) - { - if (SaveGames.Size() > 0 && SaveGames[0]->bNoDelete) - { - index = LastSaved + 1; - } - else - { - index = LastAccessed < 0? 0 : LastAccessed; - } - } + arc("Creation Time", comment) + ("Map Label", fcomment) + ("Map Name", ncomment) + ("Map Time", mtime); - UnloadSaveData(); - - if ((unsigned)index < SaveGames.Size() && - (node = SaveGames[index]) && - !node->Filename.IsEmpty() && - !node->bOldVersion && - (resf = FResourceFile::OpenResourceFile(node->Filename.GetChars(), true)) != nullptr) - { - FResourceLump *info = resf->FindLump("info.json"); - if (info == nullptr) - { - // this should not happen because the file has already been verified. - return index; - } - void *data = info->Lock(); - FSerializer arc; - if (arc.OpenReader((const char *)data, info->LumpSize)) - { - FString comment; - - FString time = arc.GetString("Creation Time"); - FString pcomment = arc.GetString("Comment"); - - comment = time; - if (time.Len() > 0) comment += "\n"; - comment += pcomment; - SaveCommentString = comment; - - // Extract pic - FResourceLump *pic = resf->FindLump("savepic.png"); - if (pic != nullptr) - { - FileReader picreader; - - picreader.OpenMemoryArray([=](TArray &array) - { - auto cache = pic->Lock(); - array.Resize(pic->LumpSize); - memcpy(&array[0], cache, pic->LumpSize); - return true; - }); - PNGHandle *png = M_VerifyPNG(picreader); - if (png != nullptr) - { - SavePic = PNGTexture_CreateFromFile(png, node->Filename); - delete png; - if (SavePic && SavePic->GetDisplayWidth() == 1 && SavePic->GetDisplayHeight() == 1) - { - delete SavePic; - SavePic = nullptr; - } - } - } - } - delete resf; - } - return index; + comment.AppendFormat("\n%s - %s\n%s", fcomment.GetChars(), ncomment.GetChars(), mtime.GetChars()); + return comment; } -DEFINE_ACTION_FUNCTION(FSavegameManager, ExtractSaveData) -{ - PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); - PARAM_INT(sel); - ACTION_RETURN_INT(self->ExtractSaveData(sel)); -} - -//============================================================================= -// -// -// -//============================================================================= - -void FSavegameManager::UnloadSaveData() -{ - if (SavePic != nullptr) - { - delete SavePic; - } - - SaveCommentString = ""; - SavePic = nullptr; -} - -DEFINE_ACTION_FUNCTION(FSavegameManager, UnloadSaveData) -{ - PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); - self->UnloadSaveData(); - return 0; -} - -//============================================================================= -// -// -// -//============================================================================= - -void FSavegameManager::ClearSaveStuff() -{ - UnloadSaveData(); - if (quickSaveSlot == (FSaveGameNode*)1) - { - quickSaveSlot = nullptr; - } -} - -DEFINE_ACTION_FUNCTION(FSavegameManager, ClearSaveStuff) -{ - PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); - self->ClearSaveStuff(); - return 0; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool FSavegameManager::DrawSavePic(int x, int y, int w, int h) -{ - if (SavePic == nullptr) return false; - DrawTexture(twod, SavePic, x, y, DTA_DestWidth, w, DTA_DestHeight, h, DTA_Masked, false, TAG_DONE); - return true; -} - -DEFINE_ACTION_FUNCTION(FSavegameManager, DrawSavePic) -{ - PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); - PARAM_INT(x); - PARAM_INT(y); - PARAM_INT(w); - PARAM_INT(h); - ACTION_RETURN_BOOL(self->DrawSavePic(x, y, w, h)); -} - -//============================================================================= -// -// -// -//============================================================================= - -void FSavegameManager::SetFileInfo(int Selected) -{ - if (!SaveGames[Selected]->Filename.IsEmpty()) - { - SaveCommentString.Format("File on disk:\n%s", SaveGames[Selected]->Filename.GetChars()); - } -} - -DEFINE_ACTION_FUNCTION(FSavegameManager, SetFileInfo) -{ - PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); - PARAM_INT(i); - self->SetFileInfo(i); - return 0; -} - - -//============================================================================= -// -// -// -//============================================================================= - -unsigned FSavegameManager::SavegameCount() -{ - return SaveGames.Size(); -} - -DEFINE_ACTION_FUNCTION(FSavegameManager, SavegameCount) -{ - PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); - ACTION_RETURN_INT(self->SavegameCount()); -} - -//============================================================================= -// -// -// -//============================================================================= - -FSaveGameNode *FSavegameManager::GetSavegame(int i) -{ - return SaveGames[i]; -} - -DEFINE_ACTION_FUNCTION(FSavegameManager, GetSavegame) -{ - PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); - PARAM_INT(i); - ACTION_RETURN_POINTER(self->GetSavegame(i)); -} - -//============================================================================= -// -// -// -//============================================================================= - -void FSavegameManager::InsertNewSaveNode() -{ - NewSaveNode.SaveTitle = GStrings["NEWSAVE"]; - NewSaveNode.bNoDelete = true; - SaveGames.Insert(0, &NewSaveNode); -} - -DEFINE_ACTION_FUNCTION(FSavegameManager, InsertNewSaveNode) -{ - PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); - self->InsertNewSaveNode(); - return 0; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool FSavegameManager::RemoveNewSaveNode() -{ - if (SaveGames[0] == &NewSaveNode) - { - SaveGames.Delete(0); - return true; - } - return false; -} - -DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveNewSaveNode) -{ - PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); - ACTION_RETURN_INT(self->RemoveNewSaveNode()); -} - - FSavegameManager savegameManager; DEFINE_ACTION_FUNCTION(FSavegameManager, GetManager) @@ -702,15 +154,3 @@ DEFINE_ACTION_FUNCTION(FSavegameManager, GetManager) ACTION_RETURN_POINTER(&savegameManager); } - - -DEFINE_FIELD(FSaveGameNode, SaveTitle); -DEFINE_FIELD(FSaveGameNode, Filename); -DEFINE_FIELD(FSaveGameNode, bOldVersion); -DEFINE_FIELD(FSaveGameNode, bMissingWads); -DEFINE_FIELD(FSaveGameNode, bNoDelete); - -DEFINE_FIELD(FSavegameManager, WindowSize); -DEFINE_FIELD(FSavegameManager, quickSaveSlot); -DEFINE_FIELD(FSavegameManager, SaveCommentString); - diff --git a/source/core/menu/razemenu.cpp b/source/core/menu/razemenu.cpp index 29ddfb6ee..cd25fcfae 100644 --- a/source/core/menu/razemenu.cpp +++ b/source/core/menu/razemenu.cpp @@ -61,6 +61,8 @@ #include "razemenu.h" #include "mapinfo.h" #include "statistics.h" +#include "i_net.h" +#include "savegamehelp.h" EXTERN_CVAR(Int, cl_gfxlocalization) EXTERN_CVAR(Bool, m_quickexit) @@ -131,12 +133,12 @@ bool M_SetSpecialMenu(FName& menu, int param) case NAME_Quitmenu: // This is no separate class C_DoCommand("menu_quit"); - return true; + return false; case NAME_EndGameMenu: // This is no separate class C_DoCommand("menu_endgame"); - return true; + return false; } // End of special checks @@ -251,54 +253,43 @@ CCMD(menu_endgame) // //============================================================================= -CCMD (quicksave) + +//============================================================================= +// +// +// +//============================================================================= + +CCMD(quicksave) { // F6 -#if 0 - if (!usergame || (players[consoleplayer].health <= 0 && !multiplayer)) - { - S_Sound (CHAN_VOICE, CHANF_UI, "menu/invalid", snd_menuvolume, ATTN_NONE); - return; - } + if (!gi->CanSave()) return; - if (gamestate != GS_LEVEL) - return; - - // If the quick save rotation is enabled, it handles the save slot. - if (quicksaverotation) - { - G_DoQuickSave(); - return; - } - if (savegameManager.quickSaveSlot == NULL || savegameManager.quickSaveSlot == (FSaveGameNode*)1) { - S_Sound(CHAN_VOICE, CHANF_UI, "menu/activate", snd_menuvolume, ATTN_NONE); - M_StartControlPanel(false); + M_StartControlPanel(true); M_SetMenu(NAME_Savegamemenu); return; } - + + auto slot = savegameManager.quickSaveSlot; + // [mxd]. Just save the game, no questions asked. if (!saveloadconfirmation) { - G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->SaveTitle.GetChars()); + G_SaveGame(savegameManager.quickSaveSlot->Filename, savegameManager.quickSaveSlot->SaveTitle, true, true); return; } - S_Sound(CHAN_VOICE, CHANF_UI, "menu/activate", snd_menuvolume, ATTN_NONE); - FString tempstring = GStrings("QSPROMPT"); - tempstring.Substitute("%s", savegameManager.quickSaveSlot->SaveTitle.GetChars()); + tempstring.Substitute("%s", slot->SaveTitle.GetChars()); + M_StartControlPanel(true); - DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []() - { - G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->SaveTitle.GetChars()); - S_Sound(CHAN_VOICE, CHANF_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); - M_ClearMenus(); - }); + DMenu* newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []() + { + G_SaveGame(savegameManager.quickSaveSlot->Filename, savegameManager.quickSaveSlot->SaveTitle, true, true); + }); M_ActivateMenu(newmenu); -#endif } //============================================================================= @@ -307,21 +298,20 @@ CCMD (quicksave) // //============================================================================= -CCMD (quickload) +CCMD(quickload) { // F9 -#if 0 if (netgame) { M_StartControlPanel(true); - M_StartMessage (GStrings("QLOADNET"), 1); + M_StartMessage(GStrings("QLOADNET"), 1); return; } - - if (savegameManager.quickSaveSlot == NULL || savegameManager.quickSaveSlot == (FSaveGameNode*)1) + + if (savegameManager.quickSaveSlot == nullptr || savegameManager.quickSaveSlot == (FSaveGameNode*)1) { M_StartControlPanel(true); // signal that whatever gets loaded should be the new quicksave - savegameManager.quickSaveSlot = (FSaveGameNode *)1; + savegameManager.quickSaveSlot = (FSaveGameNode*)1; M_SetMenu(NAME_Loadgamemenu); return; } @@ -329,7 +319,7 @@ CCMD (quickload) // [mxd]. Just load the game, no questions asked. if (!saveloadconfirmation) { - G_LoadGame(savegameManager.quickSaveSlot->Filename.GetChars()); + G_LoadGame(savegameManager.quickSaveSlot->Filename); return; } FString tempstring = GStrings("QLPROMPT"); @@ -337,14 +327,11 @@ CCMD (quickload) M_StartControlPanel(true); - DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []() - { - G_LoadGame(savegameManager.quickSaveSlot->Filename.GetChars()); - S_Sound(CHAN_VOICE, CHANF_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); - M_ClearMenus(); + DMenu* newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []() + { + G_LoadGame(savegameManager.quickSaveSlot->Filename); }); M_ActivateMenu(newmenu); -#endif } //============================================================================= diff --git a/source/core/menu/razemenu.h b/source/core/menu/razemenu.h index 785df0fe5..25a16b28f 100644 --- a/source/core/menu/razemenu.h +++ b/source/core/menu/razemenu.h @@ -2,6 +2,7 @@ #include "menu.h" #include "gamestruct.h" #include "c_cvars.h" +#include "savegamemanager.h" extern bool help_disabled; @@ -14,45 +15,13 @@ void M_StartupSkillMenu(FNewGameStartup *gs); void SetDefaultMenuColors(); void BuildGameMenus(); -// The savegame manager contains too much code that is game specific. Parts are shareable but need more work first. - -struct FSavegameManager +class FSavegameManager : public FSavegameManagerBase { -private: - TArray SaveGames; - FSaveGameNode NewSaveNode; - int LastSaved = -1; - int LastAccessed = -1; - FGameTexture *SavePic = nullptr; - -public: - int WindowSize = 0; - FString SaveCommentString; - FSaveGameNode *quickSaveSlot = nullptr; - ~FSavegameManager(); - -private: - int InsertSaveNode(FSaveGameNode *node); -public: - void NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave, bool forceQuicksave); - void ClearSaveGames(); - - void ReadSaveStrings(); - void UnloadSaveData(); - - int RemoveSaveSlot(int index); - void LoadSavegame(int Selected); - void DoSave(int Selected, const char *savegamestring); - unsigned ExtractSaveData(int index); - void ClearSaveStuff(); - bool DrawSavePic(int x, int y, int w, int h); - void DrawSaveComment(FFont *font, int cr, int x, int y, int scalefactor); - void SetFileInfo(int Selected); - unsigned SavegameCount(); - FSaveGameNode *GetSavegame(int i); - void InsertNewSaveNode(); - bool RemoveNewSaveNode(); - + void PerformSaveGame(const char *fn, const char *sgdesc) override; + void PerformLoadGame(const char *fn, bool) override; + FString ExtractSaveComment(FSerializer &arc) override; + FString BuildSaveName(const char* prefix, int slot) override; + void ReadSaveStrings() override; }; extern FSavegameManager savegameManager; diff --git a/source/core/savegamehelp.cpp b/source/core/savegamehelp.cpp index 26dad0812..59579fb7d 100644 --- a/source/core/savegamehelp.cpp +++ b/source/core/savegamehelp.cpp @@ -61,6 +61,7 @@ static FResourceFile *savereader; void LoadEngineState(); void SaveEngineState(); void WriteSavePic(FileWriter* file, int width, int height); +extern FString BackupSaveGame; CVAR(String, cl_savedir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -592,6 +593,48 @@ static int nextquicksave = -1; self = 1; } + void DoLoadGame(const char* name) + { + if (OpenSaveGameForRead(name)) + { + if (gi->LoadGame()) + { + gameaction = ga_level; + } + else + { + I_Error("%s: Failed to load savegame", name); + } + } + else + { + I_Error("%s: Failed to open savegame", name); + } + } + + + void G_LoadGame(const char *filename) + { + inputState.ClearAllInput(); + gi->FreeLevelData(); + DoLoadGame(filename); + BackupSaveGame = filename; + } + + void G_SaveGame(const char *fn, const char *desc, bool ok4q, bool forceq) + { + if (OpenSaveGameForWrite(fn, desc)) + { + if (gi->SaveGame() && FinishSavegameWrite()) + { + savegameManager.NotifyNewSave(fn, desc, ok4q, forceq); + Printf(PRINT_NOTIFY, "%s\n", GStrings("GAME SAVED")); + BackupSaveGame = fn; + } + } + } + + void M_Autosave() { if (disableautosave) return; @@ -611,12 +654,11 @@ void M_Autosave() num.Int = nextautosave; autosavenum.ForceSet(num, CVAR_Int); - FSaveGameNode sg; - sg.Filename = G_BuildSaveName(FStringf("auto%04d", nextautosave)); + auto Filename = G_BuildSaveName(FStringf("auto%04d", nextautosave)); readableTime = myasctime(); - sg.SaveTitle.Format("Autosave %s", readableTime); + FStringf SaveTitle("Autosave %s", readableTime); nextautosave = (nextautosave + 1) % count; - //savegameManager.SaveGame(&sg, false, false); + G_SaveGame(Filename, SaveTitle, false, false); } CCMD(autosave) @@ -643,11 +685,11 @@ CCMD(rotatingquicksave) quicksavenum.ForceSet(num, CVAR_Int); FSaveGameNode sg; - sg.Filename = G_BuildSaveName(FStringf("quick%04d", nextquicksave)); + auto Filename = G_BuildSaveName(FStringf("quick%04d", nextquicksave)); readableTime = myasctime(); - sg.SaveTitle.Format("Quicksave %s", readableTime); + FStringf SaveTitle("Quicksave %s", readableTime); nextquicksave = (nextquicksave + 1) % count; - //savegameManager.SaveGame(&sg, false, false); + G_SaveGame(Filename, SaveTitle, false, false); } diff --git a/source/core/savegamehelp.h b/source/core/savegamehelp.h index 23c9a844f..ee086fc74 100644 --- a/source/core/savegamehelp.h +++ b/source/core/savegamehelp.h @@ -17,6 +17,9 @@ class FileReader; FString G_BuildSaveName (const char *prefix); int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu); +void G_LoadGame(const char* filename); +void G_SaveGame(const char* fn, const char* desc, bool ok4q, bool forceq); + void SaveEngineState(); void LoadEngineState(); void M_Autosave(); diff --git a/source/exhumed/src/exhumed.h b/source/exhumed/src/exhumed.h index 9e531724c..7f287d578 100644 --- a/source/exhumed/src/exhumed.h +++ b/source/exhumed/src/exhumed.h @@ -243,8 +243,8 @@ struct GameInterface : ::GameInterface void MenuClosed() override; bool StartGame(FNewGameStartup& gs) override; FSavegameInfo GetSaveSig() override; - bool LoadGame(FSaveGameNode* sv) override; - bool SaveGame(FSaveGameNode* sv) override; + bool LoadGame() override; + bool SaveGame() override; bool CanSave() override; ReservedSpace GetReservedScreenSpace(int viewsize) override { return { 0, 24 }; } void QuitToTitle() override; diff --git a/source/exhumed/src/save.cpp b/source/exhumed/src/save.cpp index 6fd056eb5..1f530302b 100644 --- a/source/exhumed/src/save.cpp +++ b/source/exhumed/src/save.cpp @@ -34,14 +34,14 @@ void LoadTextureState(); static TArray sghelpers(TArray::NoInit); -bool GameInterface::SaveGame(FSaveGameNode* sv) +bool GameInterface::SaveGame() { for (auto sgh : sghelpers) sgh->Save(); SaveTextureState(); return 1; // CHECKME } -bool GameInterface::LoadGame(FSaveGameNode* sv) +bool GameInterface::LoadGame() { for (auto sgh : sghelpers) sgh->Load(); diff --git a/source/sw/src/game.h b/source/sw/src/game.h index a10ef9f65..c9b20b9a7 100644 --- a/source/sw/src/game.h +++ b/source/sw/src/game.h @@ -2193,8 +2193,8 @@ struct GameInterface : ::GameInterface bool CanSave() override; bool StartGame(FNewGameStartup& gs) override; FSavegameInfo GetSaveSig() override; - bool LoadGame(FSaveGameNode* sv) override; - bool SaveGame(FSaveGameNode* sv) override; + bool LoadGame() override; + bool SaveGame() override; void SetAmbience(bool on) override { if (on) StartAmbientSound(); else StopAmbientSound(); } FString GetCoordString() override; ReservedSpace GetReservedScreenSpace(int viewsize) override; diff --git a/source/sw/src/save.cpp b/source/sw/src/save.cpp index b7e15ada6..51b26950e 100644 --- a/source/sw/src/save.cpp +++ b/source/sw/src/save.cpp @@ -204,7 +204,7 @@ int LoadSymCodeInfo(MFILE_READ fil, void **ptr) -bool GameInterface::SaveGame(FSaveGameNode *sv) +bool GameInterface::SaveGame() { MFILE_WRITE fil; int i,j; @@ -643,7 +643,7 @@ bool GameInterface::SaveGame(FSaveGameNode *sv) } -bool GameInterface::LoadGame(FSaveGameNode* sv) +bool GameInterface::LoadGame() { MFILE_READ fil; int i,j,saveisshot=0; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 88c640838..dfda19da6 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -377,11 +377,9 @@ ImageScroller "CreditsMenu" ListMenu "LoadGameMenu" { - /* - Caption "$MNU_LOADGAME" Position 0, 40 Class "LoadMenu" // uses its own implementation - */ + CaptionItem "$MNU_LOADGAME" } //------------------------------------------------------------------------------------------- @@ -392,11 +390,9 @@ ListMenu "LoadGameMenu" ListMenu "SaveGameMenu" { - /* - Caption "$MNU_SAVEGAME" Position 0, 40 Class "SaveMenu" // uses its own implementation - */ + CaptionItem "$MNU_SAVEGAME" } //------------------------------------------------------------------------------------------- diff --git a/wadsrc/static/zscript/base.zs b/wadsrc/static/zscript/base.zs index b79630b81..17f9e680f 100644 --- a/wadsrc/static/zscript/base.zs +++ b/wadsrc/static/zscript/base.zs @@ -389,6 +389,7 @@ struct Screen native native static vararg void DrawChar(Font font, int normalcolor, double x, double y, int character, ...); native static vararg void DrawText(Font font, int normalcolor, double x, double y, String text, ...); native static void DrawLine(int x0, int y0, int x1, int y1, Color color, int alpha = 255); + native static void DrawLineFrame(Color color, int x0, int y0, int w, int h, int thickness = 1); native static void DrawThickLine(int x0, int y0, int x1, int y1, double thickness, Color color, int alpha = 255); native static Vector2, Vector2 VirtualToRealCoords(Vector2 pos, Vector2 size, Vector2 vsize, bool vbottom=false, bool handleaspect=true); native static double GetAspectRatio(); diff --git a/wadsrc/static/zscript/constants.zs b/wadsrc/static/zscript/constants.zs index 9814c7837..3110cee4a 100644 --- a/wadsrc/static/zscript/constants.zs +++ b/wadsrc/static/zscript/constants.zs @@ -6,3 +6,25 @@ enum ETranslationTable TRANSLATION_Remap, }; +enum gameaction_t : int +{ + ga_nothing, + ga_level, // Switch to play mode without any initialization + ga_intro, + ga_intermission, + + ga_startup, // go back to intro after uninitializing the game state + ga_mainmenu, // go back to main menu after uninitializing the game state + ga_mainmenunostopsound, // Same but doesn't stop playing sounds. + ga_creditsmenu, // go to the credits menu after uninitializing the game state + ga_newgame, // start a new game + ga_recordgame, // start a new demo recording (later) + ga_loadgame, // load a savegame and resume play. + ga_loadgameplaydemo, // load a savegame and play a demo. + ga_autoloadgame, // load last autosave and resume play. + ga_savegame, // save the game + ga_autosave, // autosave the game (for triggering a save from within the game.) + ga_completed, // Level was exited. + ga_nextlevel, // Actually start the next level. + ga_loadgamehidecon +}; diff --git a/wadsrc/static/zscript/ui/menu/loadsavemenu.zs b/wadsrc/static/zscript/ui/menu/loadsavemenu.zs index 92273bfdf..837970a9c 100644 --- a/wadsrc/static/zscript/ui/menu/loadsavemenu.zs +++ b/wadsrc/static/zscript/ui/menu/loadsavemenu.zs @@ -91,14 +91,11 @@ class LoadSaveMenu : ListMenu int listboxRows; int listboxHeight; int listboxRight; - int listboxBottom; int commentLeft; int commentTop; int commentWidth; int commentHeight; - int commentRight; - int commentBottom; int commentRows; bool mEntering; @@ -118,33 +115,34 @@ class LoadSaveMenu : ListMenu override void Init(Menu parent, ListMenuDescriptor desc) { Super.Init(parent, desc); + bool aspect43 = true; + int Width43 = screen.GetHeight() * 4 / 3; + int Left43 = (screen.GetWidth() - Width43) / 2; + manager = SavegameManager.GetManager(); manager.ReadSaveStrings(); + double wScale = Width43 / 640.; - savepicLeft = 10; - savepicTop = 54*CleanYfac; - savepicWidth = 216*screen.GetWidth() / 640; - savepicHeight = 135*screen.GetHeight() / 400; + savepicLeft = Left43 + int(20 * wScale); + savepicTop = mDesc.mYpos * screen.GetHeight() / 200 ; + savepicWidth = int(240 * wScale); + savepicHeight = int(180 * wScale); FontScale = max(screen.GetHeight() / 480, 1); rowHeight = int(max((NewConsoleFont.GetHeight() + 1) * FontScale, 1)); - listboxLeft = savepicLeft + savepicWidth + 14; + listboxLeft = savepicLeft + savepicWidth + int(20*wScale); listboxTop = savepicTop; - listboxWidth = screen.GetWidth() - listboxLeft - 10; - int listboxHeight1 = screen.GetHeight() - listboxTop - 10; + listboxWidth = Width43 + Left43 - listboxLeft - int(30 * wScale); + int listboxHeight1 = screen.GetHeight() - listboxTop - int(20*wScale); listboxRows = (listboxHeight1 - 1) / rowHeight; listboxHeight = listboxRows * rowHeight + 1; listboxRight = listboxLeft + listboxWidth; - listboxBottom = listboxTop + listboxHeight; commentLeft = savepicLeft; - commentTop = savepicTop + savepicHeight + 16; + commentTop = savepicTop + savepicHeight + int(16 * wScale); commentWidth = savepicWidth; - //commentHeight = (51+(screen.GetHeight()>200?10:0))*CleanYfac; - commentHeight = listboxHeight - savepicHeight - 16; - commentRight = commentLeft + commentWidth; - commentBottom = commentTop + commentHeight; + commentHeight = listboxHeight - savepicHeight - int(16 * wScale); commentRows = commentHeight / rowHeight; } @@ -169,7 +167,8 @@ class LoadSaveMenu : ListMenu virtual void DrawFrame(int left, int top, int width, int height) { - // Todo: Define this in subclasses + let framecolor = Color(255, 80, 80, 80); + Screen.DrawLineFrame(framecolor, left, top, width, height, screen.GetHeight() / 240); } override void Drawer () @@ -182,31 +181,30 @@ class LoadSaveMenu : ListMenu bool didSeeSelected = false; // Draw picture area - /* (todo: move to subclass) if (gameaction == ga_loadgame || gameaction == ga_loadgamehidecon || gameaction == ga_savegame) { return; } - */ - DrawFrame (savepicLeft, savepicTop, savepicWidth, savepicHeight); + DrawFrame(savepicLeft, savepicTop, savepicWidth, savepicHeight); if (!manager.DrawSavePic(savepicLeft, savepicTop, savepicWidth, savepicHeight)) { - screen.Clear (savepicLeft, savepicTop, savepicLeft+savepicWidth, savepicTop+savepicHeight, 0, 0); + screen.Dim(0, 0.6, savepicLeft, savepicTop, savepicWidth, savepicHeight); if (manager.SavegameCount() > 0) { + if (Selected >= manager.SavegameCount()) Selected = 0; String text = (Selected == -1 || !manager.GetSavegame(Selected).bOldVersion)? Stringtable.Localize("$MNU_NOPICTURE") : Stringtable.Localize("$MNU_DIFFVERSION"); - int textlen = NewSmallFont.StringWidth(text) * CleanXfac; + int textlen = NewSmallFont.StringWidth(text); - screen.DrawText (NewSmallFont, Font.CR_GOLD, savepicLeft+(savepicWidth-textlen)/2, - savepicTop+(savepicHeight-rowHeight)/2, text, DTA_CleanNoMove, true); + screen.DrawText (NewSmallFont, Font.CR_GOLD, (savepicLeft+(savepicWidth-textlen)/2) / FontScale, + (savepicTop+(savepicHeight-rowHeight)/2) / FontScale, text, DTA_VirtualWidthF, screen.GetWidth() / FontScale, DTA_VirtualHeightF, screen.GetHeight() / FontScale); } } // Draw comment area DrawFrame (commentLeft, commentTop, commentWidth, commentHeight); - screen.Clear (commentLeft, commentTop, commentRight, commentBottom, 0, 0); + screen.Dim(0, 0.6, commentLeft, commentTop, commentWidth, commentHeight); int numlinestoprint = min(commentRows, BrokenSaveComment? BrokenSaveComment.Count() : 0); for(int i = 0; i < numlinestoprint; i++) @@ -218,7 +216,7 @@ class LoadSaveMenu : ListMenu // Draw file area DrawFrame (listboxLeft, listboxTop, listboxWidth, listboxHeight); - screen.Clear (listboxLeft, listboxTop, listboxRight, listboxBottom, 0, 0); + screen.Dim(0, 0.6, listboxLeft, listboxTop, listboxWidth, listboxHeight); if (manager.SavegameCount() == 0) {