- get load and save game menus and related CCMDs working again.

This commit is contained in:
Christoph Oelckers 2020-10-07 18:32:57 +02:00
parent 53c3a6fc9b
commit 2bb38f7d67
23 changed files with 806 additions and 750 deletions

View file

@ -932,6 +932,7 @@ set (PCH_SOURCES
common/menu/optionmenu.cpp common/menu/optionmenu.cpp
common/menu/resolutionmenu.cpp common/menu/resolutionmenu.cpp
common/menu/menudef.cpp common/menu/menudef.cpp
common/menu/savegamemanager.cpp
common/rendering/v_framebuffer.cpp common/rendering/v_framebuffer.cpp
common/rendering/v_video.cpp common/rendering/v_video.cpp

View file

@ -80,8 +80,8 @@ struct GameInterface : ::GameInterface
void MenuClosed() override; void MenuClosed() override;
bool CanSave() override; bool CanSave() override;
bool StartGame(FNewGameStartup& gs) override; bool StartGame(FNewGameStartup& gs) override;
bool SaveGame(FSaveGameNode*) override; bool SaveGame() override;
bool LoadGame(FSaveGameNode*) override; bool LoadGame() override;
void QuitToTitle() override; void QuitToTitle() override;
FString GetCoordString() override; FString GetCoordString() override;
ReservedSpace GetReservedScreenSpace(int viewsize) override; ReservedSpace GetReservedScreenSpace(int viewsize) override;

View file

@ -474,7 +474,7 @@ void LoadSave::Write(void *pData, int nSize)
ThrowError("File error #%d writing save file.", errno); ThrowError("File error #%d writing save file.", errno);
} }
bool GameInterface::LoadGame(FSaveGameNode* node) bool GameInterface::LoadGame()
{ {
sndKillAllSounds(); sndKillAllSounds();
sfxKillAllSounds(); sfxKillAllSounds();
@ -534,7 +534,7 @@ bool GameInterface::LoadGame(FSaveGameNode* node)
return true; return true;
} }
bool GameInterface::SaveGame(FSaveGameNode* node) bool GameInterface::SaveGame()
{ {
LoadSave::hSFile = WriteSavegameChunk("snapshot.bld"); LoadSave::hSFile = WriteSavegameChunk("snapshot.bld");

View file

@ -41,8 +41,6 @@ public:
virtual void Load(void); virtual void Load(void);
void Read(void *, int); void Read(void *, int);
void Write(void *, int); void Write(void *, int);
static void LoadGame(FSavegameNode *);
static void SaveGame(FSavegameNode*);
}; };
void LoadSaveSetup(void); void LoadSaveSetup(void);

View file

@ -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); 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) 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; if (designheight < 240 && realheight >= 480) designheight = 240;

View file

@ -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; i<SaveGames.Size(); i++)
{
if (!SaveGames[i]->bNoDelete)
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; i<SaveGames.Size(); i++)
{
FSaveGameNode *node = SaveGames[i];
#ifdef __unix__
if (node->Filename.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<uint8_t> &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);

View file

@ -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<FSaveGameNode*> 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();
};

View file

@ -22,10 +22,8 @@ extern cycle_t drawtime, actortime, thinktime, gameupdatetime;
extern bool r_NoInterpolate; extern bool r_NoInterpolate;
struct MapRecord; struct MapRecord;
struct FSaveGameNode;
extern MapRecord* g_nextmap; extern MapRecord* g_nextmap;
extern int g_nextskill; 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. 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.

View file

@ -40,7 +40,8 @@ enum gameaction_t : int
ga_savegame, // save the game ga_savegame, // save the game
ga_autosave, // autosave the game (for triggering a save from within the game.) ga_autosave, // autosave the game (for triggering a save from within the game.)
ga_completed, // Level was exited. 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 gamestate_t gamestate;
extern gameaction_t gameaction; extern gameaction_t gameaction;

View file

