- 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/keyboard.cpp
mact/src/input.cpp mact/src/input.cpp
thirdparty/src/base64.cpp
thirdparty/src/sjson.cpp thirdparty/src/sjson.cpp
thirdparty/src/crc32.cpp thirdparty/src/crc32.cpp
thirdparty/src/fix16.cpp thirdparty/src/fix16.cpp

View file

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

View file

@ -44,6 +44,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "seq.h" #include "seq.h"
#include "sfx.h" #include "sfx.h"
#include "sound.h" #include "sound.h"
#include "i_specialpaths.h"
#include "view.h" #include "view.h"
BEGIN_BLD_NS BEGIN_BLD_NS
@ -426,11 +427,13 @@ void MyLoadSave::Save(void)
void LoadSavedInfo(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; 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()) if (!hFile.isOpen())
ThrowError("Error loading save file header."); ThrowError("Error loading save file header.");
int vc; int vc;
@ -453,8 +456,8 @@ void LoadSavedInfo(void)
if ((uint32_t)hFile.Read(&gSaveGameOptions[nCount], sizeof(gSaveGameOptions[0])) != sizeof(gSaveGameOptions[0])) if ((uint32_t)hFile.Read(&gSaveGameOptions[nCount], sizeof(gSaveGameOptions[0])) != sizeof(gSaveGameOptions[0]))
ThrowError("Error reading save file."); ThrowError("Error reading save file.");
strcpy(strRestoreGameStrings[gSaveGameOptions[nCount].nSaveGameSlot], gSaveGameOptions[nCount].szUserGameName); strcpy(strRestoreGameStrings[gSaveGameOptions[nCount].nSaveGameSlot], gSaveGameOptions[nCount].szUserGameName);
nCount++;
} }
klistfree(pList);
} }
void UpdateSavedInfo(int nSlot) 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 "screen.h"
#include "sound.h" #include "sound.h"
#include "view.h" #include "view.h"
#include "i_specialpaths.h"
EXTERN_CVAR(Bool, hud_powerupduration) EXTERN_CVAR(Bool, hud_powerupduration)
@ -2102,7 +2103,7 @@ void SaveGame(CGameMenuItemZEditBitmap *pItem, CGameMenuEvent *event)
gGameMenuMgr.Deactivate(); gGameMenuMgr.Deactivate();
return; 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]); strcpy(gGameOptions.szUserGameName, strRestoreGameStrings[nSlot]);
sprintf(gGameOptions.szSaveGameName, "%s", strSaveGameName); sprintf(gGameOptions.szSaveGameName, "%s", strSaveGameName);
gGameOptions.nSaveGameSlot = nSlot; gGameOptions.nSaveGameSlot = nSlot;
@ -2124,7 +2125,7 @@ void QuickSaveGame(void)
gGameMenuMgr.Deactivate(); gGameMenuMgr.Deactivate();
return; 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]); strcpy(gGameOptions.szUserGameName, strRestoreGameStrings[gQuickSaveSlot]);
sprintf(gGameOptions.szSaveGameName, "%s", strSaveGameName); sprintf(gGameOptions.szSaveGameName, "%s", strSaveGameName);
gGameOptions.nSaveGameSlot = gQuickSaveSlot; gGameOptions.nSaveGameSlot = gQuickSaveSlot;
@ -2144,7 +2145,7 @@ void LoadGame(CGameMenuItemZEditBitmap *pItem, CGameMenuEvent *event)
int nSlot = pItem->at28; int nSlot = pItem->at28;
if (gGameOptions.nGameType > 0) if (gGameOptions.nGameType > 0)
return; 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)) if (!testkopen(strLoadGameName, 0))
return; return;
viewLoadingScreen(2518, "Loading", "Loading Saved Game", strRestoreGameStrings[nSlot]); viewLoadingScreen(2518, "Loading", "Loading Saved Game", strRestoreGameStrings[nSlot]);
@ -2159,7 +2160,7 @@ void QuickLoadGame(void)
char strLoadGameName[BMAX_PATH]; char strLoadGameName[BMAX_PATH];
if (gGameOptions.nGameType > 0) if (gGameOptions.nGameType > 0)
return; 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)) if (!testkopen(strLoadGameName, 0))
return; return;
viewLoadingScreen(2518, "Loading", "Loading Saved Game", strRestoreGameStrings[gQuickLoadSlot]); 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 int32_t pathsearchmode; // 0 = gamefs mode (default), 1 = localfs mode (editor's mode)
extern char g_modDir[BMAX_PATH];
enum { enum {
CACHE1D_FIND_FILE = 1, CACHE1D_FIND_FILE = 1,

View file

@ -122,9 +122,6 @@ static size_t maxsearchpathlen = 0;
int32_t pathsearchmode = 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) 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 "resourcefile.h"
#include "name.h" #include "name.h"
#include "m_swap.h" #include "m_swap.h"
#include "gamecontrol.h"
extern FString LumpFilter;
//========================================================================== //==========================================================================
// //

View file

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

View file

@ -462,12 +462,6 @@ CUSTOM_CVAR(String, rtsname, "", CVAR_ARCHIVE | CVAR_USERINFO)
{ {
RTS_Init(self); 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 #if 0

View file

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

View file

@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "menus.h" #include "menus.h"
#include "savegame.h" #include "savegame.h"
#include "screens.h" #include "screens.h"
#include "i_specialpaths.h"
#include "vfs.h" #include "vfs.h"
@ -156,7 +157,7 @@ void G_OpenDemoWrite(void)
if (demonum == MAXDEMOS) if (demonum == MAXDEMOS)
return; 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"); initprintf("Couldn't start demo writing: INTERNAL ERROR: file name too long\n");
goto error_wopen_demo; 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) static inline void G_NewGame_EnterLevel(void)
{ {
G_NewGame(ud.m_volume_number, ud.m_level_number, ud.m_player_skill); 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 "gamecvars.h"
#include "gamecontrol.h" #include "gamecontrol.h"
#include "gameconfigfile.h" #include "gameconfigfile.h"
#include "files.h"
#include "base64.h"
#include "vfs.h" #include "vfs.h"
@ -5615,13 +5617,21 @@ badindex:
int const quoteFilename = *insptr++; int const quoteFilename = *insptr++;
VM_ASSERT((unsigned)quoteFilename < MAXQUOTES && apStrings[quoteFilename], "invalid quote %d\n", quoteFilename); VM_ASSERT((unsigned)quoteFilename < MAXQUOTES && apStrings[quoteFilename], "invalid quote %d\n", quoteFilename);
FStringf IniSection("UserStrings.%s", LumpFilter.GetChars());
auto kFile = kopenFileReader(apStrings[quoteFilename], 0); auto IniKey = apStrings[quoteFilename];
if (!GameConfig->SetSection(IniSection))
if (!kFile.isOpen()) {
dispatch(); 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); size_t const numElements = Gv_GetArrayCountForAllocSize(arrayNum, filelength);
if (numElements > 0) if (numElements > 0)
@ -5649,7 +5659,7 @@ badindex:
{ {
void *const pArray = Xcalloc(1, newBytes); void *const pArray = Xcalloc(1, newBytes);
kFile.Read(pArray, readBytes); memcpy(pArray, decoded.c_str(), readBytes);
if (flags & GAMEARRAY_UNSIGNED) if (flags & GAMEARRAY_UNSIGNED)
{ {
@ -5668,12 +5678,11 @@ badindex:
#endif #endif
default: default:
memset((char *)pValues + readBytes, 0, newBytes - readBytes); memset((char *)pValues + readBytes, 0, newBytes - readBytes);
kFile.Read(pValues, readBytes); memcpy(pValues, decoded.c_str(), readBytes);
break; break;
} }
} }
kFile.Close();
dispatch(); dispatch();
} }
@ -5684,22 +5693,12 @@ badindex:
int const quoteFilename = *insptr++; int const quoteFilename = *insptr++;
VM_ASSERT((unsigned)quoteFilename < MAXQUOTES && apStrings[quoteFilename], "invalid quote %d\n", quoteFilename); VM_ASSERT((unsigned)quoteFilename < MAXQUOTES && apStrings[quoteFilename], "invalid quote %d\n", quoteFilename);
// No, we are not writing stuff to an arbitrary file on the hard drive! This is a first grade exploit for doing serious damage.
char temp[BMAX_PATH]; // Instead, encode the data as BASE64 and write it to the config file,
// which doesn't create a wide open door for exploits.
if (EDUKE32_PREDICT_FALSE(G_ModDirSnprintf(temp, sizeof(temp), "%s", apStrings[quoteFilename]))) FStringf IniSection("UserStrings.%s", LumpFilter.GetChars());
{ auto IniKey = apStrings[quoteFilename];
CON_ERRPRINTF("file name too long\n"); BufferWriter bw;
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();
}
switch (aGameArrays[arrayNum].flags & GAMEARRAY_SIZE_MASK) switch (aGameArrays[arrayNum].flags & GAMEARRAY_SIZE_MASK)
{ {
@ -5713,15 +5712,19 @@ badindex:
for (unative_t k = 0; k < numElements; ++k) for (unative_t k = 0; k < numElements; ++k)
pArray[k] = Gv_GetArrayValue(arrayNum, k); pArray[k] = Gv_GetArrayValue(arrayNum, k);
buildvfs_fwrite(pArray, 1, numDiskBytes, fil); bw.Write(pArray, numDiskBytes);
Xfree(pArray); Xfree(pArray);
break; break;
} }
#endif #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(); dispatch();
} }

