/
dieoska
/
ddnet
Обзор
Документация
Войти
/
dieoska
/
ddnet
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/engine/shared/storage.cpp
963 строки
24 KB
heinrich5991
Convert all `dbg_assert(false, …)` to `dbg_assert_failed(…)`
12 ноя 2025, 16:42
12 ноя 2025, 16:42
84c940a
Код
Авторство
О чём код?
/* (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/hash_ctxt.h> #include <base/log.h> #include <base/math.h> #include <base/system.h> #include <engine/client/updater.h> #include <engine/shared/linereader.h> #include <engine/storage.h> #include <unordered_set> #ifdef CONF_PLATFORM_HAIKU #include <cstdlib> #endif #include <zlib.h> class CStorage : public IStorage { char m_aaStoragePaths[MAX_PATHS][IO_MAX_PATH_LENGTH]; int m_NumPaths = 0; char m_aUserdir[IO_MAX_PATH_LENGTH] = ""; char m_aDatadir[IO_MAX_PATH_LENGTH] = ""; char m_aCurrentdir[IO_MAX_PATH_LENGTH] = ""; char m_aBinarydir[IO_MAX_PATH_LENGTH] = ""; public: bool Init(EInitializationType InitializationType, int NumArgs, const char **ppArguments) { dbg_assert(NumArgs > 0, "Expected at least one argument"); const char *pExecutablePath = ppArguments[0]; FindUserDirectory(); FindDataDirectory(pExecutablePath); FindCurrentDirectory(); FindBinaryDirectory(pExecutablePath); if(!LoadPathsFromFile(pExecutablePath)) { return false; } if(!m_NumPaths) { if(!AddDefaultPaths()) { return false; } } if(InitializationType == EInitializationType::BASIC) { return true; } if(m_aaStoragePaths[TYPE_SAVE][0] != '\0') { if(fs_makedir_rec_for(m_aaStoragePaths[TYPE_SAVE]) != 0 || fs_makedir(m_aaStoragePaths[TYPE_SAVE]) != 0) { log_error("storage", "failed to create the user directory"); return false; } } bool Success = true; if(InitializationType == EInitializationType::CLIENT) { static constexpr const char *CLIENT_DIRS[] = { "assets", "assets/emoticons", "assets/entities", "assets/extras", "assets/game", "assets/hud", "assets/particles", "audio", "communityicons", "downloadedmaps", "downloadedskins", "mapres", "maps", "maps/auto", "screenshots", "screenshots/auto", "screenshots/auto/stats", "skins", "skins7", "themes", #if defined(CONF_VIDEORECORDER) "videos" #endif }; for(const char *pDir : CLIENT_DIRS) Success &= CreateFolder(pDir, TYPE_SAVE); } static constexpr const char *COMMON_DIRS[] = { "dumps", "demos", "demos/auto", "demos/auto/race", "demos/auto/server", "demos/replays", "editor", "ghosts", "teehistorian"}; for(const char *pDir : COMMON_DIRS) Success &= CreateFolder(pDir, TYPE_SAVE); if(!Success) { log_error("storage", "failed to create default folders in the user directory"); } return Success; } bool LoadPathsFromFile(const char *pArgv0) { // check current directory IOHANDLE File = io_open("storage.cfg", IOFLAG_READ); 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) { char aBuffer[IO_MAX_PATH_LENGTH]; str_copy(aBuffer, pArgv0, Pos + 1); str_append(aBuffer, "/storage.cfg"); File = io_open(aBuffer, IOFLAG_READ); } } CLineReader LineReader; if(!LineReader.OpenFile(File)) { log_error("storage", "couldn't open storage.cfg"); return true; } while(const char *pLine = LineReader.Get()) { const char *pLineWithoutPrefix = str_startswith(pLine, "add_path "); if(pLineWithoutPrefix) { if(!AddPath(pLineWithoutPrefix) && !m_NumPaths) { log_error("storage", "failed to add path for the user directory"); return false; } } } if(!m_NumPaths) { log_error("storage", "no usable paths found in storage.cfg"); } return true; } bool AddDefaultPaths() { log_info("storage", "using standard paths"); if(!AddPath("$USERDIR")) { log_error("storage", "failed to add default path for the user directory"); return false; } AddPath("$DATADIR"); AddPath("$CURRENTDIR"); return true; } bool AddPath(const char *pPath) { if(!pPath[0]) { log_error("storage", "cannot add empty path"); return false; } if(m_NumPaths >= MAX_PATHS) { log_error("storage", "cannot add path '%s', the maximum number of paths is %d", pPath, MAX_PATHS); return false; } if(!str_comp(pPath, "$USERDIR")) { if(m_aUserdir[0]) { str_copy(m_aaStoragePaths[m_NumPaths++], m_aUserdir); log_info("storage", "added path '$USERDIR' ('%s')", m_aUserdir); return true; } else { log_error("storage", "cannot add path '$USERDIR' because it could not be determined"); return false; } } else if(!str_comp(pPath, "$DATADIR")) { if(m_aDatadir[0]) { str_copy(m_aaStoragePaths[m_NumPaths++], m_aDatadir); log_info("storage", "added path '$DATADIR' ('%s')", m_aDatadir); return true; } else { log_error("storage", "cannot add path '$DATADIR' because it could not be determined"); return false; } } else if(!str_comp(pPath, "$CURRENTDIR")) { m_aaStoragePaths[m_NumPaths++][0] = '\0'; log_info("storage", "added path '$CURRENTDIR' ('%s')", m_aCurrentdir); return true; } else if(str_utf8_check(pPath)) { if(fs_is_dir(pPath)) { str_copy(m_aaStoragePaths[m_NumPaths++], pPath); log_info("storage", "added path '%s'", pPath); return true; } else { log_error("storage", "cannot add path '%s', which is not a directory", pPath); return false; } } else { log_error("storage", "cannot add path containing invalid UTF-8"); return false; } } void FindUserDirectory() { #if defined(CONF_PLATFORM_ANDROID) // See InitAndroid in android_main.cpp for details about Android storage handling. // The current working directory is set to the app specific external storage location // on Android. The user data is stored within a folder "user" in the external storage. str_copy(m_aUserdir, "user"); #else char aFallbackUserdir[IO_MAX_PATH_LENGTH]; if(fs_storage_path("DDNet", m_aUserdir, sizeof(m_aUserdir))) { log_error("storage", "could not determine user directory"); } if(fs_storage_path("Teeworlds", aFallbackUserdir, sizeof(aFallbackUserdir))) { log_error("storage", "could not determine fallback user directory"); } if((m_aUserdir[0] == '\0' || !fs_is_dir(m_aUserdir)) && aFallbackUserdir[0] != '\0' && fs_is_dir(aFallbackUserdir)) { str_copy(m_aUserdir, aFallbackUserdir); } #endif } void FindDataDirectory(const char *pArgv0) { // 1) use data-dir in PWD if present if(fs_is_dir("data/mapres")) { str_copy(m_aDatadir, "data"); return; } #if defined(DATA_DIR) // 2) use compiled-in data-dir if present if(fs_is_dir(DATA_DIR "/mapres")) { str_copy(m_aDatadir, DATA_DIR, sizeof(m_aDatadir)); return; } #endif // 3) check for usable path in argv[0] { #ifdef CONF_PLATFORM_HAIKU pArgv0 = realpath(pArgv0, NULL); #endif 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]; 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); return; } } } #ifdef CONF_PLATFORM_HAIKU free((void *)pArgv0); #endif #if defined(CONF_FAMILY_UNIX) // 4) check for all default locations { 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"}; for(const char *pDir : apDirs) { char aBuf[IO_MAX_PATH_LENGTH]; str_format(aBuf, sizeof(aBuf), "%s/data/mapres", pDir); if(fs_is_dir(aBuf)) { str_format(m_aDatadir, sizeof(m_aDatadir), "%s/data", pDir); return; } } } #endif log_warn("storage", "no data directory found"); } bool FindCurrentDirectory() { if(!fs_getcwd(m_aCurrentdir, sizeof(m_aCurrentdir))) { log_error("storage", "could not determine current directory"); return false; } return true; } void FindBinaryDirectory(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'; } 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 { char aBuffer[IO_MAX_PATH_LENGTH]; if(Type == TYPE_ALL) { SListDirectoryInfoUniqueCallbackData Data; Data.m_pfnDelegate = pfnCallback; Data.m_pDelegateUser = pUser; // list all available directories for(int i = TYPE_SAVE; i < m_NumPaths; ++i) fs_listdir_fileinfo(GetPath(i, pPath, aBuffer, sizeof(aBuffer)), ListDirectoryInfoUniqueCallback, i, &Data); } else if(Type >= TYPE_SAVE && Type < m_NumPaths) { // list wanted directory fs_listdir_fileinfo(GetPath(Type, pPath, aBuffer, sizeof(aBuffer)), pfnCallback, Type, pUser); } else { dbg_assert_failed("Type invalid"); } } 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 { char aBuffer[IO_MAX_PATH_LENGTH]; if(Type == TYPE_ALL) { SListDirectoryUniqueCallbackData Data; Data.m_pfnDelegate = pfnCallback; Data.m_pDelegateUser = pUser; // list all available directories for(int i = TYPE_SAVE; i < m_NumPaths; ++i) fs_listdir(GetPath(i, pPath, aBuffer, sizeof(aBuffer)), ListDirectoryUniqueCallback, i, &Data); } else if(Type >= TYPE_SAVE && Type < m_NumPaths) { // list wanted directory fs_listdir(GetPath(Type, pPath, aBuffer, sizeof(aBuffer)), pfnCallback, Type, pUser); } else { dbg_assert_failed("Type invalid"); } } const char *GetPath(int Type, const char *pDir, char *pBuffer, unsigned BufferSize) const { 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); } return pBuffer; } void TranslateType(int &Type, const char *pPath) const { 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 = nullptr, int BufferSize = 0) override { TranslateType(Type, pFilename); dbg_assert((Flags & IOFLAG_WRITE) == 0 || Type == TYPE_SAVE || Type == TYPE_ABSOLUTE, "IOFLAG_WRITE only usable with TYPE_SAVE and TYPE_ABSOLUTE"); char aBuffer[IO_MAX_PATH_LENGTH]; if(!pBuffer) { pBuffer = aBuffer; BufferSize = sizeof(aBuffer); } pBuffer[0] = '\0'; if(Type == TYPE_ABSOLUTE) { return io_open(GetPath(TYPE_ABSOLUTE, pFilename, pBuffer, BufferSize), Flags); } if(str_startswith(pFilename, "mapres/../skins/")) { pFilename = pFilename + str_length("mapres/../"); } if(pFilename[0] == '/' || pFilename[0] == '\\' || str_find(pFilename, "../") != nullptr || str_find(pFilename, "..\\") != nullptr #ifdef CONF_FAMILY_WINDOWS || (pFilename[0] && pFilename[1] == ':') #endif ) { // don't escape base directory return nullptr; } else if(Type == TYPE_ALL) { // check all available directories for(int i = TYPE_SAVE; i < m_NumPaths; ++i) { IOHANDLE Handle = io_open(GetPath(i, pFilename, pBuffer, BufferSize), Flags); if(Handle) { return Handle; } } return nullptr; } else if(Type >= TYPE_SAVE && Type < m_NumPaths) { // check wanted directory return io_open(GetPath(Type, pFilename, pBuffer, BufferSize), Flags); } else { dbg_assert_failed("Type invalid"); } } template<typename F> bool GenericExists(const char *pFilename, int Type, F &&CheckFunction) const { 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_failed("Type invalid"); } } 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; } const bool ReadSuccess = io_read_all(File, ppResult, pResultLen); io_close(File); if(!ReadSuccess) { *ppResult = nullptr; *pResultLen = 0; return false; } return true; } char *ReadFileStr(const char *pFilename, int Type) override { IOHANDLE File = OpenFile(pFilename, IOFLAG_READ, Type); if(!File) return nullptr; char *pResult = io_read_all_str(File); io_close(File); return pResult; } bool RetrieveTimes(const char *pFilename, int Type, time_t *pCreated, time_t *pModified) override { dbg_assert(Type == TYPE_ABSOLUTE || (Type >= TYPE_SAVE && Type < m_NumPaths), "Type invalid"); char aBuffer[IO_MAX_PATH_LENGTH]; return fs_file_time(GetPath(Type, pFilename, aBuffer, sizeof(aBuffer)), pCreated, pModified) == 0; } bool CalculateHashes(const char *pFilename, int Type, SHA256_DIGEST *pSha256, unsigned *pCrc) override { dbg_assert(pSha256 != nullptr || pCrc != nullptr, "At least one output argument required"); IOHANDLE File = OpenFile(pFilename, IOFLAG_READ, Type); if(!File) return false; SHA256_CTX Sha256Ctxt; if(pSha256 != nullptr) sha256_init(&Sha256Ctxt); if(pCrc != nullptr) *pCrc = 0; unsigned char aBuffer[64 * 1024]; while(true) { unsigned Bytes = io_read(File, aBuffer, sizeof(aBuffer)); if(Bytes == 0) break; if(pSha256 != nullptr) sha256_update(&Sha256Ctxt, aBuffer, Bytes); if(pCrc != nullptr) *pCrc = crc32(*pCrc, aBuffer, Bytes); } if(pSha256 != nullptr) *pSha256 = sha256_finish(&Sha256Ctxt); io_close(File); return true; } struct CFindCBData { 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]; 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; } else if(!str_comp(pName, Data.m_pFilename)) { // found the file = end 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; CFindCBData Data; Data.m_pStorage = this; Data.m_pFilename = pFilename; Data.m_pPath = pPath; Data.m_pBuffer = pBuffer; Data.m_BufferSize = BufferSize; 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)), FindFileCallback, i, &Data); if(pBuffer[0]) return true; } } 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_failed("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_failed("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"); char aBuffer[IO_MAX_PATH_LENGTH]; GetPath(Type, pFilename, aBuffer, sizeof(aBuffer)); return fs_remove(aBuffer) == 0; } 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)); return fs_removedir(aBuffer) == 0; } bool RemoveBinaryFile(const char *pFilename) override { char aBuffer[IO_MAX_PATH_LENGTH]; GetBinaryPath(pFilename, aBuffer, sizeof(aBuffer)); return fs_remove(aBuffer) == 0; } 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)); return fs_rename(aOldBuffer, aNewBuffer) == 0; } bool RenameBinaryFile(const char *pOldFilename, const char *pNewFilename) override { char aOldBuffer[IO_MAX_PATH_LENGTH]; char aNewBuffer[IO_MAX_PATH_LENGTH]; GetBinaryPath(pOldFilename, aOldBuffer, sizeof(aOldBuffer)); GetBinaryPath(pNewFilename, aNewBuffer, sizeof(aNewBuffer)); if(fs_makedir_rec_for(aNewBuffer) < 0) { log_error("storage", "failed to create folders for: %s", aNewBuffer); return false; } return fs_rename(aOldBuffer, aNewBuffer) == 0; } 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)); return fs_makedir(aBuffer) == 0; } 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(pFilename, aBinaryPath, sizeof(aBinaryPath)); if(fs_is_relative_path(aBinaryPath)) { if(fs_getcwd(pBuffer, BufferSize)) { str_append(pBuffer, "/", BufferSize); str_append(pBuffer, aBinaryPath, BufferSize); } } else str_copy(pBuffer, aBinaryPath, BufferSize); return pBuffer; } static IStorage *Create(EInitializationType InitializationType, int NumArgs, const char **ppArguments) { CStorage *pStorage = new CStorage(); if(!pStorage->Init(InitializationType, NumArgs, ppArguments)) { delete pStorage; return nullptr; } return pStorage; } }; 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; } } 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(IStorage::EInitializationType InitializationType, int NumArgs, const char **ppArguments) { return CStorage::Create(InitializationType, NumArgs, ppArguments); } std::unique_ptr<IStorage> CreateLocalStorage() { std::unique_ptr<CStorage> pStorage = std::make_unique<CStorage>(); if(!pStorage->FindCurrentDirectory() || !pStorage->AddPath("$CURRENTDIR")) { return std::unique_ptr<IStorage>(nullptr); } return pStorage; } std::unique_ptr<IStorage> CreateTempStorage(const char *pDirectory, int NumArgs, const char **ppArguments) { dbg_assert(NumArgs > 0, "Expected at least one argument"); std::unique_ptr<CStorage> pStorage = std::make_unique<CStorage>(); pStorage->FindDataDirectory(ppArguments[0]); if(!pStorage->FindCurrentDirectory() || !pStorage->AddPath(pDirectory) || !pStorage->AddPath("$DATADIR") || !pStorage->AddPath("$CURRENTDIR")) { return std::unique_ptr<IStorage>(nullptr); } return pStorage; }