@ -33,21 +33,6 @@ struct FSavegameInfo
int currentsavever; 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 struct ReservedSpace
{ {
int top; int top;
@ -83,8 +68,8 @@ struct GameInterface
virtual bool DrawSpecialScreen(const DVector2 &origin, int tilenum) { return false; } 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 void DrawCenteredTextScreen(const DVector2& origin, const char* text, int position, bool withbg = true);
virtual double SmallFontScale() { return 1; } virtual double SmallFontScale() { return 1; }
virtual bool SaveGame(FSaveGameNode*) { return true; } virtual bool SaveGame() { return true; }
virtual bool LoadGame(FSaveGameNode*) { return true; } virtual bool LoadGame() { return true; }
virtual void SerializeGameState(FSerializer& arc) {} virtual void SerializeGameState(FSerializer& arc) {}
virtual void DrawPlayerSprite(const DVector2& origin, bool onteam) {} virtual void DrawPlayerSprite(const DVector2& origin, bool onteam) {}
virtual void QuitToTitle() {} virtual void QuitToTitle() {}

View file

@ -4,7 +4,7 @@
** **
**--------------------------------------------------------------------------- **---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit ** Copyright 2001-2010 Randy Heit
** Copyright 2010-2017 Christoph Oelckers ** Copyright 2010-2020 Christoph Oelckers
** All rights reserved. ** All rights reserved.
** **
** Redistribution and use in source and binary forms, with or without ** Redistribution and use in source and binary forms, with or without
@ -45,103 +45,7 @@
#include "v_video.h" #include "v_video.h"
#include "findfile.h" #include "findfile.h"
#include "v_draw.h" #include "v_draw.h"
#include "savegamehelp.h"
// Save name length limit for old binary formats.
#define OLDSAVESTRINGSIZE 24
//=============================================================================
//
// Save data maintenance
//
//=============================================================================
void FSavegameManager::ClearSaveGames()
{
for (unsigned i = 0; i<SaveGames.Size(); i++)
{
if (!SaveGames[i]->bNoDelete)
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;
}
}
//============================================================================= //=============================================================================
// //
@ -153,7 +57,6 @@ int FSavegameManager::InsertSaveNode(FSaveGameNode *node)
void FSavegameManager::ReadSaveStrings() void FSavegameManager::ReadSaveStrings()
{ {
#if 0
if (SaveGames.Size() == 0) if (SaveGames.Size() == 0)
{ {
void *filefirst; void *filefirst;
@ -162,235 +65,46 @@ void FSavegameManager::ReadSaveStrings()
LastSaved = LastAccessed = -1; LastSaved = LastAccessed = -1;
quickSaveSlot = nullptr; quickSaveSlot = nullptr;
filter = G_BuildSaveName("*." SAVEGAME_EXT, -1); filter = G_BuildSaveName("*");
filefirst = I_FindFirst(filter.GetChars(), &c_file); filefirst = I_FindFirst(filter.GetChars(), &c_file);
if (filefirst != ((void *)(-1))) if (filefirst != ((void *)(-1)))
{ {
do do
{ {
// I_FindName only returns the file's name and not its full path // 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<FResourceFile> savegame(FResourceFile::OpenResourceFile(filepath, true, true)); FResourceFile *savegame = FResourceFile::OpenResourceFile(filepath, true, true);
if (savegame != nullptr) if (savegame != nullptr)
{ {
bool oldVer = false;
bool missing = false;
FResourceLump *info = savegame->FindLump("info.json"); FResourceLump *info = savegame->FindLump("info.json");
if (info == nullptr) if (info == nullptr)
{ {
// savegame info not found. This is not a savegame so leave it alone. // savegame info not found. This is not a savegame so leave it alone.
delete savegame;
continue; continue;
} }
void *data = info->Lock(); auto fr = info->NewReader();
FSerializer arc; FString title;
if (arc.OpenReader((const char *)data, info->LumpSize)) 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; FSaveGameNode *node = new FSaveGameNode;
node->Filename = filepath; node->Filename = filepath;
node->bOldVersion = oldVer; node->bOldVersion = check == -1;
node->bMissingWads = missing; node->bMissingWads = check == -2;
node->SaveTitle = title; node->SaveTitle = title;
InsertSaveNode(node); InsertSaveNode(node);
} }
} }
else // check for old formats. } while (I_FindNext (filefirst, &c_file) == 0);
{ I_FindClose (filefirst);
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);
} }
} }
#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; i<SaveGames.Size(); i++)
{
FSaveGameNode *node = SaveGames[i];
#ifdef __unix__
if (node->Filename.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 G_LoadGame(f);
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();
} }
DEFINE_ACTION_FUNCTION(FSavegameManager, DoSave) void FSavegameManager::PerformSaveGame(const char *f, const char *s)
{ {
PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); G_SaveGame(f, s, false, false);
PARAM_INT(sel); }
PARAM_STRING(name);
self->DoSave(sel, name); FString FSavegameManager::BuildSaveName(const char* fn, int slot)
return 0; {
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; FString comment, fcomment, ncomment, mtime;
FSaveGameNode *node;
if (index == -1) arc("Creation Time", comment)
{ ("Map Label", fcomment)
if (SaveGames.Size() > 0 && SaveGames[0]->bNoDelete) ("Map Name", ncomment)
{ ("Map Time", mtime);
index = LastSaved + 1;
}
else
{
index = LastAccessed < 0? 0 : LastAccessed;
}
}
UnloadSaveData(); comment.AppendFormat("\n%s - %s\n%s", fcomment.GetChars(), ncomment.GetChars(), mtime.GetChars());
return comment;
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<uint8_t> &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;
} }
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; FSavegameManager savegameManager;
DEFINE_ACTION_FUNCTION(FSavegameManager, GetManager) DEFINE_ACTION_FUNCTION(FSavegameManager, GetManager)
@ -702,15 +154,3 @@ DEFINE_ACTION_FUNCTION(FSavegameManager, GetManager)
ACTION_RETURN_POINTER(&savegameManager); 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);

View file

@ -61,6 +61,8 @@
#include "razemenu.h" #include "razemenu.h"
#include "mapinfo.h" #include "mapinfo.h"
#include "statistics.h" #include "statistics.h"
#include "i_net.h"
#include "savegamehelp.h"
EXTERN_CVAR(Int, cl_gfxlocalization) EXTERN_CVAR(Int, cl_gfxlocalization)
EXTERN_CVAR(Bool, m_quickexit) EXTERN_CVAR(Bool, m_quickexit)
@ -131,12 +133,12 @@ bool M_SetSpecialMenu(FName& menu, int param)
case NAME_Quitmenu: case NAME_Quitmenu:
// This is no separate class // This is no separate class
C_DoCommand("menu_quit"); C_DoCommand("menu_quit");
return true; return false;
case NAME_EndGameMenu: case NAME_EndGameMenu:
// This is no separate class // This is no separate class
C_DoCommand("menu_endgame"); C_DoCommand("menu_endgame");
return true; return false;
} }
// End of special checks // End of special checks
@ -251,54 +253,43 @@ CCMD(menu_endgame)
// //
//============================================================================= //=============================================================================
CCMD (quicksave)
//=============================================================================
//
//
//
//=============================================================================
CCMD(quicksave)
{ // F6 { // F6
#if 0 if (!gi->CanSave()) return;
if (!usergame || (players[consoleplayer].health <= 0 && !multiplayer))
{
S_Sound (CHAN_VOICE, CHANF_UI, "menu/invalid", snd_menuvolume, ATTN_NONE);
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) if (savegameManager.quickSaveSlot == NULL || savegameManager.quickSaveSlot == (FSaveGameNode*)1)
{ {
S_Sound(CHAN_VOICE, CHANF_UI, "menu/activate", snd_menuvolume, ATTN_NONE); M_StartControlPanel(true);
M_StartControlPanel(false);
M_SetMenu(NAME_Savegamemenu); M_SetMenu(NAME_Savegamemenu);
return; return;
} }
auto slot = savegameManager.quickSaveSlot;
// [mxd]. Just save the game, no questions asked. // [mxd]. Just save the game, no questions asked.
if (!saveloadconfirmation) if (!saveloadconfirmation)
{ {
G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->SaveTitle.GetChars()); G_SaveGame(savegameManager.quickSaveSlot->Filename, savegameManager.quickSaveSlot->SaveTitle, true, true);
return; return;
} }
S_Sound(CHAN_VOICE, CHANF_UI, "menu/activate", snd_menuvolume, ATTN_NONE);
FString tempstring = GStrings("QSPROMPT"); 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, []() DMenu* newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []()
{ {
G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->SaveTitle.GetChars()); G_SaveGame(savegameManager.quickSaveSlot->Filename, savegameManager.quickSaveSlot->SaveTitle, true, true);
S_Sound(CHAN_VOICE, CHANF_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); });
M_ClearMenus();
});
M_ActivateMenu(newmenu); M_ActivateMenu(newmenu);
#endif
} }
//============================================================================= //=============================================================================
@ -307,21 +298,20 @@ CCMD (quicksave)
// //
//============================================================================= //=============================================================================
CCMD (quickload) CCMD(quickload)
{ // F9 { // F9
#if 0
if (netgame) if (netgame)
{ {
M_StartControlPanel(true); M_StartControlPanel(true);
M_StartMessage (GStrings("QLOADNET"), 1); M_StartMessage(GStrings("QLOADNET"), 1);
return; return;
} }
if (savegameManager.quickSaveSlot == NULL || savegameManager.quickSaveSlot == (FSaveGameNode*)1) if (savegameManager.quickSaveSlot == nullptr || savegameManager.quickSaveSlot == (FSaveGameNode*)1)
{ {
M_StartControlPanel(true); M_StartControlPanel(true);
// signal that whatever gets loaded should be the new quicksave // signal that whatever gets loaded should be the new quicksave
savegameManager.quickSaveSlot = (FSaveGameNode *)1; savegameManager.quickSaveSlot = (FSaveGameNode*)1;
M_SetMenu(NAME_Loadgamemenu); M_SetMenu(NAME_Loadgamemenu);
return; return;
} }
@ -329,7 +319,7 @@ CCMD (quickload)
// [mxd]. Just load the game, no questions asked. // [mxd]. Just load the game, no questions asked.
if (!saveloadconfirmation) if (!saveloadconfirmation)
{ {
G_LoadGame(savegameManager.quickSaveSlot->Filename.GetChars()); G_LoadGame(savegameManager.quickSaveSlot->Filename);
return; return;
} }
FString tempstring = GStrings("QLPROMPT"); FString tempstring = GStrings("QLPROMPT");
@ -337,14 +327,11 @@ CCMD (quickload)
M_StartControlPanel(true); M_StartControlPanel(true);
DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []() DMenu* newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []()
{ {
G_LoadGame(savegameManager.quickSaveSlot->Filename.GetChars()); G_LoadGame(savegameManager.quickSaveSlot->Filename);
S_Sound(CHAN_VOICE, CHANF_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE);
M_ClearMenus();
}); });
M_ActivateMenu(newmenu); M_ActivateMenu(newmenu);
#endif
} }
//============================================================================= //=============================================================================

