mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-02-12 23:25:21 +00:00
- overhaul of savegame path management.
* use a different subfolder for each IWAD's saves. * do not allow load and save CCMDs to escape the save folder. Absolute paths and '..' are being blocked now. * unified savegame path and filename generation in one single function. All ad-hoc file name generation was replaced. * -loadgame will also use the designated savegame folder now.
This commit is contained in:
parent
f0601a49a2
commit
ff37d710e2
8 changed files with 87 additions and 88 deletions
|
@ -46,8 +46,11 @@
|
||||||
#include "findfile.h"
|
#include "findfile.h"
|
||||||
#include "v_draw.h"
|
#include "v_draw.h"
|
||||||
#include "savegamemanager.h"
|
#include "savegamemanager.h"
|
||||||
|
#include "m_argv.h"
|
||||||
|
#include "i_specialpaths.h"
|
||||||
|
|
||||||
|
CVAR(String, save_dir, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
|
||||||
|
FString SavegameFolder;
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
//
|
//
|
||||||
|
@ -538,3 +541,56 @@ DEFINE_FIELD_X(SavegameManager, FSavegameManagerBase, WindowSize);
|
||||||
DEFINE_FIELD_X(SavegameManager, FSavegameManagerBase, quickSaveSlot);
|
DEFINE_FIELD_X(SavegameManager, FSavegameManagerBase, quickSaveSlot);
|
||||||
DEFINE_FIELD_X(SavegameManager, FSavegameManagerBase, SaveCommentString);
|
DEFINE_FIELD_X(SavegameManager, FSavegameManagerBase, SaveCommentString);
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// todo: cache this - it never changes once set up.
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
FString G_GetSavegamesFolder()
|
||||||
|
{
|
||||||
|
FString name;
|
||||||
|
bool usefilter;
|
||||||
|
|
||||||
|
if (const char* const dir = Args->CheckValue("-savedir"))
|
||||||
|
{
|
||||||
|
name = dir;
|
||||||
|
usefilter = false; //-savedir specifies an absolute save directory path.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
name = **save_dir ? save_dir : M_GetSavegamesPath();
|
||||||
|
usefilter = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t len = name.Len();
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
FixPathSeperator(name);
|
||||||
|
if (name[len - 1] != '/')
|
||||||
|
name << '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usefilter && SavegameFolder.IsNotEmpty())
|
||||||
|
name << SavegameFolder << '/';
|
||||||
|
|
||||||
|
name = NicePath(name);
|
||||||
|
CreatePath(name);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
FString G_BuildSaveName(const char* prefix)
|
||||||
|
{
|
||||||
|
FString name = G_GetSavegamesFolder() + prefix;
|
||||||
|
DefaultExtension(name, "." SAVEGAME_EXT); // only add an extension if the prefix doesn't have one already.
|
||||||
|
name = NicePath(name);
|
||||||
|
name.Substitute("\\", "/");
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,3 +59,6 @@ public:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern FString SavegameFolder; // specifies a subdirectory for the current IWAD.
|
||||||
|
FString G_GetSavegamesFolder();
|
||||||
|
FString G_BuildSaveName(const char* prefix);
|
||||||
|
|
|
@ -70,6 +70,7 @@
|
||||||
#include "texturemanager.h"
|
#include "texturemanager.h"
|
||||||
#include "v_draw.h"
|
#include "v_draw.h"
|
||||||
#include "d_main.h"
|
#include "d_main.h"
|
||||||
|
#include "savegamemanager.h"
|
||||||
|
|
||||||
extern FILE *Logfile;
|
extern FILE *Logfile;
|
||||||
extern bool insave;
|
extern bool insave;
|
||||||
|
@ -639,6 +640,11 @@ UNSAFE_CCMD (load)
|
||||||
Printf("saving to an absolute path is not allowed\n");
|
Printf("saving to an absolute path is not allowed\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (fname.IndexOf("..") > 0)
|
||||||
|
{
|
||||||
|
Printf("'..' not allowed in file names\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// block all invalid characters for Windows file names
|
// block all invalid characters for Windows file names
|
||||||
if (fname.IndexOfAny(":?*<>|") >= 0)
|
if (fname.IndexOfAny(":?*<>|") >= 0)
|
||||||
|
@ -647,8 +653,7 @@ UNSAFE_CCMD (load)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
fname = G_BuildSaveName(fname, -1);
|
fname = G_BuildSaveName(fname);
|
||||||
DefaultExtension(fname, "." SAVEGAME_EXT);
|
|
||||||
G_LoadGame (fname);
|
G_LoadGame (fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,6 +679,11 @@ UNSAFE_CCMD(save)
|
||||||
Printf("saving to an absolute path is not allowed\n");
|
Printf("saving to an absolute path is not allowed\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (fname.IndexOf("..") > 0)
|
||||||
|
{
|
||||||
|
Printf("'..' not allowed in file names\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// block all invalid characters for Windows file names
|
// block all invalid characters for Windows file names
|
||||||
if (fname.IndexOfAny(":?*<>|") >= 0)
|
if (fname.IndexOfAny(":?*<>|") >= 0)
|
||||||
|
@ -682,8 +692,7 @@ UNSAFE_CCMD(save)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
fname = G_BuildSaveName(fname, -1);
|
fname = G_BuildSaveName(fname);
|
||||||
DefaultExtension(fname, "." SAVEGAME_EXT);
|
|
||||||
G_SaveGame (fname, argv.argc() > 2 ? argv[2] : argv[1]);
|
G_SaveGame (fname, argv.argc() > 2 ? argv[2] : argv[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3021,6 +3021,7 @@ static FILE* D_GetHashFile()
|
||||||
|
|
||||||
static int D_InitGame(const FIWADInfo* iwad_info, TArray<FString>& allwads, TArray<FString>& pwads)
|
static int D_InitGame(const FIWADInfo* iwad_info, TArray<FString>& allwads, TArray<FString>& pwads)
|
||||||
{
|
{
|
||||||
|
SavegameFolder = iwad_info->Autoname;
|
||||||
gameinfo.gametype = iwad_info->gametype;
|
gameinfo.gametype = iwad_info->gametype;
|
||||||
gameinfo.flags = iwad_info->flags;
|
gameinfo.flags = iwad_info->flags;
|
||||||
gameinfo.nokeyboardcheats = iwad_info->nokeyboardcheats;
|
gameinfo.nokeyboardcheats = iwad_info->nokeyboardcheats;
|
||||||
|
@ -3428,10 +3429,8 @@ static int D_InitGame(const FIWADInfo* iwad_info, TArray<FString>& allwads, TArr
|
||||||
v = Args->CheckValue ("-loadgame");
|
v = Args->CheckValue ("-loadgame");
|
||||||
if (v)
|
if (v)
|
||||||
{
|
{
|
||||||
FString file(v);
|
FString file = G_BuildSaveName(v);
|
||||||
FixPathSeperator (file);
|
G_LoadGame(file);
|
||||||
DefaultExtension (file, "." SAVEGAME_EXT);
|
|
||||||
G_LoadGame (file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
v = Args->CheckValue("-playdemo");
|
v = Args->CheckValue("-playdemo");
|
||||||
|
@ -3612,9 +3611,7 @@ static int D_DoomMain_Internal (void)
|
||||||
v = Args->CheckValue("-loadgame");
|
v = Args->CheckValue("-loadgame");
|
||||||
if (v)
|
if (v)
|
||||||
{
|
{
|
||||||
FString file(v);
|
FString file = G_BuildSaveName(v);
|
||||||
FixPathSeperator(file);
|
|
||||||
DefaultExtension(file, "." SAVEGAME_EXT);
|
|
||||||
if (!FileExists(file))
|
if (!FileExists(file))
|
||||||
{
|
{
|
||||||
I_FatalError("Cannot find savegame %s", file.GetChars());
|
I_FatalError("Cannot find savegame %s", file.GetChars());
|
||||||
|
|
|
@ -71,6 +71,7 @@
|
||||||
#include "screenjob.h"
|
#include "screenjob.h"
|
||||||
#include "d_main.h"
|
#include "d_main.h"
|
||||||
#include "i_interface.h"
|
#include "i_interface.h"
|
||||||
|
#include "savegamemanager.h"
|
||||||
|
|
||||||
EXTERN_CVAR (Int, disableautosave)
|
EXTERN_CVAR (Int, disableautosave)
|
||||||
EXTERN_CVAR (Int, autosavecount)
|
EXTERN_CVAR (Int, autosavecount)
|
||||||
|
@ -2457,21 +2458,8 @@ void Net_DoCommand (int type, uint8_t **stream, int player)
|
||||||
{
|
{
|
||||||
// Paths sent over the network will be valid for the system that sent
|
// Paths sent over the network will be valid for the system that sent
|
||||||
// the save command. For other systems, the path needs to be changed.
|
// the save command. For other systems, the path needs to be changed.
|
||||||
const char *fileonly = savegamefile.GetChars();
|
FString basename = ExtractFileBase(savegamefile, true);
|
||||||
const char *slash = strrchr (fileonly, '\\');
|
savegamefile = G_BuildSaveName (basename);
|
||||||
if (slash != NULL)
|
|
||||||
{
|
|
||||||
fileonly = slash + 1;
|
|
||||||
}
|
|
||||||
slash = strrchr (fileonly, '/');
|
|
||||||
if (slash != NULL)
|
|
||||||
{
|
|
||||||
fileonly = slash + 1;
|
|
||||||
}
|
|
||||||
if (fileonly != savegamefile.GetChars())
|
|
||||||
{
|
|
||||||
savegamefile = G_BuildSaveName (fileonly, -1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gameaction = ga_savegame;
|
gameaction = ga_savegame;
|
||||||
|
|
|
@ -119,7 +119,6 @@ CVAR (Int, deathmatch, 0, CVAR_SERVERINFO|CVAR_LATCH);
|
||||||
CVAR (Bool, chasedemo, false, 0);
|
CVAR (Bool, chasedemo, false, 0);
|
||||||
CVAR (Bool, storesavepic, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
CVAR (Bool, storesavepic, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||||
CVAR (Bool, longsavemessages, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
CVAR (Bool, longsavemessages, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||||
CVAR (String, save_dir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
|
||||||
CVAR (Bool, cl_waitforsave, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
|
CVAR (Bool, cl_waitforsave, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
|
||||||
CVAR (Bool, enablescriptscreenshot, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
|
CVAR (Bool, enablescriptscreenshot, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
|
||||||
EXTERN_CVAR (Float, con_midtime);
|
EXTERN_CVAR (Float, con_midtime);
|
||||||
|
@ -2170,61 +2169,10 @@ void G_SaveGame (const char *filename, const char *description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FString G_BuildSaveName (const char *prefix, int slot)
|
|
||||||
{
|
|
||||||
FString name;
|
|
||||||
FString leader;
|
|
||||||
const char *slash = "";
|
|
||||||
|
|
||||||
leader = Args->CheckValue ("-savedir");
|
|
||||||
if (leader.IsEmpty())
|
|
||||||
{
|
|
||||||
leader = save_dir;
|
|
||||||
if (leader.IsEmpty())
|
|
||||||
{
|
|
||||||
leader = M_GetSavegamesPath();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
size_t len = leader.Len();
|
|
||||||
if (leader[0] != '\0' && leader[len-1] != '\\' && leader[len-1] != '/')
|
|
||||||
{
|
|
||||||
slash = "/";
|
|
||||||
}
|
|
||||||
name << leader << slash;
|
|
||||||
name = NicePath(name);
|
|
||||||
CreatePath(name);
|
|
||||||
name << prefix;
|
|
||||||
if (slot >= 0)
|
|
||||||
{
|
|
||||||
name.AppendFormat("%d." SAVEGAME_EXT, slot);
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
CCMD(opensaves)
|
CCMD(opensaves)
|
||||||
{
|
{
|
||||||
FString name;
|
FString name = G_GetSavegamesFolder();
|
||||||
FString leader;
|
|
||||||
const char *slash = "";
|
|
||||||
|
|
||||||
leader = Args->CheckValue ("-savedir");
|
|
||||||
if (leader.IsEmpty())
|
|
||||||
{
|
|
||||||
leader = save_dir;
|
|
||||||
if (leader.IsEmpty())
|
|
||||||
{
|
|
||||||
leader = M_GetSavegamesPath();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
size_t len = leader.Len();
|
|
||||||
if (leader[0] != '\0' && leader[len-1] != '\\' && leader[len-1] != '/')
|
|
||||||
{
|
|
||||||
slash = "/";
|
|
||||||
}
|
|
||||||
name << leader << slash;
|
|
||||||
name = NicePath(name);
|
|
||||||
CreatePath(name);
|
CreatePath(name);
|
||||||
|
|
||||||
I_OpenShellFolder(name);
|
I_OpenShellFolder(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2263,7 +2211,7 @@ void G_DoAutoSave ()
|
||||||
num.Int = nextautosave;
|
num.Int = nextautosave;
|
||||||
autosavenum->ForceSet (num, CVAR_Int);
|
autosavenum->ForceSet (num, CVAR_Int);
|
||||||
|
|
||||||
file = G_BuildSaveName ("auto", nextautosave);
|
file = G_BuildSaveName(FStringf("auto%02d", nextautosave));
|
||||||
|
|
||||||
// The hint flag is only relevant on the primary level.
|
// The hint flag is only relevant on the primary level.
|
||||||
if (!(primaryLevel->flags2 & LEVEL2_NOAUTOSAVEHINT))
|
if (!(primaryLevel->flags2 & LEVEL2_NOAUTOSAVEHINT))
|
||||||
|
@ -2302,7 +2250,7 @@ void G_DoQuickSave ()
|
||||||
num.Int = lastquicksave;
|
num.Int = lastquicksave;
|
||||||
quicksavenum->ForceSet (num, CVAR_Int);
|
quicksavenum->ForceSet (num, CVAR_Int);
|
||||||
|
|
||||||
file = G_BuildSaveName ("quick", lastquicksave);
|
file = G_BuildSaveName(FStringf("quick%02d", nextautosave));
|
||||||
|
|
||||||
readableTime = myasctime ();
|
readableTime = myasctime ();
|
||||||
description.Format("Quicksave %s", readableTime);
|
description.Format("Quicksave %s", readableTime);
|
||||||
|
@ -2377,7 +2325,7 @@ void G_DoSaveGame (bool okForQuicksave, bool forceQuicksave, FString filename, c
|
||||||
|
|
||||||
if (demoplayback)
|
if (demoplayback)
|
||||||
{
|
{
|
||||||
filename = G_BuildSaveName ("demosave." SAVEGAME_EXT, -1);
|
filename = G_BuildSaveName ("demosave");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cl_waitforsave)
|
if (cl_waitforsave)
|
||||||
|
|
|
@ -81,8 +81,6 @@ bool G_Responder (event_t* ev);
|
||||||
void G_ScreenShot (const char* filename);
|
void G_ScreenShot (const char* filename);
|
||||||
void G_StartSlideshow(FLevelLocals *Level, FName whichone);
|
void G_StartSlideshow(FLevelLocals *Level, FName whichone);
|
||||||
|
|
||||||
FString G_BuildSaveName (const char *prefix, int slot);
|
|
||||||
|
|
||||||
class FSerializer;
|
class FSerializer;
|
||||||
bool G_CheckSaveGameWads (FSerializer &arc, bool printwarn);
|
bool G_CheckSaveGameWads (FSerializer &arc, bool printwarn);
|
||||||
|
|
||||||
|
|
|
@ -68,14 +68,14 @@ 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));
|
std::unique_ptr<FResourceFile> savegame(FResourceFile::OpenResourceFile(filepath, true, true));
|
||||||
if (savegame != nullptr)
|
if (savegame != nullptr)
|
||||||
|
@ -252,7 +252,7 @@ FString FSavegameManager::ExtractSaveComment(FSerializer &arc)
|
||||||
|
|
||||||
FString FSavegameManager::BuildSaveName(const char* prefix, int slot)
|
FString FSavegameManager::BuildSaveName(const char* prefix, int slot)
|
||||||
{
|
{
|
||||||
return G_BuildSaveName(prefix, slot);
|
return G_BuildSaveName(FStringf("%s%02d", prefix, slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
|
|
Loading…
Reference in a new issue