View file

@ -27,6 +27,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "enet.h" #include "enet.h"
#include "sjson.h" #include "sjson.h"
#include "gamecvars.h" #include "gamecvars.h"
#include "i_specialpaths.h"
BEGIN_DUKE_NS BEGIN_DUKE_NS
@ -5631,7 +5632,7 @@ int portableBackupSave(const char * path, const char * name, int volume, int lev
char fn[BMAX_PATH]; 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; return 1;
} }

View file

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

View file

@ -42,6 +42,8 @@
#include "printf.h" #include "printf.h"
#include "cmdlib.h" #include "cmdlib.h"
#include "i_findfile.h" #include "i_findfile.h"
#include "gamecontrol.h"
#include "m_argv.h"
//#include "version.h" // for GAMENAME //#include "version.h" // for GAMENAME
// Stuff that needs to be set up later. // Stuff that needs to be set up later.
@ -245,19 +247,19 @@ FString M_GetScreenshotsPath()
if (!UseKnownFolders()) if (!UseKnownFolders())
{ {
return progdir; path << progdir << "/Screenshots/";
} }
else if (GetKnownFolder(-1, MyFOLDERID_Screenshots, true, path)) else if (GetKnownFolder(-1, MyFOLDERID_Screenshots, true, path))
{ {
path << "/" GAMENAME; path << "/" GAMENAME "/";
} }
else if (GetKnownFolder(CSIDL_MYPICTURES, FOLDERID_Pictures, true, path)) else if (GetKnownFolder(CSIDL_MYPICTURES, FOLDERID_Pictures, true, path))
{ {
path << "/Screenshots/" GAMENAME; path << "/Screenshots/" GAMENAME "/";
} }
else else
{ {
return progdir; path << progdir << "/Screenshots/";
} }
CreatePath(path); CreatePath(path);
return path; return path;
@ -270,32 +272,47 @@ FString M_GetScreenshotsPath()
// Returns the path to the default save games directory. // Returns the path to the default save games directory.
// //
//=========================================================================== //===========================================================================
CVAR(String, cl_savedir, "", CVAR_ARCHIVE)
FString M_GetSavegamesPath() FString M_GetSavegamesPath()
{ {
FString path; 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 // Try standard Saved Games folder
else if (GetKnownFolder(-1, FOLDERID_SavedGames, true, path)) else if (GetKnownFolder(-1, FOLDERID_SavedGames, true, path))
{ {
path << "/" GAMENAME; path << "/" GAMENAME "/" << LumpFilter << "/";
} }
// Try defacto My Documents/My Games folder // Try defacto My Documents/My Games folder
else if (GetKnownFolder(CSIDL_PERSONAL, FOLDERID_Documents, true, path)) else if (GetKnownFolder(CSIDL_PERSONAL, FOLDERID_Documents, true, path))
{ {
// I assume since this isn't a standard folder, it doesn't have // I assume since this isn't a standard folder, it doesn't have
// a localized name either. // a localized name either.
path << "/My Games/" GAMENAME; path << "/My Games/" GAMENAME "/" << LumpFilter << "/";
CreatePath(path);
} }
else else
{ {
path = progdir; path << progdir << "Save/" << LumpFilter << "/";
} }
CreatePath(path);
return path; return path;
} }
@ -327,7 +344,7 @@ FString M_GetDocumentsPath()
{ {
// I assume since this isn't a standard folder, it doesn't have // I assume since this isn't a standard folder, it doesn't have
// a localized name either. // a localized name either.
path << "/My Games/" GAMENAME; path << "/My Games/" GAMENAME "/";
CreatePath(path); CreatePath(path);
} }
else else
@ -337,6 +354,40 @@ FString M_GetDocumentsPath()
return path; 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 // I_FindFirst