View file

@ -2,6 +2,7 @@
#include "menu.h" #include "menu.h"
#include "gamestruct.h" #include "gamestruct.h"
#include "c_cvars.h" #include "c_cvars.h"
#include "savegamemanager.h"
extern bool help_disabled; extern bool help_disabled;
@ -14,45 +15,13 @@ void M_StartupSkillMenu(FNewGameStartup *gs);
void SetDefaultMenuColors(); void SetDefaultMenuColors();
void BuildGameMenus(); void BuildGameMenus();
// The savegame manager contains too much code that is game specific. Parts are shareable but need more work first. class FSavegameManager : public FSavegameManagerBase
struct FSavegameManager
{ {
private: void PerformSaveGame(const char *fn, const char *sgdesc) override;
TArray<FSaveGameNode*> SaveGames; void PerformLoadGame(const char *fn, bool) override;
FSaveGameNode NewSaveNode; FString ExtractSaveComment(FSerializer &arc) override;
int LastSaved = -1; FString BuildSaveName(const char* prefix, int slot) override;
int LastAccessed = -1; void ReadSaveStrings() override;
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();
}; };
extern FSavegameManager savegameManager; extern FSavegameManager savegameManager;

View file

@ -61,6 +61,7 @@ static FResourceFile *savereader;
void LoadEngineState(); void LoadEngineState();
void SaveEngineState(); void SaveEngineState();
void WriteSavePic(FileWriter* file, int width, int height); void WriteSavePic(FileWriter* file, int width, int height);
extern FString BackupSaveGame;
CVAR(String, cl_savedir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(String, cl_savedir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
@ -592,6 +593,48 @@ static int nextquicksave = -1;
self = 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() void M_Autosave()
{ {
if (disableautosave) return; if (disableautosave) return;
@ -611,12 +654,11 @@ void M_Autosave()
num.Int = nextautosave; num.Int = nextautosave;
autosavenum.ForceSet(num, CVAR_Int); autosavenum.ForceSet(num, CVAR_Int);
FSaveGameNode sg; auto Filename = G_BuildSaveName(FStringf("auto%04d", nextautosave));
sg.Filename = G_BuildSaveName(FStringf("auto%04d", nextautosave));
readableTime = myasctime(); readableTime = myasctime();
sg.SaveTitle.Format("Autosave %s", readableTime); FStringf SaveTitle("Autosave %s", readableTime);
nextautosave = (nextautosave + 1) % count; nextautosave = (nextautosave + 1) % count;
//savegameManager.SaveGame(&sg, false, false); G_SaveGame(Filename, SaveTitle, false, false);
} }
CCMD(autosave) CCMD(autosave)
@ -643,11 +685,11 @@ CCMD(rotatingquicksave)
quicksavenum.ForceSet(num, CVAR_Int); quicksavenum.ForceSet(num, CVAR_Int);
FSaveGameNode sg; FSaveGameNode sg;
sg.Filename = G_BuildSaveName(FStringf("quick%04d", nextquicksave)); auto Filename = G_BuildSaveName(FStringf("quick%04d", nextquicksave));
readableTime = myasctime(); readableTime = myasctime();
sg.SaveTitle.Format("Quicksave %s", readableTime); FStringf SaveTitle("Quicksave %s", readableTime);
nextquicksave = (nextquicksave + 1) % count; nextquicksave = (nextquicksave + 1) % count;
//savegameManager.SaveGame(&sg, false, false); G_SaveGame(Filename, SaveTitle, false, false);
} }

