- various fixes and improvements related to file location management:

* saving of demos and savegames no longer mindlessly writes to the mod directory. All such access is now being rerouted through the special paths interface so that the game data can reside in write protected locations.
* refactored all occurences of klistpath except fnlist_getnames.
* do not allow CON scripts to write to arbitrary files. This is a massive exploit and can be used to cause real damage if someone knows how to play this thing - it's far easier than people may think! It will now write any such data to a special section in the main config which is safe and cannot be manipulated to write to random locations on the hard drive.
This commit is contained in:
Christoph Oelckers 2019-11-02 00:38:30 +01:00
parent cfca8060ba
commit 1149b4f4aa
24 changed files with 302 additions and 153 deletions

View file

@ -722,6 +722,7 @@ set (PCH_SOURCES
mact/src/keyboard.cpp
mact/src/input.cpp
thirdparty/src/base64.cpp
thirdparty/src/sjson.cpp
thirdparty/src/crc32.cpp
thirdparty/src/fix16.cpp

View file

@ -515,12 +515,6 @@ extern void G_ExtInit(void);
extern void G_SetupGlobalPsky(void);
#define G_ModDirSnprintf(buf, size, basename, ...) \
(((g_modDir[0] != '/') ? Bsnprintf(buf, size, "%s/" basename, g_modDir, ##__VA_ARGS__) : Bsnprintf(buf, size, basename, ##__VA_ARGS__)) \
>= ((int32_t)size) - 1)
#define G_ModDirSnprintfLite(buf, size, basename) \
((g_modDir[0] != '/') ? Bsnprintf(buf, size, "%s/%s", g_modDir, basename) : Bsnprintf(buf, size, "%s", basename))
static inline int gameHandleEvents(void)
{

View file

@ -46,7 +46,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "network.h"
#include "player.h"
#include "screen.h"
#include "i_specialpaths.h"
#include "view.h"
#include "gamecontrol.h"
BEGIN_BLD_NS
@ -131,7 +133,7 @@ bool CDemo::Create(const char *pzFile)
{
for (int i = 0; i < 8 && !vc; i++)
{
G_ModDirSnprintf(buffer, BMAX_PATH, "%s0%02d.dem", BloodIniPre, i);
snprintf(buffer, BMAX_PATH, "%s%s0%02d.dem", M_GetDemoPath().GetChars(), BloodIniPre, i);
if (access(buffer, 0) != -1)
vc = 1;
}
@ -144,7 +146,7 @@ bool CDemo::Create(const char *pzFile)
}
else
{
G_ModDirSnprintfLite(buffer, BMAX_PATH, pzFile);
snprintf(buffer, BMAX_PATH, "%s%s", M_GetDemoPath().GetChars(), pzFile);
hRFile = fopen(buffer, "wb");
if (hRFile == NULL)
return false;
@ -428,12 +430,12 @@ void CDemo::LoadDemoInfo(void)
auto pDemo = &pFirstDemo;
at59ef = 0;
char zFN[BMAX_PATH];
Bsnprintf(zFN, BMAX_PATH, "%s*.dem", BloodIniPre);
auto pList = klistpath("/", zFN, BUILDVFS_FIND_FILE);
auto pIterator = pList;
while (pIterator != NULL)
Bsnprintf(zFN, BMAX_PATH, "%s%s*.dem", M_GetDemoPath().GetChars(), BloodIniPre);
TArray<FString> demos;
D_AddWildFile(demos, zFN);
for (auto &filename : demos)
{
auto hFile = fopenFileReader(pIterator->name, 0);
auto hFile = fopenFileReader(filename, 0);
if (!hFile.isOpen())
ThrowError("Error loading demo file header.");
hFile.Read(&atf, sizeof(atf));
@ -446,13 +448,11 @@ void CDemo::LoadDemoInfo(void)
{
*pDemo = new DEMOCHAIN;
(*pDemo)->pNext = NULL;
Bstrncpy((*pDemo)->zName, pIterator->name, BMAX_PATH);
Bstrncpy((*pDemo)->zName, filename, BMAX_PATH);
at59ef++;
pDemo = &(*pDemo)->pNext;
}
pIterator = pIterator->next;
}
klistfree(pList);
pCurrentDemo = pFirstDemo;
}

View file

@ -44,6 +44,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "seq.h"
#include "sfx.h"
#include "sound.h"
#include "i_specialpaths.h"
#include "view.h"
BEGIN_BLD_NS
@ -426,11 +427,13 @@ void MyLoadSave::Save(void)
void LoadSavedInfo(void)
{
auto pList = klistpath("./", "game*.sav", BUILDVFS_FIND_FILE);
FString path = M_GetSavegamesPath() + "%sgame*.sav";
TArray<FString> saves;
D_AddWildFile(saves, path);
int nCount = 0;
for (auto pIterator = pList; pIterator != NULL && nCount < 10; pIterator = pIterator->next, nCount++)
for (auto & savename : saves)
{
auto hFile = fopenFileReader(pIterator->name, 0);
auto hFile = fopenFileReader(savename, 0);
if (!hFile.isOpen())
ThrowError("Error loading save file header.");
int vc;
@ -453,8 +456,8 @@ void LoadSavedInfo(void)
if ((uint32_t)hFile.Read(&gSaveGameOptions[nCount], sizeof(gSaveGameOptions[0])) != sizeof(gSaveGameOptions[0]))
ThrowError("Error reading save file.");
strcpy(strRestoreGameStrings[gSaveGameOptions[nCount].nSaveGameSlot], gSaveGameOptions[nCount].szUserGameName);
nCount++;
}
klistfree(pList);
}
void UpdateSavedInfo(int nSlot)

View file

@ -41,6 +41,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "screen.h"
#include "sound.h"
#include "view.h"
#include "i_specialpaths.h"
EXTERN_CVAR(Bool, hud_powerupduration)
@ -2102,7 +2103,7 @@ void SaveGame(CGameMenuItemZEditBitmap *pItem, CGameMenuEvent *event)
gGameMenuMgr.Deactivate();
return;
}
G_ModDirSnprintf(strSaveGameName, BMAX_PATH, "game00%02d.sav", nSlot);
snprintf(strSaveGameName, BMAX_PATH, "%sgame00%02d.sav", M_GetSavegamesPath().GetChars(), nSlot);
strcpy(gGameOptions.szUserGameName, strRestoreGameStrings[nSlot]);
sprintf(gGameOptions.szSaveGameName, "%s", strSaveGameName);
gGameOptions.nSaveGameSlot = nSlot;
@ -2124,7 +2125,7 @@ void QuickSaveGame(void)
gGameMenuMgr.Deactivate();
return;
}*/
G_ModDirSnprintf(strSaveGameName, BMAX_PATH, "game00%02d.sav", gQuickSaveSlot);
snprintf(strSaveGameName, BMAX_PATH, "%sgame00%02d.sav", M_GetSavegamesPath().GetChars(), gQuickSaveSlot);
strcpy(gGameOptions.szUserGameName, strRestoreGameStrings[gQuickSaveSlot]);
sprintf(gGameOptions.szSaveGameName, "%s", strSaveGameName);
gGameOptions.nSaveGameSlot = gQuickSaveSlot;
@ -2144,7 +2145,7 @@ void LoadGame(CGameMenuItemZEditBitmap *pItem, CGameMenuEvent *event)
int nSlot = pItem->at28;
if (gGameOptions.nGameType > 0)
return;
G_ModDirSnprintf(strLoadGameName, BMAX_PATH, "game00%02d.sav", nSlot);
snprintf(strLoadGameName, BMAX_PATH, "%sgame00%02d.sav", M_GetSavegamesPath().GetChars(), nSlot);
if (!testkopen(strLoadGameName, 0))
return;
viewLoadingScreen(2518, "Loading", "Loading Saved Game", strRestoreGameStrings[nSlot]);
@ -2159,7 +2160,7 @@ void QuickLoadGame(void)
char strLoadGameName[BMAX_PATH];
if (gGameOptions.nGameType > 0)
return;
G_ModDirSnprintf(strLoadGameName, BMAX_PATH, "game00%02d.sav", gQuickLoadSlot);
snprintf(strLoadGameName, BMAX_PATH, "%sgame00%02d.sav", M_GetSavegamesPath().GetChars(), gQuickLoadSlot);
if (!testkopen(strLoadGameName, 0))
return;
viewLoadingScreen(2518, "Loading", "Loading Saved Game", strRestoreGameStrings[gQuickLoadSlot]);

View file

@ -22,8 +22,6 @@ using buildvfs_kfd = int32_t;
extern int32_t pathsearchmode; // 0 = gamefs mode (default), 1 = localfs mode (editor's mode)
extern char g_modDir[BMAX_PATH];
enum {
CACHE1D_FIND_FILE = 1,

View file

@ -122,9 +122,6 @@ static size_t maxsearchpathlen = 0;
int32_t pathsearchmode = 0;
char g_modDir[BMAX_PATH] = "/";
int32_t klistaddentry(CACHE1D_FIND_REC **rec, const char *name, int32_t type, int32_t source)
{

View file

@ -38,8 +38,7 @@
#include "resourcefile.h"
#include "name.h"
#include "m_swap.h"
extern FString LumpFilter;
#include "gamecontrol.h"
//==========================================================================
//

View file

@ -9,8 +9,10 @@
#include "gamecvars.h"
extern FString currentGame;
extern FString LumpFilter;
class FArgs;
void D_AddWildFile(TArray<FString>& wadfiles, const char* value);
extern uint8_t KeyboardKeys[NUMGAMEFUNCTIONS][2];

View file

@ -462,12 +462,6 @@ CUSTOM_CVAR(String, rtsname, "", CVAR_ARCHIVE | CVAR_USERINFO)
{
RTS_Init(self);
}
#if 0
// These will be redone once the resource management has been swapped out.
//GameConfig->SetValueForKey("Setup", "ModDir", &g_modDir[0]);
#endif
#if 0

View file

@ -11,6 +11,7 @@ FString M_GetConfigPath(bool for_reading);
FString M_GetScreenshotsPath();
FString M_GetSavegamesPath();
FString M_GetDocumentsPath();
FString M_GetDemoPath();
#ifdef __APPLE__

View file

@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "menus.h"
#include "savegame.h"
#include "screens.h"
#include "i_specialpaths.h"
#include "vfs.h"
@ -156,7 +157,7 @@ void G_OpenDemoWrite(void)
if (demonum == MAXDEMOS)
return;
if (G_ModDirSnprintf(demofn, sizeof(demofn), DEMOFN_FMT, demonum))
if (snprintf(demofn, sizeof(demofn), "%s" DEMOFN_FMT, M_GetDemoPath().GetChars(), demonum))
{
initprintf("Couldn't start demo writing: INTERNAL ERROR: file name too long\n");
goto error_wopen_demo;

View file

@ -460,13 +460,6 @@ enum
};
#define G_ModDirSnprintf(buf, size, basename, ...) \
(((g_modDir[0] != '/') ? Bsnprintf(buf, size, "%s/" basename, g_modDir, ##__VA_ARGS__) : Bsnprintf(buf, size, basename, ##__VA_ARGS__)) \
>= ((int32_t)size) - 1)
#define G_ModDirSnprintfLite(buf, size, basename) \
((g_modDir[0] != '/') ? Bsnprintf(buf, size, "%s/%s", g_modDir, basename) : Bsnprintf(buf, size, basename))
static inline void G_NewGame_EnterLevel(void)
{
G_NewGame(ud.m_volume_number, ud.m_level_number, ud.m_player_skill);

View file

@ -34,6 +34,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "gamecvars.h"
#include "gamecontrol.h"
#include "gameconfigfile.h"
#include "files.h"
#include "base64.h"
#include "vfs.h"
@ -5615,13 +5617,21 @@ badindex:
int const quoteFilename = *insptr++;
VM_ASSERT((unsigned)quoteFilename < MAXQUOTES && apStrings[quoteFilename], "invalid quote %d\n", quoteFilename);
auto kFile = kopenFileReader(apStrings[quoteFilename], 0);
if (!kFile.isOpen())
FStringf IniSection("UserStrings.%s", LumpFilter.GetChars());
auto IniKey = apStrings[quoteFilename];
if (!GameConfig->SetSection(IniSection))
{
dispatch();
}
auto base64 = GameConfig->GetValueForKey(IniKey);
if (base64 == nullptr)
{
dispatch();
}
auto decoded = base64_decode(base64);
FileReader fr;
size_t const filelength = kFile.GetLength();
size_t const filelength = decoded.length();
size_t const numElements = Gv_GetArrayCountForAllocSize(arrayNum, filelength);
if (numElements > 0)
@ -5649,7 +5659,7 @@ badindex:
{
void *const pArray = Xcalloc(1, newBytes);
kFile.Read(pArray, readBytes);
memcpy(pArray, decoded.c_str(), readBytes);
if (flags & GAMEARRAY_UNSIGNED)
{
@ -5668,12 +5678,11 @@ badindex:
#endif
default:
memset((char *)pValues + readBytes, 0, newBytes - readBytes);
kFile.Read(pValues, readBytes);
memcpy(pValues, decoded.c_str(), readBytes);
break;
}
}
kFile.Close();
dispatch();
}
@ -5684,22 +5693,12 @@ badindex:
int const quoteFilename = *insptr++;
VM_ASSERT((unsigned)quoteFilename < MAXQUOTES && apStrings[quoteFilename], "invalid quote %d\n", quoteFilename);
char temp[BMAX_PATH];
if (EDUKE32_PREDICT_FALSE(G_ModDirSnprintf(temp, sizeof(temp), "%s", apStrings[quoteFilename])))
{
CON_ERRPRINTF("file name too long\n");
abort_after_error();
}
buildvfs_FILE const fil = buildvfs_fopen_write(temp);
if (EDUKE32_PREDICT_FALSE(fil == NULL))
{
CON_ERRPRINTF("couldn't open file \"%s\"\n", temp);
abort_after_error();
}
// No, we are not writing stuff to an arbitrary file on the hard drive! This is a first grade exploit for doing serious damage.
// Instead, encode the data as BASE64 and write it to the config file,
// which doesn't create a wide open door for exploits.
FStringf IniSection("UserStrings.%s", LumpFilter.GetChars());
auto IniKey = apStrings[quoteFilename];
BufferWriter bw;
switch (aGameArrays[arrayNum].flags & GAMEARRAY_SIZE_MASK)
{
@ -5713,15 +5712,19 @@ badindex:
for (unative_t k = 0; k < numElements; ++k)
pArray[k] = Gv_GetArrayValue(arrayNum, k);
buildvfs_fwrite(pArray, 1, numDiskBytes, fil);
bw.Write(pArray, numDiskBytes);
Xfree(pArray);
break;
}
#endif
default: buildvfs_fwrite(aGameArrays[arrayNum].pValues, 1, Gv_GetArrayAllocSize(arrayNum), fil); break;
default: bw.Write(aGameArrays[arrayNum].pValues, Gv_GetArrayAllocSize(arrayNum)); break;
}
auto base64 = base64_encode(bw.GetBuffer()->Data(), bw.GetBuffer()->Size());
if (GameConfig->SetSection(IniSection))
{
GameConfig->SetValueForKey(IniKey, base64.c_str());
}
buildvfs_fclose(fil);
dispatch();
}

View file

@ -27,6 +27,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "enet.h"
#include "sjson.h"
#include "gamecvars.h"
#include "i_specialpaths.h"
BEGIN_DUKE_NS
@ -5631,7 +5632,7 @@ int portableBackupSave(const char * path, const char * name, int volume, int lev
char fn[BMAX_PATH];
if (G_ModDirSnprintf(fn, sizeof(fn), "%s.ext", path))
if (snprintf(fn, sizeof(fn), "%s%s.ext", M_GetSavegamesPath().GetChars(), path))
{
return 1;
}

View file

@ -27,6 +27,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "prlights.h"
#include "savegame.h"
#include "sjson.h"
#include "i_specialpaths.h"
#include "gamecontrol.h"
#include "vfs.h"
@ -149,13 +151,13 @@ uint16_t g_nummenusaves;
static menusave_t * g_internalsaves;
static uint16_t g_numinternalsaves;
static void ReadSaveGameHeaders_CACHE1D(CACHE1D_FIND_REC *f)
static void ReadSaveGameHeaders_CACHE1D(TArray<FString> &saves)
{
savehead_t h;
for (; f != nullptr; f = f->next)
for (auto &save : saves)
{
char const * fn = f->name;
char const * fn = save;
auto fil = fopenFileReader(fn, 0);
if (!fil.isOpen())
continue;
@ -201,22 +203,13 @@ static void ReadSaveGameHeaders_CACHE1D(CACHE1D_FIND_REC *f)
}
}
static int countcache1dfind(CACHE1D_FIND_REC *f)
{
int x = 0;
for (; f != nullptr; f = f->next)
++x;
return x;
}
static void ReadSaveGameHeaders_Internal(void)
{
static char const DefaultPath[] = "/", SavePattern[] = "*.esv";
CACHE1D_FIND_REC *findfiles_default = klistpath(DefaultPath, SavePattern, CACHE1D_FIND_FILE);
FString pattern = M_GetSavegamesPath() + "*.esv";
TArray<FString> saves;
D_AddWildFile(saves, pattern);
// potentially overallocating but programmatically simple
int const numfiles = countcache1dfind(findfiles_default);
int const numfiles = saves.Size();
size_t const internalsavesize = sizeof(menusave_t) * numfiles;
g_internalsaves = (menusave_t *)Xrealloc(g_internalsaves, internalsavesize);
@ -225,8 +218,7 @@ static void ReadSaveGameHeaders_Internal(void)
g_internalsaves[x].clear();
g_numinternalsaves = 0;
ReadSaveGameHeaders_CACHE1D(findfiles_default);
klistfree(findfiles_default);
ReadSaveGameHeaders_CACHE1D(saves);
g_nummenusaves = 0;
for (int x = g_numinternalsaves-1; x >= 0; --x)
@ -707,7 +699,7 @@ void G_DeleteSave(savebrief_t const & sv)
char temp[BMAX_PATH];
if (G_ModDirSnprintf(temp, sizeof(temp), "%s", sv.path))
if (snprintf(temp, sizeof(temp), "%s%s", M_GetSavegamesPath().GetChars(), sv.path))
{
OSD_Printf("G_SavePlayer: file name \"%s\" too long\n", sv.path);
return;
@ -763,7 +755,7 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave)
if (sv.isValid())
{
if (G_ModDirSnprintf(fn, sizeof(fn), "%s", sv.path))
if (snprintf(fn, sizeof(fn), "%s%s", M_GetSavegamesPath().GetChars(), sv.path))
{
OSD_Printf("G_SavePlayer: file name \"%s\" too long\n", sv.path);
goto saveproblem;
@ -773,7 +765,7 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave)
else
{
static char const SaveName[] = "save0000.esv";
int const len = G_ModDirSnprintfLite(fn, ARRAY_SIZE(fn), SaveName);
int const len = snprintf(fn, ARRAY_SIZE(fn), "%s%s", M_GetSavegamesPath().GetChars(), SaveName);
if (len >= ARRAY_SSIZE(fn)-1)
{
OSD_Printf("G_SavePlayer: could not form automatic save path\n");

View file

@ -42,6 +42,8 @@
#include "printf.h"
#include "cmdlib.h"
#include "i_findfile.h"
#include "gamecontrol.h"
#include "m_argv.h"
//#include "version.h" // for GAMENAME
// Stuff that needs to be set up later.
@ -245,19 +247,19 @@ FString M_GetScreenshotsPath()
if (!UseKnownFolders())
{
return progdir;
path << progdir << "/Screenshots/";
}
else if (GetKnownFolder(-1, MyFOLDERID_Screenshots, true, path))
{
path << "/" GAMENAME;
path << "/" GAMENAME "/";
}
else if (GetKnownFolder(CSIDL_MYPICTURES, FOLDERID_Pictures, true, path))
{
path << "/Screenshots/" GAMENAME;
path << "/Screenshots/" GAMENAME "/";
}
else
{
return progdir;
path << progdir << "/Screenshots/";
}
CreatePath(path);
return path;
@ -270,32 +272,47 @@ FString M_GetScreenshotsPath()
// Returns the path to the default save games directory.
//
//===========================================================================
CVAR(String, cl_savedir, "", CVAR_ARCHIVE)
FString M_GetSavegamesPath()
{
FString path;
if (!UseKnownFolders())
auto dir = Args->CheckValue("-savedir");
if (dir)
{
return progdir;
path = dir;
path.Substitute("\\", "/");
if (path[path.Len() - 1] != '/') path << '/';
}
else if (*cl_savedir)
{
path = cl_savedir;
path.Substitute("\\", "/");
if (path[path.Len() - 1] != '/') path << '/';
path << LumpFilter << '/';
}
else if (!UseKnownFolders())
{
path << progdir << "Save/" << LumpFilter << "/";
}
// Try standard Saved Games folder
else if (GetKnownFolder(-1, FOLDERID_SavedGames, true, path))
{
path << "/" GAMENAME;
path << "/" GAMENAME "/" << LumpFilter << "/";
}
// Try defacto My Documents/My Games folder
else if (GetKnownFolder(CSIDL_PERSONAL, FOLDERID_Documents, true, path))
{
// I assume since this isn't a standard folder, it doesn't have
// a localized name either.
path << "/My Games/" GAMENAME;
CreatePath(path);
path << "/My Games/" GAMENAME "/" << LumpFilter << "/";
}
else
{
path = progdir;
path << progdir << "Save/" << LumpFilter << "/";
}
CreatePath(path);
return path;
}
@ -327,7 +344,7 @@ FString M_GetDocumentsPath()
{
// I assume since this isn't a standard folder, it doesn't have
// a localized name either.
path << "/My Games/" GAMENAME;
path << "/My Games/" GAMENAME "/";
CreatePath(path);
}
else
@ -337,6 +354,40 @@ FString M_GetDocumentsPath()
return path;
}
//===========================================================================
//
// M_GetDocumentsPath Windows
//
// Returns the path to the default documents directory.
//
//===========================================================================
FString M_GetDemoPath()
{
FString path;
// A portable INI means that this storage location should also be portable.
path.Format("%s" GAMENAME "_portable.ini", progdir.GetChars());
if (FileExists(path) || !UseKnownFolders())
{
path << progdir << "Demos/" << LumpFilter << '/';
}
else
// Try defacto My Documents/My Games folder
if (GetKnownFolder(CSIDL_PERSONAL, FOLDERID_Documents, true, path))
{
// I assume since this isn't a standard folder, it doesn't have
// a localized name either.
path << "/My Games/" GAMENAME "/" << LumpFilter << '/';
}
else
{
path << progdir << "Demos/" << LumpFilter << '/';
}
CreatePath(path);
return path;
}
//==========================================================================
//
// I_FindFirst

View file

@ -83,8 +83,6 @@ static inline void clearfilenames(void)
static inline void getfilenames(char const *path)
{
clearfilenames();
finddirs = klistpath(path,"*",CACHE1D_FIND_DIR);
}
#define POPULATE_VIDEO 1

View file

@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "menus.h"
#include "savegame.h"
#include "screens.h"
#include "i_specialpaths.h"
#include "vfs.h"
@ -159,7 +160,7 @@ void G_OpenDemoWrite(void)
if (demonum == MAXDEMOS)
return;
if (G_ModDirSnprintf(demofn, sizeof(demofn), DEMOFN_FMT, demonum))
if (snprintf(demofn, sizeof(demofn), "%s" DEMOFN_FMT, M_GetSavegamesPath().GetChars(), demonum))
{
initprintf("Couldn't start demo writing: INTERNAL ERROR: file name too long\n");
goto error_wopen_demo;

View file

@ -436,16 +436,6 @@ enum
};
#define G_ModDirSnprintf(buf, size, basename, ...) \
\
(((g_modDir[0] != '/') ? Bsnprintf(buf, size, "%s/" basename, g_modDir, ##__VA_ARGS__) \
: Bsnprintf(buf, size, basename, ##__VA_ARGS__)) >= ((int32_t)size) - 1\
)
#define G_ModDirSnprintfLite(buf, size, basename) \
\
((g_modDir[0] != '/') ? Bsnprintf(buf, size, "%s/%s", g_modDir, basename) \
: Bsnprintf(buf, size, basename))
static inline void G_NewGame_EnterLevel(void)
{
G_NewGame(ud.m_volume_number, ud.m_level_number, ud.m_player_skill);

View file

@ -25,6 +25,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "premap.h"
#include "prlights.h"
#include "savegame.h"
#include "i_specialpaths.h"
#include "gamecontrol.h"
BEGIN_RR_NS
@ -144,13 +146,13 @@ uint16_t g_nummenusaves;
static menusave_t * g_internalsaves;
static uint16_t g_numinternalsaves;
static void ReadSaveGameHeaders_CACHE1D(CACHE1D_FIND_REC *f)
static void ReadSaveGameHeaders_CACHE1D(TArray<FString>& saves)
{
savehead_t h;
for (; f != nullptr; f = f->next)
for (auto& save : saves)
{
char const * fn = f->name;
char const* fn = save;
auto fil = fopenFileReader(fn, 0);
if (!fil.isOpen())
continue;
@ -181,22 +183,14 @@ static void ReadSaveGameHeaders_CACHE1D(CACHE1D_FIND_REC *f)
}
}
static int countcache1dfind(CACHE1D_FIND_REC *f)
{
int x = 0;
for (; f != nullptr; f = f->next)
++x;
return x;
}
static void ReadSaveGameHeaders_Internal(void)
{
static char const DefaultPath[] = "/", SavePattern[] = "*.esv";
CACHE1D_FIND_REC *findfiles_default = klistpath(DefaultPath, SavePattern, CACHE1D_FIND_FILE);
FString pattern = M_GetSavegamesPath() + "*.esv";
TArray<FString> saves;
D_AddWildFile(saves, pattern);
// potentially overallocating but programmatically simple
int const numfiles = countcache1dfind(findfiles_default);
int const numfiles = saves.Size();
size_t const internalsavesize = sizeof(menusave_t) * numfiles;
g_internalsaves = (menusave_t *)Xrealloc(g_internalsaves, internalsavesize);
@ -205,8 +199,7 @@ static void ReadSaveGameHeaders_Internal(void)
g_internalsaves[x].clear();
g_numinternalsaves = 0;
ReadSaveGameHeaders_CACHE1D(findfiles_default);
klistfree(findfiles_default);
ReadSaveGameHeaders_CACHE1D(saves);
g_nummenusaves = 0;
for (int x = g_numinternalsaves-1; x >= 0; --x)
@ -439,7 +432,7 @@ void G_DeleteSave(savebrief_t const & sv)
char temp[BMAX_PATH];
if (G_ModDirSnprintf(temp, sizeof(temp), "%s", sv.path))
if (snprintf(temp, sizeof(temp), "%s%s", M_GetSavegamesPath().GetChars(), sv.path))
{
OSD_Printf("G_SavePlayer: file name \"%s\" too long\n", sv.path);
return;
@ -493,7 +486,7 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave)
if (sv.isValid())
{
if (G_ModDirSnprintf(temp, sizeof(temp), "%s", sv.path))
if (snprintf(temp, sizeof(temp), "%s%s", M_GetSavegamesPath().GetChars(), sv.path))
{
OSD_Printf("G_SavePlayer: file name \"%s\" too long\n", sv.path);
goto saveproblem;
@ -503,7 +496,7 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave)
else
{
static char const SaveName[] = "save0000.esv";
int const len = G_ModDirSnprintfLite(temp, ARRAY_SIZE(temp), SaveName);
int const len = snprintf(temp, ARRAY_SIZE(temp), "%s%s", M_GetSavegamesPath().GetChars(), SaveName);
if (len >= ARRAY_SSIZE(temp)-1)
{
OSD_Printf("G_SavePlayer: could not form automatic save path\n");

View file

@ -55,6 +55,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
#include "cache.h"
#include "colormap.h"
#include "player.h"
#include "i_specialpaths.h"
//void TimerFunc(task * Task);
BEGIN_SW_NS
@ -240,13 +241,13 @@ int SaveGame(short save_num)
PANEL_SPRITE tpanel_sprite;
PANEL_SPRITEp psp,cur,next;
SECTOR_OBJECTp sop;
char game_name[80];
char game_name[256];
int cnt = 0, saveisshot=0;
OrgTileP otp, next_otp;
Saveable_Init();
sprintf(game_name,"game%d.sav",save_num);
snprintf(game_name, 256, "%sgame%d.sav", M_GetSavegamesPath().GetChars(), save_num);
if ((fil = MOPEN_WRITE(game_name)) == MOPEN_WRITE_ERR)
return -1;
@ -702,11 +703,11 @@ int SaveGame(short save_num)
int LoadGameFullHeader(short save_num, char *descr, short *level, short *skill)
{
MFILE_READ fil;
char game_name[80];
char game_name[256];
short tile;
int ver;
sprintf(game_name,"game%d.sav",save_num);
snprintf(game_name, 256, "%sgame%d.sav", M_GetSavegamesPath().GetChars(), save_num);
if ((fil = MOPEN_READ(game_name)) == MOPEN_READ_ERR)
return -1;
@ -733,11 +734,11 @@ int LoadGameFullHeader(short save_num, char *descr, short *level, short *skill)
void LoadGameDescr(short save_num, char *descr)
{
MFILE_READ fil;
char game_name[80];
char game_name[256];
short tile;
int ver;
sprintf(game_name,"game%d.sav",save_num);
snprintf(game_name, 256, "%sgame%d.sav", M_GetSavegamesPath().GetChars(), save_num);
if ((fil = MOPEN_READ(game_name)) == MOPEN_READ_ERR)
return;
@ -770,7 +771,7 @@ int LoadGame(short save_num)
int16_t data_ndx;
PANEL_SPRITEp psp,next,cur;
PANEL_SPRITE tpanel_sprite;
char game_name[80];
char game_name[256];
OrgTileP otp, next_otp;
int RotNdx;
@ -781,7 +782,7 @@ int LoadGame(short save_num)
Saveable_Init();
sprintf(game_name,"game%d.sav",save_num);
snprintf(game_name, 256, "%sgame%d.sav", M_GetSavegamesPath().GetChars(), save_num);
if ((fil = MOPEN_READ(game_name)) == MOPEN_READ_ERR)
return -1;

14
source/thirdparty/include/base64.h vendored Normal file
View file

@ -0,0 +1,14 @@
//
// base64 encoding and decoding with C++.
// Version: 1.01.00
//
#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A
#define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A
#include <string>
std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);
#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */

121
source/thirdparty/src/base64.cpp vendored Normal file
View file

@ -0,0 +1,121 @@
/*
base64.cpp and base64.h
base64 encoding and decoding with C++.
Version: 1.01.00
Copyright (C) 2004-2017 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
#include "base64.h"
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for(j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = ( char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while((i++ < 3))
ret += '=';
}
return ret;
}
std::string base64_decode(std::string const& encoded_string) {
size_t in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]) & 0xff;
char_array_3[0] = ( char_array_4[0] << 2 ) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = 0; j < i; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]) & 0xff;
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}