2010-11-20 10:37:14 +00:00
|
|
|
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
|
|
|
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
2020-09-26 19:41:58 +00:00
|
|
|
#include "linereader.h"
|
2017-08-30 11:59:42 +00:00
|
|
|
#include <base/math.h>
|
2010-05-29 07:25:38 +00:00
|
|
|
#include <base/system.h>
|
2020-09-20 22:07:31 +00:00
|
|
|
#include <engine/client/updater.h>
|
2010-05-29 07:25:38 +00:00
|
|
|
#include <engine/storage.h>
|
|
|
|
|
2021-04-17 15:49:41 +00:00
|
|
|
#ifdef CONF_PLATFORM_HAIKU
|
|
|
|
#include <stdlib.h>
|
|
|
|
#endif
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
class CStorage : public IStorage
|
|
|
|
{
|
|
|
|
public:
|
2021-09-13 08:06:34 +00:00
|
|
|
char m_aaStoragePaths[MAX_PATHS][IO_MAX_PATH_LENGTH];
|
2010-10-06 21:07:35 +00:00
|
|
|
int m_NumPaths;
|
2021-09-13 08:06:34 +00:00
|
|
|
char m_aDatadir[IO_MAX_PATH_LENGTH];
|
|
|
|
char m_aUserdir[IO_MAX_PATH_LENGTH];
|
|
|
|
char m_aCurrentdir[IO_MAX_PATH_LENGTH];
|
|
|
|
char m_aBinarydir[IO_MAX_PATH_LENGTH];
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
CStorage()
|
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
mem_zero(m_aaStoragePaths, sizeof(m_aaStoragePaths));
|
|
|
|
m_NumPaths = 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
m_aDatadir[0] = 0;
|
2010-10-06 21:07:35 +00:00
|
|
|
m_aUserdir[0] = 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2021-12-21 16:19:42 +00:00
|
|
|
int Init(int StorageType, int NumArgs, const char **ppArguments)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
// get userdir
|
2021-12-21 16:19:42 +00:00
|
|
|
char aFallbackUserdir[IO_MAX_PATH_LENGTH];
|
|
|
|
fs_storage_path("DDNet", m_aUserdir, sizeof(m_aUserdir));
|
|
|
|
fs_storage_path("Teeworlds", aFallbackUserdir, sizeof(aFallbackUserdir));
|
|
|
|
|
|
|
|
if(!fs_is_dir(m_aUserdir) && fs_is_dir(aFallbackUserdir))
|
|
|
|
{
|
2022-07-09 16:14:56 +00:00
|
|
|
str_copy(m_aUserdir, aFallbackUserdir);
|
2021-12-21 16:19:42 +00:00
|
|
|
}
|
2010-08-05 18:26:03 +00:00
|
|
|
|
2010-10-06 21:07:35 +00:00
|
|
|
// get datadir
|
|
|
|
FindDatadir(ppArguments[0]);
|
|
|
|
|
2020-09-20 22:07:31 +00:00
|
|
|
// get binarydir
|
|
|
|
FindBinarydir(ppArguments[0]);
|
|
|
|
|
2010-12-11 22:10:13 +00:00
|
|
|
// get currentdir
|
|
|
|
if(!fs_getcwd(m_aCurrentdir, sizeof(m_aCurrentdir)))
|
|
|
|
m_aCurrentdir[0] = 0;
|
|
|
|
|
2010-10-06 21:07:35 +00:00
|
|
|
// load paths from storage.cfg
|
|
|
|
LoadPaths(ppArguments[0]);
|
|
|
|
|
|
|
|
if(!m_NumPaths)
|
|
|
|
{
|
|
|
|
dbg_msg("storage", "using standard paths");
|
|
|
|
AddDefaultPaths();
|
|
|
|
}
|
|
|
|
|
|
|
|
// add save directories
|
2022-04-02 11:03:58 +00:00
|
|
|
if(StorageType != STORAGETYPE_BASIC && m_NumPaths && (!m_aaStoragePaths[TYPE_SAVE][0] || fs_makedir_rec_for(m_aaStoragePaths[TYPE_SAVE]) || !fs_makedir(m_aaStoragePaths[TYPE_SAVE])))
|
2010-10-06 21:07:35 +00:00
|
|
|
{
|
2021-09-13 08:06:34 +00:00
|
|
|
char aPath[IO_MAX_PATH_LENGTH];
|
2012-01-09 00:38:45 +00:00
|
|
|
if(StorageType == STORAGETYPE_CLIENT)
|
|
|
|
{
|
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "screenshots", aPath, sizeof(aPath)));
|
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "screenshots/auto", aPath, sizeof(aPath)));
|
2015-06-17 12:13:19 +00:00
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "screenshots/auto/stats", aPath, sizeof(aPath)));
|
2012-01-09 00:38:45 +00:00
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "maps", aPath, sizeof(aPath)));
|
2020-10-03 19:09:19 +00:00
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "mapres", aPath, sizeof(aPath)));
|
2012-01-09 00:38:45 +00:00
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "downloadedmaps", aPath, sizeof(aPath)));
|
2020-10-03 19:09:19 +00:00
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "skins", aPath, sizeof(aPath)));
|
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "downloadedskins", aPath, sizeof(aPath)));
|
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "themes", aPath, sizeof(aPath)));
|
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "assets", aPath, sizeof(aPath)));
|
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "assets/emoticons", aPath, sizeof(aPath)));
|
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "assets/entities", aPath, sizeof(aPath)));
|
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "assets/game", aPath, sizeof(aPath)));
|
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "assets/particles", aPath, sizeof(aPath)));
|
2022-04-14 11:31:00 +00:00
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "assets/hud", aPath, sizeof(aPath)));
|
2022-06-14 17:28:51 +00:00
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "assets/extras", aPath, sizeof(aPath)));
|
2016-08-31 16:07:27 +00:00
|
|
|
#if defined(CONF_VIDEORECORDER)
|
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "videos", aPath, sizeof(aPath)));
|
|
|
|
#endif
|
2012-01-09 00:38:45 +00:00
|
|
|
}
|
2010-10-06 21:07:35 +00:00
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "dumps", aPath, sizeof(aPath)));
|
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "demos", aPath, sizeof(aPath)));
|
2010-12-12 15:48:13 +00:00
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "demos/auto", aPath, sizeof(aPath)));
|
2017-09-28 17:14:36 +00:00
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "demos/auto/race", aPath, sizeof(aPath)));
|
2019-05-20 21:55:40 +00:00
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "demos/replays", aPath, sizeof(aPath)));
|
2014-01-19 03:02:01 +00:00
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "editor", aPath, sizeof(aPath)));
|
2011-02-04 17:25:04 +00:00
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "ghosts", aPath, sizeof(aPath)));
|
2017-09-12 12:58:44 +00:00
|
|
|
fs_makedir(GetPath(TYPE_SAVE, "teehistorian", aPath, sizeof(aPath)));
|
2010-10-06 21:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return m_NumPaths ? 0 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadPaths(const char *pArgv0)
|
|
|
|
{
|
|
|
|
// check current directory
|
2021-12-17 20:58:28 +00:00
|
|
|
IOHANDLE File = io_open("storage.cfg", IOFLAG_READ | IOFLAG_SKIP_BOM);
|
2010-10-06 21:07:35 +00:00
|
|
|
if(!File)
|
|
|
|
{
|
|
|
|
// check usable path in argv[0]
|
|
|
|
unsigned int Pos = ~0U;
|
|
|
|
for(unsigned i = 0; pArgv0[i]; i++)
|
|
|
|
if(pArgv0[i] == '/' || pArgv0[i] == '\\')
|
|
|
|
Pos = i;
|
2021-09-13 08:06:34 +00:00
|
|
|
if(Pos < IO_MAX_PATH_LENGTH)
|
2010-10-06 21:07:35 +00:00
|
|
|
{
|
2021-09-13 08:06:34 +00:00
|
|
|
char aBuffer[IO_MAX_PATH_LENGTH];
|
2020-09-26 19:41:58 +00:00
|
|
|
str_copy(aBuffer, pArgv0, Pos + 1);
|
2010-10-06 21:07:35 +00:00
|
|
|
str_append(aBuffer, "/storage.cfg", sizeof(aBuffer));
|
2021-12-17 20:58:28 +00:00
|
|
|
File = io_open(aBuffer, IOFLAG_READ | IOFLAG_SKIP_BOM);
|
2010-10-06 21:07:35 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2021-09-13 08:06:34 +00:00
|
|
|
if(Pos >= IO_MAX_PATH_LENGTH || !File)
|
2010-10-06 21:07:35 +00:00
|
|
|
{
|
|
|
|
dbg_msg("storage", "couldn't open storage.cfg");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char *pLine;
|
|
|
|
CLineReader LineReader;
|
|
|
|
LineReader.Init(File);
|
|
|
|
|
|
|
|
while((pLine = LineReader.Get()))
|
|
|
|
{
|
2018-07-25 08:29:05 +00:00
|
|
|
const char *pLineWithoutPrefix = str_startswith(pLine, "add_path ");
|
|
|
|
if(pLineWithoutPrefix)
|
|
|
|
{
|
2018-07-26 13:46:54 +00:00
|
|
|
AddPath(pLineWithoutPrefix);
|
2018-07-25 08:29:05 +00:00
|
|
|
}
|
2010-10-06 21:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
io_close(File);
|
|
|
|
|
|
|
|
if(!m_NumPaths)
|
|
|
|
dbg_msg("storage", "no paths found in storage.cfg");
|
|
|
|
}
|
|
|
|
|
|
|
|
void AddDefaultPaths()
|
|
|
|
{
|
|
|
|
AddPath("$USERDIR");
|
|
|
|
AddPath("$DATADIR");
|
|
|
|
AddPath("$CURRENTDIR");
|
|
|
|
}
|
|
|
|
|
|
|
|
void AddPath(const char *pPath)
|
|
|
|
{
|
|
|
|
if(m_NumPaths >= MAX_PATHS || !pPath[0])
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(!str_comp(pPath, "$USERDIR"))
|
|
|
|
{
|
|
|
|
if(m_aUserdir[0])
|
2010-11-13 22:54:34 +00:00
|
|
|
{
|
2022-07-09 16:14:56 +00:00
|
|
|
str_copy(m_aaStoragePaths[m_NumPaths++], m_aUserdir);
|
2010-11-13 22:54:34 +00:00
|
|
|
dbg_msg("storage", "added path '$USERDIR' ('%s')", m_aUserdir);
|
|
|
|
}
|
2010-10-06 21:07:35 +00:00
|
|
|
}
|
|
|
|
else if(!str_comp(pPath, "$DATADIR"))
|
|
|
|
{
|
|
|
|
if(m_aDatadir[0])
|
2010-11-13 22:54:34 +00:00
|
|
|
{
|
2022-07-09 16:14:56 +00:00
|
|
|
str_copy(m_aaStoragePaths[m_NumPaths++], m_aDatadir);
|
2010-11-13 22:54:34 +00:00
|
|
|
dbg_msg("storage", "added path '$DATADIR' ('%s')", m_aDatadir);
|
|
|
|
}
|
2010-10-06 21:07:35 +00:00
|
|
|
}
|
|
|
|
else if(!str_comp(pPath, "$CURRENTDIR"))
|
|
|
|
{
|
|
|
|
m_aaStoragePaths[m_NumPaths++][0] = 0;
|
2010-12-11 22:10:13 +00:00
|
|
|
dbg_msg("storage", "added path '$CURRENTDIR' ('%s')", m_aCurrentdir);
|
2010-10-06 21:07:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(fs_is_dir(pPath))
|
2010-11-13 22:54:34 +00:00
|
|
|
{
|
2022-07-09 16:14:56 +00:00
|
|
|
str_copy(m_aaStoragePaths[m_NumPaths++], pPath);
|
2010-11-13 22:54:34 +00:00
|
|
|
dbg_msg("storage", "added path '%s'", pPath);
|
|
|
|
}
|
2010-10-06 21:07:35 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-10-06 21:07:35 +00:00
|
|
|
void FindDatadir(const char *pArgv0)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-11-17 00:02:29 +00:00
|
|
|
// 1) use data-dir in PWD if present
|
2010-05-29 07:25:38 +00:00
|
|
|
if(fs_is_dir("data/mapres"))
|
|
|
|
{
|
2022-07-09 16:14:56 +00:00
|
|
|
str_copy(m_aDatadir, "data");
|
2010-10-06 21:07:35 +00:00
|
|
|
return;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
#if defined(DATA_DIR)
|
2010-11-17 00:02:29 +00:00
|
|
|
// 2) use compiled-in data-dir if present
|
2010-10-06 21:07:35 +00:00
|
|
|
if(fs_is_dir(DATA_DIR "/mapres"))
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
str_copy(m_aDatadir, DATA_DIR, sizeof(m_aDatadir));
|
2010-10-06 21:07:35 +00:00
|
|
|
return;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2020-09-26 19:41:58 +00:00
|
|
|
#endif
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-11-17 00:02:29 +00:00
|
|
|
// 3) check for usable path in argv[0]
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2021-04-17 15:49:41 +00:00
|
|
|
#ifdef CONF_PLATFORM_HAIKU
|
|
|
|
pArgv0 = realpath(pArgv0, NULL);
|
|
|
|
#endif
|
2010-05-29 07:25:38 +00:00
|
|
|
unsigned int Pos = ~0U;
|
|
|
|
for(unsigned i = 0; pArgv0[i]; i++)
|
2010-10-06 21:07:35 +00:00
|
|
|
if(pArgv0[i] == '/' || pArgv0[i] == '\\')
|
2010-05-29 07:25:38 +00:00
|
|
|
Pos = i;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2021-09-13 08:06:34 +00:00
|
|
|
if(Pos < IO_MAX_PATH_LENGTH)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2021-09-13 08:06:34 +00:00
|
|
|
char aBuf[IO_MAX_PATH_LENGTH];
|
|
|
|
char aDir[IO_MAX_PATH_LENGTH];
|
2020-09-20 22:07:31 +00:00
|
|
|
str_copy(aDir, pArgv0, Pos + 1);
|
|
|
|
str_format(aBuf, sizeof(aBuf), "%s/data/mapres", aDir);
|
2018-04-05 03:11:31 +00:00
|
|
|
if(fs_is_dir(aBuf))
|
|
|
|
{
|
2020-09-20 22:07:31 +00:00
|
|
|
str_format(m_aDatadir, sizeof(m_aDatadir), "%s/data", aDir);
|
2010-10-06 21:07:35 +00:00
|
|
|
return;
|
2018-04-05 03:11:31 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-17 15:49:41 +00:00
|
|
|
#ifdef CONF_PLATFORM_HAIKU
|
|
|
|
free((void *)pArgv0);
|
|
|
|
#endif
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
#if defined(CONF_FAMILY_UNIX)
|
2010-11-17 00:02:29 +00:00
|
|
|
// 4) check for all default locations
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2020-10-27 17:57:14 +00:00
|
|
|
const char *apDirs[] = {
|
2017-09-01 11:36:37 +00:00
|
|
|
"/usr/share/ddnet",
|
|
|
|
"/usr/share/games/ddnet",
|
|
|
|
"/usr/local/share/ddnet",
|
|
|
|
"/usr/local/share/games/ddnet",
|
|
|
|
"/usr/pkg/share/ddnet",
|
|
|
|
"/usr/pkg/share/games/ddnet",
|
2020-09-26 19:41:58 +00:00
|
|
|
"/opt/ddnet"};
|
2022-03-30 13:16:19 +00:00
|
|
|
const int DirsCount = std::size(apDirs);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
int i;
|
2020-09-26 19:41:58 +00:00
|
|
|
for(i = 0; i < DirsCount; i++)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-12-11 21:48:37 +00:00
|
|
|
char aBuf[128];
|
2020-10-27 17:57:14 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%s/data/mapres", apDirs[i]);
|
2010-12-11 21:48:37 +00:00
|
|
|
if(fs_is_dir(aBuf))
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2020-10-27 17:57:14 +00:00
|
|
|
str_format(m_aDatadir, sizeof(m_aDatadir), "%s/data", apDirs[i]);
|
2010-10-06 21:07:35 +00:00
|
|
|
return;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-26 19:41:58 +00:00
|
|
|
#endif
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-09-20 22:07:31 +00:00
|
|
|
dbg_msg("storage", "warning: no data directory found");
|
|
|
|
}
|
|
|
|
|
|
|
|
void FindBinarydir(const char *pArgv0)
|
|
|
|
{
|
|
|
|
#if defined(BINARY_DIR)
|
|
|
|
str_copy(m_aBinarydir, BINARY_DIR, sizeof(m_aBinarydir));
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// check for usable path in argv[0]
|
|
|
|
{
|
|
|
|
unsigned int Pos = ~0U;
|
|
|
|
for(unsigned i = 0; pArgv0[i]; i++)
|
|
|
|
if(pArgv0[i] == '/' || pArgv0[i] == '\\')
|
|
|
|
Pos = i;
|
|
|
|
|
2021-09-13 08:06:34 +00:00
|
|
|
if(Pos < IO_MAX_PATH_LENGTH)
|
2020-09-20 22:07:31 +00:00
|
|
|
{
|
2021-09-13 08:06:34 +00:00
|
|
|
char aBuf[IO_MAX_PATH_LENGTH];
|
2020-09-20 22:07:31 +00:00
|
|
|
str_copy(m_aBinarydir, pArgv0, Pos + 1);
|
|
|
|
str_format(aBuf, sizeof(aBuf), "%s/" PLAT_SERVER_EXEC, m_aBinarydir);
|
|
|
|
IOHANDLE File = io_open(aBuf, IOFLAG_READ);
|
|
|
|
if(File)
|
|
|
|
{
|
|
|
|
io_close(File);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
2020-09-24 08:46:19 +00:00
|
|
|
{
|
2021-02-12 12:40:29 +00:00
|
|
|
#if defined(CONF_PLATFORM_MACOS)
|
2020-09-24 08:46:19 +00:00
|
|
|
str_append(m_aBinarydir, "/../../../DDNet-Server.app/Contents/MacOS", sizeof(m_aBinarydir));
|
|
|
|
str_format(aBuf, sizeof(aBuf), "%s/" PLAT_SERVER_EXEC, m_aBinarydir);
|
2022-03-24 01:01:25 +00:00
|
|
|
IOHANDLE FileBis = io_open(aBuf, IOFLAG_READ);
|
|
|
|
if(FileBis)
|
2020-09-24 08:46:19 +00:00
|
|
|
{
|
2022-03-24 01:01:25 +00:00
|
|
|
io_close(FileBis);
|
2020-09-24 08:46:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_aBinarydir[0] = 0;
|
|
|
|
#else
|
2020-09-20 22:07:31 +00:00
|
|
|
m_aBinarydir[0] = 0;
|
2020-09-24 08:46:19 +00:00
|
|
|
#endif
|
|
|
|
}
|
2020-09-20 22:07:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-02 16:24:40 +00:00
|
|
|
// no binary directory found, use $PATH on Posix, $PWD on Windows
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2022-05-17 18:33:27 +00:00
|
|
|
void ListDirectoryInfo(int Type, const char *pPath, FS_LISTDIR_CALLBACK_FILEINFO pfnCallback, void *pUser) override
|
2015-08-27 12:57:56 +00:00
|
|
|
{
|
2021-09-13 08:06:34 +00:00
|
|
|
char aBuffer[IO_MAX_PATH_LENGTH];
|
2015-08-27 12:57:56 +00:00
|
|
|
if(Type == TYPE_ALL)
|
|
|
|
{
|
|
|
|
// list all available directories
|
|
|
|
for(int i = 0; i < m_NumPaths; ++i)
|
2021-08-23 10:49:15 +00:00
|
|
|
fs_listdir_fileinfo(GetPath(i, pPath, aBuffer, sizeof(aBuffer)), pfnCallback, i, pUser);
|
2015-08-27 12:57:56 +00:00
|
|
|
}
|
|
|
|
else if(Type >= 0 && Type < m_NumPaths)
|
|
|
|
{
|
|
|
|
// list wanted directory
|
2021-08-23 10:49:15 +00:00
|
|
|
fs_listdir_fileinfo(GetPath(Type, pPath, aBuffer, sizeof(aBuffer)), pfnCallback, Type, pUser);
|
2015-08-27 12:57:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-17 18:33:27 +00:00
|
|
|
void ListDirectory(int Type, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser) override
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2021-09-13 08:06:34 +00:00
|
|
|
char aBuffer[IO_MAX_PATH_LENGTH];
|
2010-10-06 21:07:35 +00:00
|
|
|
if(Type == TYPE_ALL)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
// list all available directories
|
|
|
|
for(int i = 0; i < m_NumPaths; ++i)
|
|
|
|
fs_listdir(GetPath(i, pPath, aBuffer, sizeof(aBuffer)), pfnCallback, i, pUser);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2010-10-06 21:07:35 +00:00
|
|
|
else if(Type >= 0 && Type < m_NumPaths)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
// list wanted directory
|
|
|
|
fs_listdir(GetPath(Type, pPath, aBuffer, sizeof(aBuffer)), pfnCallback, Type, pUser);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
2010-09-24 11:38:03 +00:00
|
|
|
|
2015-03-14 19:01:18 +00:00
|
|
|
virtual const char *GetPath(int Type, const char *pDir, char *pBuffer, unsigned BufferSize)
|
2010-09-24 11:38:03 +00:00
|
|
|
{
|
2020-09-21 22:51:10 +00:00
|
|
|
if(Type == TYPE_ABSOLUTE)
|
|
|
|
{
|
|
|
|
str_copy(pBuffer, pDir, BufferSize);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
str_format(pBuffer, BufferSize, "%s%s%s", m_aaStoragePaths[Type], !m_aaStoragePaths[Type][0] ? "" : "/", pDir);
|
|
|
|
}
|
2010-10-06 21:07:35 +00:00
|
|
|
return pBuffer;
|
2010-09-24 11:38:03 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2022-05-17 18:33:27 +00:00
|
|
|
IOHANDLE OpenFile(const char *pFilename, int Flags, int Type, char *pBuffer = 0, int BufferSize = 0) override
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2021-09-13 08:06:34 +00:00
|
|
|
char aBuffer[IO_MAX_PATH_LENGTH];
|
2010-05-29 07:25:38 +00:00
|
|
|
if(!pBuffer)
|
|
|
|
{
|
|
|
|
pBuffer = aBuffer;
|
|
|
|
BufferSize = sizeof(aBuffer);
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2017-07-12 18:15:00 +00:00
|
|
|
if(Type == TYPE_ABSOLUTE)
|
|
|
|
{
|
|
|
|
return io_open(pFilename, Flags);
|
|
|
|
}
|
2018-07-25 08:29:05 +00:00
|
|
|
if(str_startswith(pFilename, "mapres/../skins/"))
|
|
|
|
{
|
2017-07-21 19:43:56 +00:00
|
|
|
pFilename = pFilename + 10; // just start from skins/
|
|
|
|
}
|
|
|
|
if(pFilename[0] == '/' || pFilename[0] == '\\' || str_find(pFilename, "../") != NULL || str_find(pFilename, "..\\") != NULL
|
2020-09-26 19:41:58 +00:00
|
|
|
#ifdef CONF_FAMILY_WINDOWS
|
2017-06-18 14:26:29 +00:00
|
|
|
|| (pFilename[0] && pFilename[1] == ':')
|
2020-09-26 19:41:58 +00:00
|
|
|
#endif
|
2017-06-18 14:26:29 +00:00
|
|
|
)
|
2017-06-16 18:22:34 +00:00
|
|
|
{
|
|
|
|
// don't escape base directory
|
|
|
|
}
|
2020-09-26 19:41:58 +00:00
|
|
|
else if(Flags & IOFLAG_WRITE)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
return io_open(GetPath(TYPE_SAVE, pFilename, pBuffer, BufferSize), Flags);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
IOHANDLE Handle = 0;
|
2010-10-06 21:07:35 +00:00
|
|
|
|
2017-07-12 18:15:00 +00:00
|
|
|
if(Type <= TYPE_ALL)
|
2010-10-06 21:07:35 +00:00
|
|
|
{
|
|
|
|
// check all available directories
|
|
|
|
for(int i = 0; i < m_NumPaths; ++i)
|
|
|
|
{
|
|
|
|
Handle = io_open(GetPath(i, pFilename, pBuffer, BufferSize), Flags);
|
|
|
|
if(Handle)
|
|
|
|
return Handle;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(Type >= 0 && Type < m_NumPaths)
|
|
|
|
{
|
|
|
|
// check wanted directory
|
|
|
|
Handle = io_open(GetPath(Type, pFilename, pBuffer, BufferSize), Flags);
|
|
|
|
if(Handle)
|
|
|
|
return Handle;
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
pBuffer[0] = 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
return 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2022-06-14 18:15:05 +00:00
|
|
|
bool ReadFile(const char *pFilename, int Type, void **ppResult, unsigned *pResultLen) override
|
|
|
|
{
|
|
|
|
IOHANDLE File = OpenFile(pFilename, IOFLAG_READ, Type);
|
|
|
|
if(!File)
|
|
|
|
{
|
|
|
|
*ppResult = nullptr;
|
|
|
|
*pResultLen = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
io_read_all(File, ppResult, pResultLen);
|
|
|
|
io_close(File);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *ReadFileStr(const char *pFilename, int Type) override
|
|
|
|
{
|
|
|
|
IOHANDLE File = OpenFile(pFilename, IOFLAG_READ | IOFLAG_SKIP_BOM, Type);
|
|
|
|
if(!File)
|
|
|
|
return nullptr;
|
|
|
|
char *pResult = io_read_all_str(File);
|
|
|
|
io_close(File);
|
|
|
|
return pResult;
|
|
|
|
}
|
|
|
|
|
2011-02-21 10:23:30 +00:00
|
|
|
struct CFindCBData
|
|
|
|
{
|
2020-10-27 17:57:14 +00:00
|
|
|
CStorage *m_pStorage;
|
|
|
|
const char *m_pFilename;
|
|
|
|
const char *m_pPath;
|
|
|
|
char *m_pBuffer;
|
|
|
|
int m_BufferSize;
|
2011-02-21 10:23:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int FindFileCallback(const char *pName, int IsDir, int Type, void *pUser)
|
|
|
|
{
|
|
|
|
CFindCBData Data = *static_cast<CFindCBData *>(pUser);
|
|
|
|
if(IsDir)
|
|
|
|
{
|
|
|
|
if(pName[0] == '.')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// search within the folder
|
2021-09-13 08:06:34 +00:00
|
|
|
char aBuf[IO_MAX_PATH_LENGTH];
|
|
|
|
char aPath[IO_MAX_PATH_LENGTH];
|
2020-10-27 17:57:14 +00:00
|
|
|
str_format(aPath, sizeof(aPath), "%s/%s", Data.m_pPath, pName);
|
|
|
|
Data.m_pPath = aPath;
|
|
|
|
fs_listdir(Data.m_pStorage->GetPath(Type, aPath, aBuf, sizeof(aBuf)), FindFileCallback, Type, &Data);
|
|
|
|
if(Data.m_pBuffer[0])
|
2011-04-07 16:45:24 +00:00
|
|
|
return 1;
|
2011-02-21 10:23:30 +00:00
|
|
|
}
|
2020-10-27 17:57:14 +00:00
|
|
|
else if(!str_comp(pName, Data.m_pFilename))
|
2011-02-21 10:23:30 +00:00
|
|
|
{
|
|
|
|
// found the file = end
|
2020-10-27 17:57:14 +00:00
|
|
|
str_format(Data.m_pBuffer, Data.m_BufferSize, "%s/%s", Data.m_pPath, Data.m_pFilename);
|
2011-02-21 10:23:30 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-05-17 18:33:27 +00:00
|
|
|
bool FindFile(const char *pFilename, const char *pPath, int Type, char *pBuffer, int BufferSize) override
|
2011-02-21 10:23:30 +00:00
|
|
|
{
|
|
|
|
if(BufferSize < 1)
|
|
|
|
return false;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-02-21 10:23:30 +00:00
|
|
|
pBuffer[0] = 0;
|
2021-09-13 08:06:34 +00:00
|
|
|
char aBuf[IO_MAX_PATH_LENGTH];
|
2011-02-21 10:23:30 +00:00
|
|
|
CFindCBData Data;
|
2020-10-27 17:57:14 +00:00
|
|
|
Data.m_pStorage = this;
|
|
|
|
Data.m_pFilename = pFilename;
|
|
|
|
Data.m_pPath = pPath;
|
|
|
|
Data.m_pBuffer = pBuffer;
|
|
|
|
Data.m_BufferSize = BufferSize;
|
2011-02-21 10:23:30 +00:00
|
|
|
|
|
|
|
if(Type == TYPE_ALL)
|
|
|
|
{
|
|
|
|
// search within all available directories
|
|
|
|
for(int i = 0; i < m_NumPaths; ++i)
|
2011-04-07 16:45:24 +00:00
|
|
|
{
|
2011-02-21 10:23:30 +00:00
|
|
|
fs_listdir(GetPath(i, pPath, aBuf, sizeof(aBuf)), FindFileCallback, i, &Data);
|
2011-04-07 16:45:24 +00:00
|
|
|
if(pBuffer[0])
|
|
|
|
return true;
|
|
|
|
}
|
2011-02-21 10:23:30 +00:00
|
|
|
}
|
|
|
|
else if(Type >= 0 && Type < m_NumPaths)
|
|
|
|
{
|
|
|
|
// search within wanted directory
|
|
|
|
fs_listdir(GetPath(Type, pPath, aBuf, sizeof(aBuf)), FindFileCallback, Type, &Data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return pBuffer[0] != 0;
|
|
|
|
}
|
|
|
|
|
2022-05-17 18:33:27 +00:00
|
|
|
bool RemoveFile(const char *pFilename, int Type) override
|
2010-09-03 18:05:22 +00:00
|
|
|
{
|
2020-09-21 22:51:10 +00:00
|
|
|
if(Type < TYPE_ABSOLUTE || Type == TYPE_ALL || Type >= m_NumPaths)
|
2010-10-06 21:07:35 +00:00
|
|
|
return false;
|
|
|
|
|
2021-09-13 08:06:34 +00:00
|
|
|
char aBuffer[IO_MAX_PATH_LENGTH];
|
2018-08-23 07:58:35 +00:00
|
|
|
GetPath(Type, pFilename, aBuffer, sizeof(aBuffer));
|
|
|
|
|
2020-10-27 17:57:14 +00:00
|
|
|
bool Success = !fs_remove(aBuffer);
|
|
|
|
if(!Success)
|
2018-08-23 07:58:35 +00:00
|
|
|
dbg_msg("storage", "failed to remove: %s", aBuffer);
|
2020-10-27 17:57:14 +00:00
|
|
|
return Success;
|
2010-09-03 18:05:22 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2022-05-17 18:33:27 +00:00
|
|
|
bool RemoveBinaryFile(const char *pFilename) override
|
2015-03-14 19:01:18 +00:00
|
|
|
{
|
2021-09-13 08:06:34 +00:00
|
|
|
char aBuffer[IO_MAX_PATH_LENGTH];
|
2018-08-23 07:58:35 +00:00
|
|
|
GetBinaryPath(pFilename, aBuffer, sizeof(aBuffer));
|
|
|
|
|
2020-10-27 17:57:14 +00:00
|
|
|
bool Success = !fs_remove(aBuffer);
|
|
|
|
if(!Success)
|
2020-11-12 13:53:38 +00:00
|
|
|
dbg_msg("storage", "failed to remove binary: %s", aBuffer);
|
2020-10-27 17:57:14 +00:00
|
|
|
return Success;
|
2015-03-14 19:01:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-17 18:33:27 +00:00
|
|
|
bool RenameFile(const char *pOldFilename, const char *pNewFilename, int Type) override
|
2010-12-07 23:09:18 +00:00
|
|
|
{
|
|
|
|
if(Type < 0 || Type >= m_NumPaths)
|
|
|
|
return false;
|
2018-08-23 07:58:35 +00:00
|
|
|
|
2021-09-13 08:06:34 +00:00
|
|
|
char aOldBuffer[IO_MAX_PATH_LENGTH];
|
|
|
|
char aNewBuffer[IO_MAX_PATH_LENGTH];
|
2018-08-23 07:58:35 +00:00
|
|
|
GetPath(Type, pOldFilename, aOldBuffer, sizeof(aOldBuffer));
|
|
|
|
GetPath(Type, pNewFilename, aNewBuffer, sizeof(aNewBuffer));
|
|
|
|
|
2020-10-27 17:57:14 +00:00
|
|
|
bool Success = !fs_rename(aOldBuffer, aNewBuffer);
|
|
|
|
if(!Success)
|
2018-08-23 07:58:35 +00:00
|
|
|
dbg_msg("storage", "failed to rename: %s -> %s", aOldBuffer, aNewBuffer);
|
2020-10-27 17:57:14 +00:00
|
|
|
return Success;
|
2010-12-07 23:09:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-17 18:33:27 +00:00
|
|
|
bool RenameBinaryFile(const char *pOldFilename, const char *pNewFilename) override
|
2015-03-14 19:01:18 +00:00
|
|
|
{
|
2021-09-13 08:06:34 +00:00
|
|
|
char aOldBuffer[IO_MAX_PATH_LENGTH];
|
|
|
|
char aNewBuffer[IO_MAX_PATH_LENGTH];
|
2016-05-01 12:43:08 +00:00
|
|
|
GetBinaryPath(pOldFilename, aOldBuffer, sizeof(aOldBuffer));
|
|
|
|
GetBinaryPath(pNewFilename, aNewBuffer, sizeof(aNewBuffer));
|
|
|
|
|
|
|
|
if(fs_makedir_rec_for(aNewBuffer) < 0)
|
2016-05-02 19:35:32 +00:00
|
|
|
dbg_msg("storage", "cannot create folder for: %s", aNewBuffer);
|
2016-05-01 12:43:08 +00:00
|
|
|
|
2020-10-27 17:57:14 +00:00
|
|
|
bool Success = !fs_rename(aOldBuffer, aNewBuffer);
|
|
|
|
if(!Success)
|
2018-08-23 07:58:35 +00:00
|
|
|
dbg_msg("storage", "failed to rename: %s -> %s", aOldBuffer, aNewBuffer);
|
2020-10-27 17:57:14 +00:00
|
|
|
return Success;
|
2015-03-14 19:01:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-17 18:33:27 +00:00
|
|
|
bool CreateFolder(const char *pFoldername, int Type) override
|
2010-10-16 16:33:54 +00:00
|
|
|
{
|
|
|
|
if(Type < 0 || Type >= m_NumPaths)
|
|
|
|
return false;
|
|
|
|
|
2021-09-13 08:06:34 +00:00
|
|
|
char aBuffer[IO_MAX_PATH_LENGTH];
|
2018-08-23 07:58:35 +00:00
|
|
|
GetPath(Type, pFoldername, aBuffer, sizeof(aBuffer));
|
|
|
|
|
2020-10-27 17:57:14 +00:00
|
|
|
bool Success = !fs_makedir(aBuffer);
|
|
|
|
if(!Success)
|
2018-08-23 07:58:35 +00:00
|
|
|
dbg_msg("storage", "failed to create folder: %s", aBuffer);
|
2020-10-27 17:57:14 +00:00
|
|
|
return Success;
|
2010-10-16 16:33:54 +00:00
|
|
|
}
|
|
|
|
|
2022-05-17 18:33:27 +00:00
|
|
|
void GetCompletePath(int Type, const char *pDir, char *pBuffer, unsigned BufferSize) override
|
2012-01-08 12:14:02 +00:00
|
|
|
{
|
|
|
|
if(Type < 0 || Type >= m_NumPaths)
|
|
|
|
{
|
|
|
|
if(BufferSize > 0)
|
|
|
|
pBuffer[0] = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
GetPath(Type, pDir, pBuffer, BufferSize);
|
|
|
|
}
|
|
|
|
|
2022-05-17 18:33:27 +00:00
|
|
|
const char *GetBinaryPath(const char *pFilename, char *pBuffer, unsigned BufferSize) override
|
2015-03-14 19:01:18 +00:00
|
|
|
{
|
2020-09-05 22:38:35 +00:00
|
|
|
str_format(pBuffer, BufferSize, "%s%s%s", m_aBinarydir, !m_aBinarydir[0] ? "" : "/", pFilename);
|
2015-03-14 19:01:18 +00:00
|
|
|
return pBuffer;
|
|
|
|
}
|
|
|
|
|
2021-12-21 16:19:42 +00:00
|
|
|
static IStorage *Create(int StorageType, int NumArgs, const char **ppArguments)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
CStorage *p = new CStorage();
|
2021-12-21 16:19:42 +00:00
|
|
|
if(p && p->Init(StorageType, NumArgs, ppArguments))
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-10-06 21:07:35 +00:00
|
|
|
dbg_msg("storage", "initialisation failed");
|
2010-05-29 07:25:38 +00:00
|
|
|
delete p;
|
|
|
|
p = 0;
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-08-30 11:59:42 +00:00
|
|
|
void IStorage::StripPathAndExtension(const char *pFilename, char *pBuffer, int BufferSize)
|
|
|
|
{
|
|
|
|
const char *pFilenameEnd = pFilename + str_length(pFilename);
|
|
|
|
const char *pExtractedName = pFilename;
|
|
|
|
const char *pEnd = pFilenameEnd;
|
|
|
|
for(const char *pIter = pFilename; *pIter; pIter++)
|
|
|
|
{
|
|
|
|
if(*pIter == '/' || *pIter == '\\')
|
|
|
|
{
|
|
|
|
pExtractedName = pIter + 1;
|
|
|
|
pEnd = pFilenameEnd;
|
|
|
|
}
|
|
|
|
else if(*pIter == '.')
|
|
|
|
{
|
|
|
|
pEnd = pIter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-26 19:36:49 +00:00
|
|
|
int Length = minimum(BufferSize, (int)(pEnd - pExtractedName + 1));
|
2017-08-30 11:59:42 +00:00
|
|
|
str_copy(pBuffer, pExtractedName, Length);
|
|
|
|
}
|
|
|
|
|
2021-12-20 14:00:21 +00:00
|
|
|
const char *IStorage::FormatTmpPath(char *aBuf, unsigned BufSize, const char *pPath)
|
|
|
|
{
|
|
|
|
str_format(aBuf, BufSize, "%s.%d.tmp", pPath, pid());
|
|
|
|
return aBuf;
|
|
|
|
}
|
|
|
|
|
2021-12-21 16:19:42 +00:00
|
|
|
IStorage *CreateStorage(int StorageType, int NumArgs, const char **ppArguments)
|
2021-04-21 11:13:29 +00:00
|
|
|
{
|
2021-12-21 16:19:42 +00:00
|
|
|
return CStorage::Create(StorageType, NumArgs, ppArguments);
|
2021-04-21 11:13:29 +00:00
|
|
|
}
|
2015-08-21 21:02:50 +00:00
|
|
|
|
|
|
|
IStorage *CreateLocalStorage()
|
|
|
|
{
|
|
|
|
CStorage *pStorage = new CStorage();
|
|
|
|
if(pStorage)
|
|
|
|
{
|
2015-08-26 11:18:44 +00:00
|
|
|
if(!fs_getcwd(pStorage->m_aCurrentdir, sizeof(pStorage->m_aCurrentdir)))
|
|
|
|
{
|
|
|
|
delete pStorage;
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-08-21 21:02:50 +00:00
|
|
|
pStorage->AddPath("$CURRENTDIR");
|
|
|
|
}
|
|
|
|
return pStorage;
|
|
|
|
}
|
2021-04-21 11:13:29 +00:00
|
|
|
IStorage *CreateTempStorage(const char *pDirectory)
|
|
|
|
{
|
|
|
|
CStorage *pStorage = new CStorage();
|
|
|
|
if(!pStorage)
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
pStorage->AddPath(pDirectory);
|
|
|
|
return pStorage;
|
|
|
|
}
|