View file

@ -83,8 +83,6 @@ static inline void clearfilenames(void)
static inline void getfilenames(char const *path) static inline void getfilenames(char const *path)
{ {
clearfilenames();
finddirs = klistpath(path,"*",CACHE1D_FIND_DIR);
} }
#define POPULATE_VIDEO 1 #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 "menus.h"
#include "savegame.h" #include "savegame.h"
#include "screens.h" #include "screens.h"
#include "i_specialpaths.h"
#include "vfs.h" #include "vfs.h"
@ -159,7 +160,7 @@ void G_OpenDemoWrite(void)
if (demonum == MAXDEMOS) if (demonum == MAXDEMOS)
return; 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"); initprintf("Couldn't start demo writing: INTERNAL ERROR: file name too long\n");
goto error_wopen_demo; 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) static inline void G_NewGame_EnterLevel(void)
{ {
G_NewGame(ud.m_volume_number, ud.m_level_number, ud.m_player_skill); 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 "premap.h"
#include "prlights.h" #include "prlights.h"
#include "savegame.h" #include "savegame.h"
#include "i_specialpaths.h"
#include "gamecontrol.h"
BEGIN_RR_NS BEGIN_RR_NS
@ -144,13 +146,13 @@ uint16_t g_nummenusaves;
static menusave_t * g_internalsaves; static menusave_t * g_internalsaves;
static uint16_t g_numinternalsaves; static uint16_t g_numinternalsaves;
static void ReadSaveGameHeaders_CACHE1D(CACHE1D_FIND_REC *f) static void ReadSaveGameHeaders_CACHE1D(TArray<FString>& saves)
{ {
savehead_t h; 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); auto fil = fopenFileReader(fn, 0);
if (!fil.isOpen()) if (!fil.isOpen())
continue; 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 void ReadSaveGameHeaders_Internal(void)
{ {
static char const DefaultPath[] = "/", SavePattern[] = "*.esv"; FString pattern = M_GetSavegamesPath() + "*.esv";
TArray<FString> saves;
CACHE1D_FIND_REC *findfiles_default = klistpath(DefaultPath, SavePattern, CACHE1D_FIND_FILE); D_AddWildFile(saves, pattern);
// potentially overallocating but programmatically simple // potentially overallocating but programmatically simple
int const numfiles = countcache1dfind(findfiles_default); int const numfiles = saves.Size();
size_t const internalsavesize = sizeof(menusave_t) * numfiles; size_t const internalsavesize = sizeof(menusave_t) * numfiles;
g_internalsaves = (menusave_t *)Xrealloc(g_internalsaves, internalsavesize); g_internalsaves = (menusave_t *)Xrealloc(g_internalsaves, internalsavesize);
@ -205,8 +199,7 @@ static void ReadSaveGameHeaders_Internal(void)
g_internalsaves[x].clear(); g_internalsaves[x].clear();
g_numinternalsaves = 0; g_numinternalsaves = 0;
ReadSaveGameHeaders_CACHE1D(findfiles_default); ReadSaveGameHeaders_CACHE1D(saves);
klistfree(findfiles_default);
g_nummenusaves = 0; g_nummenusaves = 0;
for (int x = g_numinternalsaves-1; x >= 0; --x) for (int x = g_numinternalsaves-1; x >= 0; --x)
@ -439,7 +432,7 @@ void G_DeleteSave(savebrief_t const & sv)
char temp[BMAX_PATH]; 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); OSD_Printf("G_SavePlayer: file name \"%s\" too long\n", sv.path);
return; return;
@ -493,7 +486,7 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave)
if (sv.isValid()) 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); OSD_Printf("G_SavePlayer: file name \"%s\" too long\n", sv.path);
goto saveproblem; goto saveproblem;
@ -503,7 +496,7 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave)
else else
{ {
static char const SaveName[] = "save0000.esv"; 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) if (len >= ARRAY_SSIZE(temp)-1)
{ {
OSD_Printf("G_SavePlayer: could not form automatic save path\n"); 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 "cache.h"
#include "colormap.h" #include "colormap.h"
#include "player.h" #include "player.h"
#include "i_specialpaths.h"
//void TimerFunc(task * Task); //void TimerFunc(task * Task);
BEGIN_SW_NS BEGIN_SW_NS
@ -240,13 +241,13 @@ int SaveGame(short save_num)
PANEL_SPRITE tpanel_sprite; PANEL_SPRITE tpanel_sprite;
PANEL_SPRITEp psp,cur,next; PANEL_SPRITEp psp,cur,next;
SECTOR_OBJECTp sop; SECTOR_OBJECTp sop;
char game_name[80]; char game_name[256];
int cnt = 0, saveisshot=0; int cnt = 0, saveisshot=0;
OrgTileP otp, next_otp; OrgTileP otp, next_otp;
Saveable_Init(); 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) if ((fil = MOPEN_WRITE(game_name)) == MOPEN_WRITE_ERR)
return -1; return -1;
@ -702,11 +703,11 @@ int SaveGame(short save_num)
int LoadGameFullHeader(short save_num, char *descr, short *level, short *skill) int LoadGameFullHeader(short save_num, char *descr, short *level, short *skill)
{ {
MFILE_READ fil; MFILE_READ fil;
char game_name[80]; char game_name[256];
short tile; short tile;
int ver; 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) if ((fil = MOPEN_READ(game_name)) == MOPEN_READ_ERR)
return -1; return -1;
@ -733,11 +734,11 @@ int LoadGameFullHeader(short save_num, char *descr, short *level, short *skill)
void LoadGameDescr(short save_num, char *descr) void LoadGameDescr(short save_num, char *descr)
{ {
MFILE_READ fil; MFILE_READ fil;
char game_name[80]; char game_name[256];
short tile; short tile;
int ver; 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) if ((fil = MOPEN_READ(game_name)) == MOPEN_READ_ERR)
return; return;
@ -770,7 +771,7 @@ int LoadGame(short save_num)
int16_t data_ndx; int16_t data_ndx;
PANEL_SPRITEp psp,next,cur; PANEL_SPRITEp psp,next,cur;
PANEL_SPRITE tpanel_sprite; PANEL_SPRITE tpanel_sprite;
char game_name[80]; char game_name[256];
OrgTileP otp, next_otp; OrgTileP otp, next_otp;
int RotNdx; int RotNdx;
@ -781,7 +782,7 @@ int LoadGame(short save_num)
Saveable_Init(); 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) if ((fil = MOPEN_READ(game_name)) == MOPEN_READ_ERR)
return -1; 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;
}