ddnet/src/engine/shared/storage.cpp

863 lines
22 KiB
C++
Raw Normal View History

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. */
#include <base/math.h>
2010-05-29 07:25:38 +00:00
#include <base/system.h>
#include <engine/client/updater.h>
#include <engine/shared/linereader.h>
2010-05-29 07:25:38 +00:00
#include <engine/storage.h>
#include <unordered_set>
#ifdef CONF_PLATFORM_HAIKU
#include <cstdlib>
#endif
2010-05-29 07:25:38 +00:00
class CStorage : public IStorage
{
public:
char m_aaStoragePaths[MAX_PATHS][IO_MAX_PATH_LENGTH];
2010-10-06 21:07:35 +00:00
int m_NumPaths;
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];
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
}
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
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);
}
2010-08-05 18:26:03 +00:00
2010-10-06 21:07:35 +00:00
// get datadir
FindDatadir(ppArguments[0]);
// get binarydir
FindBinarydir(ppArguments[0]);
// 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
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
{
if(StorageType == STORAGETYPE_CLIENT)
{
CreateFolder("screenshots", TYPE_SAVE);
CreateFolder("screenshots/auto", TYPE_SAVE);
CreateFolder("screenshots/auto/stats", TYPE_SAVE);
CreateFolder("maps", TYPE_SAVE);
Autosave copy of current editor map periodically to `auto` folder A copy of the map currently open in the editor is saved every 10 minutes to the `maps/auto` folder (interval configurable, see below). The automatically saved map uses the filename of the original map with an additional timestamp. Per map name 10 autosaves are kept in the `auto` folder before old autosaves will be deleted (number configurable, see below). Add config variable `ed_autosave_interval` (0 - 240, default 10) to configure the interval in minutes at which a copy of the current editor map is automatically saved to the 'auto' folder. Add config variable `ed_autosave_max` (0 - 1000, default 10) to configure the maximum number of autosaves that are kept per map name (0 = no limit). Autosaving will not take place in the 5 seconds immediately after the map was last modified by the user, to avoid interrupting the user with the autosave. This will only delay autosaving for up to 1 minute though, so autosaves are not prevented entirely, should the user continuously edit the map. When the editor is reopened after being closed for more than 10 seconds, the autosave timer will be adjusted to compensate for the time that was not spent on editing in the editor. When the map is saved manually by the user the autosave file is also updated, if it's outdated by at least half of the configured autosave interval. This ensures that autosaves are always available as a periodic backup of the map. When a copy of the current map is saved, this does not update the autosave and will also no longer reset the modified state. The modified state should reflect whether changes have been made that are not saved to the current map file. As saving a copy does not update the current file, the modified state should not be reset in this case. Closes #6693.
2023-06-23 15:39:05 +00:00
CreateFolder("maps/auto", TYPE_SAVE);
CreateFolder("mapres", TYPE_SAVE);
CreateFolder("downloadedmaps", TYPE_SAVE);
CreateFolder("skins", TYPE_SAVE);
CreateFolder("downloadedskins", TYPE_SAVE);
CreateFolder("themes", TYPE_SAVE);
CreateFolder("assets", TYPE_SAVE);
CreateFolder("assets/emoticons", TYPE_SAVE);
CreateFolder("assets/entities", TYPE_SAVE);
CreateFolder("assets/game", TYPE_SAVE);
CreateFolder("assets/particles", TYPE_SAVE);
CreateFolder("assets/hud", TYPE_SAVE);
CreateFolder("assets/extras", TYPE_SAVE);
#if defined(CONF_VIDEORECORDER)
CreateFolder("videos", TYPE_SAVE);
#endif
}
CreateFolder("dumps", TYPE_SAVE);
CreateFolder("demos", TYPE_SAVE);
CreateFolder("demos/auto", TYPE_SAVE);
CreateFolder("demos/auto/race", TYPE_SAVE);
CreateFolder("demos/replays", TYPE_SAVE);
CreateFolder("editor", TYPE_SAVE);
CreateFolder("ghosts", TYPE_SAVE);
CreateFolder("teehistorian", TYPE_SAVE);
2010-10-06 21:07:35 +00:00
}
return m_NumPaths ? 0 : 1;
}
void LoadPaths(const char *pArgv0)
{
// check current directory
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;
if(Pos < IO_MAX_PATH_LENGTH)
2010-10-06 21:07:35 +00:00
{
char aBuffer[IO_MAX_PATH_LENGTH];
str_copy(aBuffer, pArgv0, Pos + 1);
str_append(aBuffer, "/storage.cfg");
File = io_open(aBuffer, IOFLAG_READ | IOFLAG_SKIP_BOM);
2010-10-06 21:07:35 +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;
}
}
CLineReader LineReader;
LineReader.Init(File);
2022-09-24 09:59:10 +00:00
char *pLine;
2010-10-06 21:07:35 +00:00
while((pLine = LineReader.Get()))
{
const char *pLineWithoutPrefix = str_startswith(pLine, "add_path ");
if(pLineWithoutPrefix)
{
AddPath(pLineWithoutPrefix);
}
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(!pPath[0])
{
dbg_msg("storage", "cannot add empty path");
return;
}
if(m_NumPaths >= MAX_PATHS)
{
dbg_msg("storage", "cannot add path '%s', the maximum number of paths is %d", pPath, MAX_PATHS);
2010-10-06 21:07:35 +00:00
return;
}
2010-10-06 21:07:35 +00:00
if(!str_comp(pPath, "$USERDIR"))
{
if(m_aUserdir[0])
{
2022-07-09 16:14:56 +00:00
str_copy(m_aaStoragePaths[m_NumPaths++], m_aUserdir);
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])
{
2022-07-09 16:14:56 +00:00
str_copy(m_aaStoragePaths[m_NumPaths++], m_aDatadir);
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;
dbg_msg("storage", "added path '$CURRENTDIR' ('%s')", m_aCurrentdir);
2010-10-06 21:07:35 +00:00
}
else
{
if(fs_is_dir(pPath))
{
2022-07-09 16:14:56 +00:00
str_copy(m_aaStoragePaths[m_NumPaths++], pPath);
dbg_msg("storage", "added path '%s'", pPath);
}
else
{
dbg_msg("storage", "cannot add path '%s', which is not a directory", pPath);
}
2010-10-06 21:07:35 +00:00
}
2010-05-29 07:25:38 +00:00
}
2010-10-06 21:07:35 +00:00
void FindDatadir(const char *pArgv0)
2010-05-29 07:25:38 +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
}
#if defined(DATA_DIR)
// 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
}
#endif
// 3) check for usable path in argv[0]
2010-05-29 07:25:38 +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;
if(Pos < IO_MAX_PATH_LENGTH)
2010-05-29 07:25:38 +00:00
{
char aBuf[IO_MAX_PATH_LENGTH];
char aDir[IO_MAX_PATH_LENGTH];
str_copy(aDir, pArgv0, Pos + 1);
str_format(aBuf, sizeof(aBuf), "%s/data/mapres", aDir);
if(fs_is_dir(aBuf))
{
str_format(m_aDatadir, sizeof(m_aDatadir), "%s/data", aDir);
2010-10-06 21:07:35 +00:00
return;
}
2010-05-29 07:25:38 +00:00
}
}
#ifdef CONF_PLATFORM_HAIKU
free((void *)pArgv0);
#endif
#if defined(CONF_FAMILY_UNIX)
// 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[] = {
"/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",
"/opt/ddnet"};
2022-09-24 10:50:17 +00:00
for(const char *pDir : apDirs)
2010-05-29 07:25:38 +00:00
{
2010-12-11 21:48:37 +00:00
char aBuf[128];
2022-09-24 10:50:17 +00:00
str_format(aBuf, sizeof(aBuf), "%s/data/mapres", pDir);
2010-12-11 21:48:37 +00:00
if(fs_is_dir(aBuf))
2010-05-29 07:25:38 +00:00
{
2022-09-24 10:50:17 +00:00
str_format(m_aDatadir, sizeof(m_aDatadir), "%s/data", pDir);
2010-10-06 21:07:35 +00:00
return;
2010-05-29 07:25:38 +00:00
}
}
}
#endif
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;
if(Pos < IO_MAX_PATH_LENGTH)
{
char aBuf[IO_MAX_PATH_LENGTH];
str_copy(m_aBinarydir, pArgv0, Pos + 1);
str_format(aBuf, sizeof(aBuf), "%s/" PLAT_SERVER_EXEC, m_aBinarydir);
if(fs_is_file(aBuf))
{
return;
}
#if defined(CONF_PLATFORM_MACOS)
str_append(m_aBinarydir, "/../../../DDNet-Server.app/Contents/MacOS");
str_format(aBuf, sizeof(aBuf), "%s/" PLAT_SERVER_EXEC, m_aBinarydir);
if(fs_is_file(aBuf))
{
return;
}
#endif
}
}
// no binary directory found, use $PATH on Posix, $PWD on Windows
m_aBinarydir[0] = '\0';
2010-05-29 07:25:38 +00:00
}
int NumPaths() const override
{
return m_NumPaths;
}
struct SListDirectoryInfoUniqueCallbackData
{
FS_LISTDIR_CALLBACK_FILEINFO m_pfnDelegate;
void *m_pDelegateUser;
std::unordered_set<std::string> m_Seen;
};
static int ListDirectoryInfoUniqueCallback(const CFsFileInfo *pInfo, int IsDir, int Type, void *pUser)
{
SListDirectoryInfoUniqueCallbackData *pData = static_cast<SListDirectoryInfoUniqueCallbackData *>(pUser);
auto [_, InsertionTookPlace] = pData->m_Seen.emplace(pInfo->m_pName);
if(InsertionTookPlace)
return pData->m_pfnDelegate(pInfo, IsDir, Type, pData->m_pDelegateUser);
return 0;
}
void ListDirectoryInfo(int Type, const char *pPath, FS_LISTDIR_CALLBACK_FILEINFO pfnCallback, void *pUser) override
2015-08-27 12:57:56 +00:00
{
char aBuffer[IO_MAX_PATH_LENGTH];
2015-08-27 12:57:56 +00:00
if(Type == TYPE_ALL)
{
SListDirectoryInfoUniqueCallbackData Data;
Data.m_pfnDelegate = pfnCallback;
Data.m_pDelegateUser = pUser;
2015-08-27 12:57:56 +00:00
// list all available directories
2022-09-24 11:53:08 +00:00
for(int i = TYPE_SAVE; i < m_NumPaths; ++i)
fs_listdir_fileinfo(GetPath(i, pPath, aBuffer, sizeof(aBuffer)), ListDirectoryInfoUniqueCallback, i, &Data);
2015-08-27 12:57:56 +00:00
}
2022-09-24 11:53:08 +00:00
else if(Type >= TYPE_SAVE && Type < m_NumPaths)
2015-08-27 12:57:56 +00:00
{
// list wanted directory
fs_listdir_fileinfo(GetPath(Type, pPath, aBuffer, sizeof(aBuffer)), pfnCallback, Type, pUser);
2015-08-27 12:57:56 +00:00
}
else
{
dbg_assert(false, "Type invalid");
}
2015-08-27 12:57:56 +00:00
}
struct SListDirectoryUniqueCallbackData
{
FS_LISTDIR_CALLBACK m_pfnDelegate;
void *m_pDelegateUser;
std::unordered_set<std::string> m_Seen;
};
static int ListDirectoryUniqueCallback(const char *pName, int IsDir, int Type, void *pUser)
{
SListDirectoryUniqueCallbackData *pData = static_cast<SListDirectoryUniqueCallbackData *>(pUser);
auto [_, InsertionTookPlace] = pData->m_Seen.emplace(pName);
if(InsertionTookPlace)
return pData->m_pfnDelegate(pName, IsDir, Type, pData->m_pDelegateUser);
return 0;
}
void ListDirectory(int Type, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser) override
2010-05-29 07:25:38 +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
{
SListDirectoryUniqueCallbackData Data;
Data.m_pfnDelegate = pfnCallback;
Data.m_pDelegateUser = pUser;
2010-10-06 21:07:35 +00:00
// list all available directories
2022-09-24 11:53:08 +00:00
for(int i = TYPE_SAVE; i < m_NumPaths; ++i)
fs_listdir(GetPath(i, pPath, aBuffer, sizeof(aBuffer)), ListDirectoryUniqueCallback, i, &Data);
2010-05-29 07:25:38 +00:00
}
2022-09-24 11:53:08 +00:00
else if(Type >= TYPE_SAVE && 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
}
else
{
dbg_assert(false, "Type invalid");
}
2010-05-29 07:25:38 +00:00
}
const char *GetPath(int Type, const char *pDir, char *pBuffer, unsigned BufferSize)
{
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;
}
void TranslateType(int &Type, const char *pPath)
{
if(Type == TYPE_SAVE_OR_ABSOLUTE)
Type = fs_is_relative_path(pPath) ? TYPE_SAVE : TYPE_ABSOLUTE;
else if(Type == TYPE_ALL_OR_ABSOLUTE)
Type = fs_is_relative_path(pPath) ? TYPE_ALL : TYPE_ABSOLUTE;
}
IOHANDLE OpenFile(const char *pFilename, int Flags, int Type, char *pBuffer = 0, int BufferSize = 0) override
2010-05-29 07:25:38 +00:00
{
TranslateType(Type, pFilename);
char aBuffer[IO_MAX_PATH_LENGTH];
2010-05-29 07:25:38 +00:00
if(!pBuffer)
{
pBuffer = aBuffer;
BufferSize = sizeof(aBuffer);
}
if(Type == TYPE_ABSOLUTE)
{
return io_open(GetPath(TYPE_ABSOLUTE, pFilename, pBuffer, BufferSize), Flags);
}
if(str_startswith(pFilename, "mapres/../skins/"))
{
pFilename = pFilename + 10; // just start from skins/
}
if(pFilename[0] == '/' || pFilename[0] == '\\' || str_find(pFilename, "../") != NULL || str_find(pFilename, "..\\") != NULL
#ifdef CONF_FAMILY_WINDOWS
|| (pFilename[0] && pFilename[1] == ':')
#endif
)
2017-06-16 18:22:34 +00:00
{
// don't escape base directory
}
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
{
if(Type == TYPE_ALL)
2010-10-06 21:07:35 +00:00
{
// check all available directories
2022-09-24 11:53:08 +00:00
for(int i = TYPE_SAVE; i < m_NumPaths; ++i)
2010-10-06 21:07:35 +00:00
{
2022-09-24 09:59:10 +00:00
IOHANDLE Handle = io_open(GetPath(i, pFilename, pBuffer, BufferSize), Flags);
2010-10-06 21:07:35 +00:00
if(Handle)
return Handle;
}
}
2022-09-24 11:53:08 +00:00
else if(Type >= TYPE_SAVE && Type < m_NumPaths)
2010-10-06 21:07:35 +00:00
{
// check wanted directory
2022-09-24 09:59:10 +00:00
IOHANDLE Handle = io_open(GetPath(Type, pFilename, pBuffer, BufferSize), Flags);
2010-10-06 21:07:35 +00:00
if(Handle)
return Handle;
}
else
{
dbg_assert(false, "Type invalid");
}
2010-05-29 07:25:38 +00:00
}
2010-05-29 07:25:38 +00:00
pBuffer[0] = 0;
return 0;
2010-05-29 07:25:38 +00:00
}
template<typename F>
bool GenericExists(const char *pFilename, int Type, F &&CheckFunction)
{
TranslateType(Type, pFilename);
char aBuffer[IO_MAX_PATH_LENGTH];
if(Type == TYPE_ALL)
{
// check all available directories
for(int i = TYPE_SAVE; i < m_NumPaths; ++i)
{
if(CheckFunction(GetPath(i, pFilename, aBuffer, sizeof(aBuffer))))
return true;
}
return false;
}
else if(Type == TYPE_ABSOLUTE || (Type >= TYPE_SAVE && Type < m_NumPaths))
{
// check wanted directory
return CheckFunction(GetPath(Type, pFilename, aBuffer, sizeof(aBuffer)));
}
else
{
dbg_assert(false, "Type invalid");
return false;
}
}
bool FileExists(const char *pFilename, int Type) override
{
return GenericExists(pFilename, Type, fs_is_file);
}
bool FolderExists(const char *pFilename, int Type) override
{
return GenericExists(pFilename, Type, fs_is_dir);
}
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;
}
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;
};
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
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])
return 1;
}
2020-10-27 17:57:14 +00:00
else if(!str_comp(pName, Data.m_pFilename))
{
// 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);
return 1;
}
return 0;
}
bool FindFile(const char *pFilename, const char *pPath, int Type, char *pBuffer, int BufferSize) override
{
dbg_assert(BufferSize >= 1, "BufferSize invalid");
pBuffer[0] = 0;
2022-09-24 09:59:10 +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;
2022-09-24 09:59:10 +00:00
char aBuf[IO_MAX_PATH_LENGTH];
if(Type == TYPE_ALL)
{
// search within all available directories
2022-09-24 11:53:08 +00:00
for(int i = TYPE_SAVE; i < m_NumPaths; ++i)
{
fs_listdir(GetPath(i, pPath, aBuf, sizeof(aBuf)), FindFileCallback, i, &Data);
if(pBuffer[0])
return true;
}
}
2022-09-24 11:53:08 +00:00
else if(Type >= TYPE_SAVE && Type < m_NumPaths)
{
// search within wanted directory
fs_listdir(GetPath(Type, pPath, aBuf, sizeof(aBuf)), FindFileCallback, Type, &Data);
}
else
{
dbg_assert(false, "Type invalid");
}
return pBuffer[0] != 0;
}
struct SFindFilesCallbackData
{
CStorage *m_pStorage;
const char *m_pFilename;
const char *m_pPath;
std::set<std::string> *m_pEntries;
};
static int FindFilesCallback(const char *pName, int IsDir, int Type, void *pUser)
{
SFindFilesCallbackData Data = *static_cast<SFindFilesCallbackData *>(pUser);
if(IsDir)
{
if(pName[0] == '.')
return 0;
// search within the folder
char aBuf[IO_MAX_PATH_LENGTH];
char aPath[IO_MAX_PATH_LENGTH];
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)), FindFilesCallback, Type, &Data);
}
else if(!str_comp(pName, Data.m_pFilename))
{
char aBuffer[IO_MAX_PATH_LENGTH];
str_format(aBuffer, sizeof(aBuffer), "%s/%s", Data.m_pPath, Data.m_pFilename);
Data.m_pEntries->emplace(aBuffer);
}
return 0;
}
size_t FindFiles(const char *pFilename, const char *pPath, int Type, std::set<std::string> *pEntries) override
{
SFindFilesCallbackData Data;
Data.m_pStorage = this;
Data.m_pFilename = pFilename;
Data.m_pPath = pPath;
Data.m_pEntries = pEntries;
char aBuf[IO_MAX_PATH_LENGTH];
if(Type == TYPE_ALL)
{
// search within all available directories
for(int i = TYPE_SAVE; i < m_NumPaths; ++i)
{
fs_listdir(GetPath(i, pPath, aBuf, sizeof(aBuf)), FindFilesCallback, i, &Data);
}
}
else if(Type >= TYPE_SAVE && Type < m_NumPaths)
{
// search within wanted directory
fs_listdir(GetPath(Type, pPath, aBuf, sizeof(aBuf)), FindFilesCallback, Type, &Data);
}
else
{
dbg_assert(false, "Type invalid");
}
return pEntries->size();
}
bool RemoveFile(const char *pFilename, int Type) override
{
dbg_assert(Type == TYPE_ABSOLUTE || (Type >= TYPE_SAVE && Type < m_NumPaths), "Type invalid");
2010-10-06 21:07:35 +00:00
char aBuffer[IO_MAX_PATH_LENGTH];
GetPath(Type, pFilename, aBuffer, sizeof(aBuffer));
2020-10-27 17:57:14 +00:00
bool Success = !fs_remove(aBuffer);
if(!Success)
dbg_msg("storage", "failed to remove: %s", aBuffer);
2020-10-27 17:57:14 +00:00
return Success;
}
2010-05-29 07:25:38 +00:00
bool RemoveFolder(const char *pFilename, int Type) override
{
dbg_assert(Type == TYPE_ABSOLUTE || (Type >= TYPE_SAVE && Type < m_NumPaths), "Type invalid");
char aBuffer[IO_MAX_PATH_LENGTH];
GetPath(Type, pFilename, aBuffer, sizeof(aBuffer));
bool Success = !fs_removedir(aBuffer);
if(!Success)
dbg_msg("storage", "failed to remove: %s", aBuffer);
return Success;
}
bool RemoveBinaryFile(const char *pFilename) override
{
char aBuffer[IO_MAX_PATH_LENGTH];
GetBinaryPath(pFilename, aBuffer, sizeof(aBuffer));
2020-10-27 17:57:14 +00:00
bool Success = !fs_remove(aBuffer);
if(!Success)
dbg_msg("storage", "failed to remove binary: %s", aBuffer);
2020-10-27 17:57:14 +00:00
return Success;
}
bool RenameFile(const char *pOldFilename, const char *pNewFilename, int Type) override
{
dbg_assert(Type >= TYPE_SAVE && Type < m_NumPaths, "Type invalid");
char aOldBuffer[IO_MAX_PATH_LENGTH];
char aNewBuffer[IO_MAX_PATH_LENGTH];
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)
dbg_msg("storage", "failed to rename: %s -> %s", aOldBuffer, aNewBuffer);
2020-10-27 17:57:14 +00:00
return Success;
}
bool RenameBinaryFile(const char *pOldFilename, const char *pNewFilename) override
{
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)
{
dbg_msg("storage", "cannot create folder for: %s", aNewBuffer);
return false;
}
2016-05-01 12:43:08 +00:00
2020-10-27 17:57:14 +00:00
bool Success = !fs_rename(aOldBuffer, aNewBuffer);
if(!Success)
dbg_msg("storage", "failed to rename: %s -> %s", aOldBuffer, aNewBuffer);
2020-10-27 17:57:14 +00:00
return Success;
}
bool CreateFolder(const char *pFoldername, int Type) override
{
dbg_assert(Type >= TYPE_SAVE && Type < m_NumPaths, "Type invalid");
char aBuffer[IO_MAX_PATH_LENGTH];
GetPath(Type, pFoldername, aBuffer, sizeof(aBuffer));
2020-10-27 17:57:14 +00:00
bool Success = !fs_makedir(aBuffer);
if(!Success)
dbg_msg("storage", "failed to create folder: %s", aBuffer);
2020-10-27 17:57:14 +00:00
return Success;
}
void GetCompletePath(int Type, const char *pDir, char *pBuffer, unsigned BufferSize) override
{
TranslateType(Type, pDir);
dbg_assert(Type >= TYPE_SAVE && Type < m_NumPaths, "Type invalid");
GetPath(Type, pDir, pBuffer, BufferSize);
}
const char *GetBinaryPath(const char *pFilename, char *pBuffer, unsigned BufferSize) override
{
str_format(pBuffer, BufferSize, "%s%s%s", m_aBinarydir, !m_aBinarydir[0] ? "" : "/", pFilename);
return pBuffer;
}
const char *GetBinaryPathAbsolute(const char *pFilename, char *pBuffer, unsigned BufferSize) override
{
char aBinaryPath[IO_MAX_PATH_LENGTH];
GetBinaryPath(PLAT_CLIENT_EXEC, aBinaryPath, sizeof(aBinaryPath));
if(fs_is_relative_path(aBinaryPath))
{
if(fs_getcwd(pBuffer, BufferSize))
{
str_append(pBuffer, "/", BufferSize);
str_append(pBuffer, aBinaryPath, BufferSize);
}
else
pBuffer[0] = '\0';
}
else
str_copy(pBuffer, aBinaryPath, BufferSize);
return pBuffer;
}
static IStorage *Create(int StorageType, int NumArgs, const char **ppArguments)
2010-05-29 07:25:38 +00:00
{
2022-09-24 09:51:59 +00:00
CStorage *pStorage = new CStorage();
if(pStorage && pStorage->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");
2022-09-24 09:51:59 +00:00
delete pStorage;
pStorage = nullptr;
2010-05-29 07:25:38 +00:00
}
2022-09-24 09:51:59 +00:00
return pStorage;
2010-05-29 07:25:38 +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));
str_copy(pBuffer, pExtractedName, Length);
}
const char *IStorage::FormatTmpPath(char *aBuf, unsigned BufSize, const char *pPath)
{
str_format(aBuf, BufSize, "%s.%d.tmp", pPath, pid());
return aBuf;
}
IStorage *CreateStorage(int StorageType, int NumArgs, const char **ppArguments)
{
return CStorage::Create(StorageType, NumArgs, ppArguments);
}
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;
}
pStorage->AddPath("$CURRENTDIR");
}
return pStorage;
}
IStorage *CreateTempStorage(const char *pDirectory)
{
CStorage *pStorage = new CStorage();
if(!pStorage)
{
return nullptr;
}
pStorage->AddPath(pDirectory);
return pStorage;
}