View file

@ -17,6 +17,9 @@ class FileReader;
FString G_BuildSaveName (const char *prefix); FString G_BuildSaveName (const char *prefix);
int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu); 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 SaveEngineState();
void LoadEngineState(); void LoadEngineState();
void M_Autosave(); void M_Autosave();

View file

@ -243,8 +243,8 @@ struct GameInterface : ::GameInterface
void MenuClosed() override; void MenuClosed() override;
bool StartGame(FNewGameStartup& gs) override; bool StartGame(FNewGameStartup& gs) override;
FSavegameInfo GetSaveSig() override; FSavegameInfo GetSaveSig() override;
bool LoadGame(FSaveGameNode* sv) override; bool LoadGame() override;
bool SaveGame(FSaveGameNode* sv) override; bool SaveGame() override;
bool CanSave() override; bool CanSave() override;
ReservedSpace GetReservedScreenSpace(int viewsize) override { return { 0, 24 }; } ReservedSpace GetReservedScreenSpace(int viewsize) override { return { 0, 24 }; }
void QuitToTitle() override; void QuitToTitle() override;

View file

@ -34,14 +34,14 @@ void LoadTextureState();
static TArray<SavegameHelper*> sghelpers(TArray<SavegameHelper*>::NoInit); static TArray<SavegameHelper*> sghelpers(TArray<SavegameHelper*>::NoInit);
bool GameInterface::SaveGame(FSaveGameNode* sv) bool GameInterface::SaveGame()
{ {
for (auto sgh : sghelpers) sgh->Save(); for (auto sgh : sghelpers) sgh->Save();
SaveTextureState(); SaveTextureState();
return 1; // CHECKME return 1; // CHECKME
} }
bool GameInterface::LoadGame(FSaveGameNode* sv) bool GameInterface::LoadGame()
{ {
for (auto sgh : sghelpers) sgh->Load(); for (auto sgh : sghelpers) sgh->Load();

View file

@ -2193,8 +2193,8 @@ struct GameInterface : ::GameInterface
bool CanSave() override; bool CanSave() override;
bool StartGame(FNewGameStartup& gs) override; bool StartGame(FNewGameStartup& gs) override;
FSavegameInfo GetSaveSig() override; FSavegameInfo GetSaveSig() override;
bool LoadGame(FSaveGameNode* sv) override; bool LoadGame() override;
bool SaveGame(FSaveGameNode* sv) override; bool SaveGame() override;
void SetAmbience(bool on) override { if (on) StartAmbientSound(); else StopAmbientSound(); } void SetAmbience(bool on) override { if (on) StartAmbientSound(); else StopAmbientSound(); }
FString GetCoordString() override; FString GetCoordString() override;
ReservedSpace GetReservedScreenSpace(int viewsize) override; ReservedSpace GetReservedScreenSpace(int viewsize) override;

View file

@ -204,7 +204,7 @@ int LoadSymCodeInfo(MFILE_READ fil, void **ptr)
bool GameInterface::SaveGame(FSaveGameNode *sv) bool GameInterface::SaveGame()
{ {
MFILE_WRITE fil; MFILE_WRITE fil;
int i,j; int i,j;
@ -643,7 +643,7 @@ bool GameInterface::SaveGame(FSaveGameNode *sv)
} }
bool GameInterface::LoadGame(FSaveGameNode* sv) bool GameInterface::LoadGame()
{ {
MFILE_READ fil; MFILE_READ fil;
int i,j,saveisshot=0; int i,j,saveisshot=0;

View file

@ -377,11 +377,9 @@ ImageScroller "CreditsMenu"
ListMenu "LoadGameMenu" ListMenu "LoadGameMenu"
{ {
/*
Caption "$MNU_LOADGAME"
Position 0, 40 Position 0, 40
Class "LoadMenu" // uses its own implementation Class "LoadMenu" // uses its own implementation
*/ CaptionItem "$MNU_LOADGAME"
} }
//------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------
@ -392,11 +390,9 @@ ListMenu "LoadGameMenu"
ListMenu "SaveGameMenu" ListMenu "SaveGameMenu"
{ {
/*
Caption "$MNU_SAVEGAME"
Position 0, 40 Position 0, 40
Class "SaveMenu" // uses its own implementation Class "SaveMenu" // uses its own implementation
*/ CaptionItem "$MNU_SAVEGAME"
} }
//------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------

View file

@ -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 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 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 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 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 Vector2, Vector2 VirtualToRealCoords(Vector2 pos, Vector2 size, Vector2 vsize, bool vbottom=false, bool handleaspect=true);
native static double GetAspectRatio(); native static double GetAspectRatio();

View file

@ -6,3 +6,25 @@ enum ETranslationTable
TRANSLATION_Remap, 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
};

View file

@ -91,14 +91,11 @@ class LoadSaveMenu : ListMenu
int listboxRows; int listboxRows;
int listboxHeight; int listboxHeight;
int listboxRight; int listboxRight;
int listboxBottom;
int commentLeft; int commentLeft;
int commentTop; int commentTop;
int commentWidth; int commentWidth;
int commentHeight; int commentHeight;
int commentRight;
int commentBottom;
int commentRows; int commentRows;
bool mEntering; bool mEntering;
@ -118,33 +115,34 @@ class LoadSaveMenu : ListMenu
override void Init(Menu parent, ListMenuDescriptor desc) override void Init(Menu parent, ListMenuDescriptor desc)
{ {
Super.Init(parent, desc); Super.Init(parent, desc);
bool aspect43 = true;
int Width43 = screen.GetHeight() * 4 / 3;
int Left43 = (screen.GetWidth() - Width43) / 2;
manager = SavegameManager.GetManager(); manager = SavegameManager.GetManager();
manager.ReadSaveStrings(); manager.ReadSaveStrings();
double wScale = Width43 / 640.;
savepicLeft = 10; savepicLeft = Left43 + int(20 * wScale);
savepicTop = 54*CleanYfac; savepicTop = mDesc.mYpos * screen.GetHeight() / 200 ;
savepicWidth = 216*screen.GetWidth() / 640; savepicWidth = int(240 * wScale);
savepicHeight = 135*screen.GetHeight() / 400; savepicHeight = int(180 * wScale);
FontScale = max(screen.GetHeight() / 480, 1); FontScale = max(screen.GetHeight() / 480, 1);
rowHeight = int(max((NewConsoleFont.GetHeight() + 1) * FontScale, 1)); rowHeight = int(max((NewConsoleFont.GetHeight() + 1) * FontScale, 1));
listboxLeft = savepicLeft + savepicWidth + 14; listboxLeft = savepicLeft + savepicWidth + int(20*wScale);
listboxTop = savepicTop; listboxTop = savepicTop;
listboxWidth = screen.GetWidth() - listboxLeft - 10; listboxWidth = Width43 + Left43 - listboxLeft - int(30 * wScale);
int listboxHeight1 = screen.GetHeight() - listboxTop - 10; int listboxHeight1 = screen.GetHeight() - listboxTop - int(20*wScale);
listboxRows = (listboxHeight1 - 1) / rowHeight; listboxRows = (listboxHeight1 - 1) / rowHeight;
listboxHeight = listboxRows * rowHeight + 1; listboxHeight = listboxRows * rowHeight + 1;
listboxRight = listboxLeft + listboxWidth; listboxRight = listboxLeft + listboxWidth;
listboxBottom = listboxTop + listboxHeight;
commentLeft = savepicLeft; commentLeft = savepicLeft;
commentTop = savepicTop + savepicHeight + 16; commentTop = savepicTop + savepicHeight + int(16 * wScale);
commentWidth = savepicWidth; commentWidth = savepicWidth;
//commentHeight = (51+(screen.GetHeight()>200?10:0))*CleanYfac; commentHeight = listboxHeight - savepicHeight - int(16 * wScale);
commentHeight = listboxHeight - savepicHeight - 16;
commentRight = commentLeft + commentWidth;
commentBottom = commentTop + commentHeight;
commentRows = commentHeight / rowHeight; commentRows = commentHeight / rowHeight;
} }
@ -169,7 +167,8 @@ class LoadSaveMenu : ListMenu
virtual void DrawFrame(int left, int top, int width, int height) 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 () override void Drawer ()
@ -182,31 +181,30 @@ class LoadSaveMenu : ListMenu
bool didSeeSelected = false; bool didSeeSelected = false;
// Draw picture area // Draw picture area
/* (todo: move to subclass)
if (gameaction == ga_loadgame || gameaction == ga_loadgamehidecon || gameaction == ga_savegame) if (gameaction == ga_loadgame || gameaction == ga_loadgamehidecon || gameaction == ga_savegame)
{ {
return; return;
} }
*/
DrawFrame (savepicLeft, savepicTop, savepicWidth, savepicHeight); DrawFrame(savepicLeft, savepicTop, savepicWidth, savepicHeight);
if (!manager.DrawSavePic(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 (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"); 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, screen.DrawText (NewSmallFont, Font.CR_GOLD, (savepicLeft+(savepicWidth-textlen)/2) / FontScale,
savepicTop+(savepicHeight-rowHeight)/2, text, DTA_CleanNoMove, true); (savepicTop+(savepicHeight-rowHeight)/2) / FontScale, text, DTA_VirtualWidthF, screen.GetWidth() / FontScale, DTA_VirtualHeightF, screen.GetHeight() / FontScale);
} }
} }
// Draw comment area // Draw comment area
DrawFrame (commentLeft, commentTop, commentWidth, commentHeight); 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); int numlinestoprint = min(commentRows, BrokenSaveComment? BrokenSaveComment.Count() : 0);
for(int i = 0; i < numlinestoprint; i++) for(int i = 0; i < numlinestoprint; i++)
@ -218,7 +216,7 @@ class LoadSaveMenu : ListMenu
// Draw file area // Draw file area
DrawFrame (listboxLeft, listboxTop, listboxWidth, listboxHeight); 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) if (manager.SavegameCount() == 0)
{ {