From 1149b4f4aa8c0c0a0cf93a4a204b01a68c34e058 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 2 Nov 2019 00:38:30 +0100 Subject: [PATCH] - 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. --- source/CMakeLists.txt | 1 + source/blood/src/common_game.h | 6 -- source/blood/src/demo.cpp | 20 ++-- source/blood/src/loadsave.cpp | 11 +- source/blood/src/menu.cpp | 9 +- source/build/include/cache1d.h | 2 - source/build/src/cache1d.cpp | 3 - source/common/filesystem/resourcefile.cpp | 3 +- source/common/gamecontrol.h | 2 + source/common/gamecvars.cpp | 6 -- source/common/i_specialpaths.h | 1 + source/duke3d/src/demo.cpp | 3 +- source/duke3d/src/game.h | 7 -- source/duke3d/src/gameexec.cpp | 59 ++++++----- source/duke3d/src/player.cpp | 3 +- source/duke3d/src/savegame.cpp | 34 +++--- source/platform/win32/i_specialpaths.cpp | 73 +++++++++++-- source/platform/win32/startwin.game.cpp | 2 - source/rr/src/demo.cpp | 3 +- source/rr/src/game.h | 10 -- source/rr/src/savegame.cpp | 39 +++---- source/sw/src/save.cpp | 23 ++-- source/thirdparty/include/base64.h | 14 +++ source/thirdparty/src/base64.cpp | 121 ++++++++++++++++++++++ 24 files changed, 302 insertions(+), 153 deletions(-) create mode 100644 source/thirdparty/include/base64.h create mode 100644 source/thirdparty/src/base64.cpp diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 172977b17..802c2169a 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -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 diff --git a/source/blood/src/common_game.h b/source/blood/src/common_game.h index 8ca7e8890..25216260d 100644 --- a/source/blood/src/common_game.h +++ b/source/blood/src/common_game.h @@ -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) { diff --git a/source/blood/src/demo.cpp b/source/blood/src/demo.cpp index a42173af9..95e5324a5 100644 --- a/source/blood/src/demo.cpp +++ b/source/blood/src/demo.cpp @@ -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 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; } diff --git a/source/blood/src/loadsave.cpp b/source/blood/src/loadsave.cpp index d576f6106..9e3234690 100644 --- a/source/blood/src/loadsave.cpp +++ b/source/blood/src/loadsave.cpp @@ -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 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) diff --git a/source/blood/src/menu.cpp b/source/blood/src/menu.cpp index 07382b881..afbc017d1 100644 --- a/source/blood/src/menu.cpp +++ b/source/blood/src/menu.cpp @@ -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]); diff --git a/source/build/include/cache1d.h b/source/build/include/cache1d.h index 8bf9aa2e1..2c3fac8fa 100644 --- a/source/build/include/cache1d.h +++ b/source/build/include/cache1d.h @@ -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, diff --git a/source/build/src/cache1d.cpp b/source/build/src/cache1d.cpp index 488f32dcd..245991840 100644 --- a/source/build/src/cache1d.cpp +++ b/source/build/src/cache1d.cpp @@ -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) { diff --git a/source/common/filesystem/resourcefile.cpp b/source/common/filesystem/resourcefile.cpp index 3fed881f8..36857ee67 100644 --- a/source/common/filesystem/resourcefile.cpp +++ b/source/common/filesystem/resourcefile.cpp @@ -38,8 +38,7 @@ #include "resourcefile.h" #include "name.h" #include "m_swap.h" - -extern FString LumpFilter; +#include "gamecontrol.h" //========================================================================== // diff --git a/source/common/gamecontrol.h b/source/common/gamecontrol.h index 68cf9bfbb..59013abd0 100644 --- a/source/common/gamecontrol.h +++ b/source/common/gamecontrol.h @@ -9,8 +9,10 @@ #include "gamecvars.h" extern FString currentGame; +extern FString LumpFilter; class FArgs; +void D_AddWildFile(TArray& wadfiles, const char* value); extern uint8_t KeyboardKeys[NUMGAMEFUNCTIONS][2]; diff --git a/source/common/gamecvars.cpp b/source/common/gamecvars.cpp index cd58531f3..3e92e61bf 100644 --- a/source/common/gamecvars.cpp +++ b/source/common/gamecvars.cpp @@ -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 diff --git a/source/common/i_specialpaths.h b/source/common/i_specialpaths.h index 7c51bae37..e8929a048 100644 --- a/source/common/i_specialpaths.h +++ b/source/common/i_specialpaths.h @@ -11,6 +11,7 @@ FString M_GetConfigPath(bool for_reading); FString M_GetScreenshotsPath(); FString M_GetSavegamesPath(); FString M_GetDocumentsPath(); +FString M_GetDemoPath(); #ifdef __APPLE__ diff --git a/source/duke3d/src/demo.cpp b/source/duke3d/src/demo.cpp index 7e642b905..9dc616f14 100644 --- a/source/duke3d/src/demo.cpp +++ b/source/duke3d/src/demo.cpp @@ -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; diff --git a/source/duke3d/src/game.h b/source/duke3d/src/game.h index 8627c2137..90cea299e 100644 --- a/source/duke3d/src/game.h +++ b/source/duke3d/src/game.h @@ -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); diff --git a/source/duke3d/src/gameexec.cpp b/source/duke3d/src/gameexec.cpp index a64a1ee1b..db5949985 100644 --- a/source/duke3d/src/gameexec.cpp +++ b/source/duke3d/src/gameexec.cpp @@ -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); + 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; - auto kFile = kopenFileReader(apStrings[quoteFilename], 0); - - if (!kFile.isOpen()) - dispatch(); - - 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(); } diff --git a/source/duke3d/src/player.cpp b/source/duke3d/src/player.cpp index b1052bc8c..083431131 100644 --- a/source/duke3d/src/player.cpp +++ b/source/duke3d/src/player.cpp @@ -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; } diff --git a/source/duke3d/src/savegame.cpp b/source/duke3d/src/savegame.cpp index 360737321..1cbc73b6e 100644 --- a/source/duke3d/src/savegame.cpp +++ b/source/duke3d/src/savegame.cpp @@ -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 &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 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"); diff --git a/source/platform/win32/i_specialpaths.cpp b/source/platform/win32/i_specialpaths.cpp index f520ed207..3cd9c8a20 100644 --- a/source/platform/win32/i_specialpaths.cpp +++ b/source/platform/win32/i_specialpaths.cpp @@ -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 diff --git a/source/platform/win32/startwin.game.cpp b/source/platform/win32/startwin.game.cpp index 343791cb7..88255ffa3 100644 --- a/source/platform/win32/startwin.game.cpp +++ b/source/platform/win32/startwin.game.cpp @@ -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 diff --git a/source/rr/src/demo.cpp b/source/rr/src/demo.cpp index cb06ee09d..861920fdc 100644 --- a/source/rr/src/demo.cpp +++ b/source/rr/src/demo.cpp @@ -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; diff --git a/source/rr/src/game.h b/source/rr/src/game.h index 63e8b89b0..83b72377f 100644 --- a/source/rr/src/game.h +++ b/source/rr/src/game.h @@ -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); diff --git a/source/rr/src/savegame.cpp b/source/rr/src/savegame.cpp index 1d4495a0f..2147e7360 100644 --- a/source/rr/src/savegame.cpp +++ b/source/rr/src/savegame.cpp @@ -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,14 +146,14 @@ 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& saves) { savehead_t h; - for (; f != nullptr; f = f->next) - { - char const * fn = f->name; - auto fil = fopenFileReader(fn, 0); + for (auto& save : saves) + { + char const* fn = save; + auto fil = fopenFileReader(fn, 0); if (!fil.isOpen()) continue; @@ -181,23 +183,15 @@ 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 saves; + D_AddWildFile(saves, pattern); // potentially overallocating but programmatically simple - int const numfiles = countcache1dfind(findfiles_default); - size_t const internalsavesize = sizeof(menusave_t) * numfiles; + 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"); diff --git a/source/sw/src/save.cpp b/source/sw/src/save.cpp index be11dbf44..3ee8e5b48 100644 --- a/source/sw/src/save.cpp +++ b/source/sw/src/save.cpp @@ -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,12 +703,12 @@ 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); - if ((fil = MOPEN_READ(game_name)) == MOPEN_READ_ERR) + snprintf(game_name, 256, "%sgame%d.sav", M_GetSavegamesPath().GetChars(), save_num); + if ((fil = MOPEN_READ(game_name)) == MOPEN_READ_ERR) return -1; MREAD(&ver,sizeof(ver),1,fil); @@ -733,12 +734,12 @@ 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); - if ((fil = MOPEN_READ(game_name)) == MOPEN_READ_ERR) + snprintf(game_name, 256, "%sgame%d.sav", M_GetSavegamesPath().GetChars(), save_num); + if ((fil = MOPEN_READ(game_name)) == MOPEN_READ_ERR) return; MREAD(&ver,sizeof(ver),1,fil); @@ -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,8 +782,8 @@ int LoadGame(short save_num) Saveable_Init(); - sprintf(game_name,"game%d.sav",save_num); - if ((fil = MOPEN_READ(game_name)) == MOPEN_READ_ERR) + snprintf(game_name, 256, "%sgame%d.sav", M_GetSavegamesPath().GetChars(), save_num); + if ((fil = MOPEN_READ(game_name)) == MOPEN_READ_ERR) return -1; MREAD(&i,sizeof(i),1,fil); diff --git a/source/thirdparty/include/base64.h b/source/thirdparty/include/base64.h new file mode 100644 index 000000000..dd1134c30 --- /dev/null +++ b/source/thirdparty/include/base64.h @@ -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 + +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 */ diff --git a/source/thirdparty/src/base64.cpp b/source/thirdparty/src/base64.cpp new file mode 100644 index 000000000..7dfc7dd4e --- /dev/null +++ b/source/thirdparty/src/base64.cpp @@ -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; +}