diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 39370dca793..cfb29c632f0 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -4707,7 +4707,23 @@ int main(int argc, const char **argv) delete pEngine; }); - IStorage *pStorage = CreateStorage(IStorage::STORAGETYPE_CLIENT, argc, argv); + IStorage *pStorage; + { + CMemoryLogger MemoryLogger; + MemoryLogger.SetParent(log_get_scope_logger()); + { + CLogScope LogScope(&MemoryLogger); + pStorage = CreateStorage(IStorage::EInitializationType::CLIENT, argc, argv); + } + if(!pStorage) + { + log_error("client", "Failed to initialize the storage location (see details above)"); + std::string Message = "Failed to initialize the storage location. See details below.\n\n" + MemoryLogger.ConcatenatedLines(); + pClient->ShowMessageBox("Storage Error", Message.c_str()); + PerformAllCleanup(); + return -1; + } + } pKernel->RegisterInterface(pStorage); pFutureAssertionLogger->Set(CreateAssertionLogger(pStorage, GAME_NAME)); diff --git a/src/engine/server/main.cpp b/src/engine/server/main.cpp index 16fbc04634f..f78422b4168 100644 --- a/src/engine/server/main.cpp +++ b/src/engine/server/main.cpp @@ -120,7 +120,12 @@ int main(int argc, const char **argv) IEngine *pEngine = CreateEngine(GAME_NAME, pFutureConsoleLogger, 2 * std::thread::hardware_concurrency() + 2); pKernel->RegisterInterface(pEngine); - IStorage *pStorage = CreateStorage(IStorage::STORAGETYPE_SERVER, argc, argv); + IStorage *pStorage = CreateStorage(IStorage::EInitializationType::SERVER, argc, argv); + if(!pStorage) + { + log_error("server", "failed to initialize storage"); + return -1; + } pKernel->RegisterInterface(pStorage); pFutureAssertionLogger->Set(CreateAssertionLogger(pStorage, GAME_NAME)); diff --git a/src/engine/shared/storage.cpp b/src/engine/shared/storage.cpp index 989f27ac3d8..6062f3991f3 100644 --- a/src/engine/shared/storage.cpp +++ b/src/engine/shared/storage.cpp @@ -19,113 +19,97 @@ class CStorage : public IStorage { -public: char m_aaStoragePaths[MAX_PATHS][IO_MAX_PATH_LENGTH]; - 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]; - - CStorage() - { - mem_zero(m_aaStoragePaths, sizeof(m_aaStoragePaths)); - m_NumPaths = 0; - m_aDatadir[0] = '\0'; - m_aUserdir[0] = '\0'; - m_aCurrentdir[0] = '\0'; - m_aBinarydir[0] = '\0'; - } + 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] = ""; - int Init(int StorageType, int NumArgs, const char **ppArguments) +public: + bool Init(EInitializationType InitializationType, int NumArgs, const char **ppArguments) { -#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 - // get userdir - 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"); - } + dbg_assert(NumArgs > 0, "Expected at least one argument"); + const char *pExecutablePath = ppArguments[0]; - if((m_aUserdir[0] == '\0' || !fs_is_dir(m_aUserdir)) && aFallbackUserdir[0] != '\0' && fs_is_dir(aFallbackUserdir)) + FindUserDirectory(); + FindDataDirectory(pExecutablePath); + FindCurrentDirectory(); + FindBinaryDirectory(pExecutablePath); + + if(!LoadPathsFromFile(pExecutablePath)) { - str_copy(m_aUserdir, aFallbackUserdir); + return false; } -#endif - - // get datadir - FindDatadir(ppArguments[0]); - // get binarydir - FindBinarydir(ppArguments[0]); - - // get currentdir - if(!fs_getcwd(m_aCurrentdir, sizeof(m_aCurrentdir))) + if(!m_NumPaths) { - log_error("storage", "could not determine current directory"); + if(!AddDefaultPaths()) + { + return false; + } } - // load paths from storage.cfg - LoadPaths(ppArguments[0]); - - if(!m_NumPaths) + if(InitializationType == EInitializationType::BASIC) { - dbg_msg("storage", "using standard paths"); - AddDefaultPaths(); + return true; } - // 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]))) + if(m_aaStoragePaths[TYPE_SAVE][0] != '\0') { - if(StorageType == STORAGETYPE_CLIENT) + if(fs_makedir_rec_for(m_aaStoragePaths[TYPE_SAVE]) != 0 || + fs_makedir(m_aaStoragePaths[TYPE_SAVE]) != 0) { - CreateFolder("screenshots", TYPE_SAVE); - CreateFolder("screenshots/auto", TYPE_SAVE); - CreateFolder("screenshots/auto/stats", TYPE_SAVE); - CreateFolder("maps", TYPE_SAVE); - CreateFolder("maps/auto", TYPE_SAVE); - CreateFolder("mapres", TYPE_SAVE); - CreateFolder("downloadedmaps", TYPE_SAVE); - CreateFolder("skins", TYPE_SAVE); - CreateFolder("skins7", TYPE_SAVE); - CreateFolder("downloadedskins", TYPE_SAVE); - CreateFolder("themes", TYPE_SAVE); - CreateFolder("communityicons", 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); + log_error("storage", "failed to create the user directory"); + return false; + } + } + + bool Success = true; + if(InitializationType == EInitializationType::CLIENT) + { + Success &= CreateFolder("screenshots", TYPE_SAVE); + Success &= CreateFolder("screenshots/auto", TYPE_SAVE); + Success &= CreateFolder("screenshots/auto/stats", TYPE_SAVE); + Success &= CreateFolder("maps", TYPE_SAVE); + Success &= CreateFolder("maps/auto", TYPE_SAVE); + Success &= CreateFolder("mapres", TYPE_SAVE); + Success &= CreateFolder("downloadedmaps", TYPE_SAVE); + Success &= CreateFolder("skins", TYPE_SAVE); + Success &= CreateFolder("skins7", TYPE_SAVE); + Success &= CreateFolder("downloadedskins", TYPE_SAVE); + Success &= CreateFolder("themes", TYPE_SAVE); + Success &= CreateFolder("communityicons", TYPE_SAVE); + Success &= CreateFolder("assets", TYPE_SAVE); + Success &= CreateFolder("assets/emoticons", TYPE_SAVE); + Success &= CreateFolder("assets/entities", TYPE_SAVE); + Success &= CreateFolder("assets/game", TYPE_SAVE); + Success &= CreateFolder("assets/particles", TYPE_SAVE); + Success &= CreateFolder("assets/hud", TYPE_SAVE); + Success &= CreateFolder("assets/extras", TYPE_SAVE); #if defined(CONF_VIDEORECORDER) - CreateFolder("videos", TYPE_SAVE); + Success &= 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/auto/server", TYPE_SAVE); - CreateFolder("demos/replays", TYPE_SAVE); - CreateFolder("editor", TYPE_SAVE); - CreateFolder("ghosts", TYPE_SAVE); - CreateFolder("teehistorian", TYPE_SAVE); } + Success &= CreateFolder("dumps", TYPE_SAVE); + Success &= CreateFolder("demos", TYPE_SAVE); + Success &= CreateFolder("demos/auto", TYPE_SAVE); + Success &= CreateFolder("demos/auto/race", TYPE_SAVE); + Success &= CreateFolder("demos/auto/server", TYPE_SAVE); + Success &= CreateFolder("demos/replays", TYPE_SAVE); + Success &= CreateFolder("editor", TYPE_SAVE); + Success &= CreateFolder("ghosts", TYPE_SAVE); + Success &= CreateFolder("teehistorian", TYPE_SAVE); - return m_NumPaths ? 0 : 1; + if(!Success) + { + log_error("storage", "failed to create default folders in the user directory"); + } + + return Success; } - void LoadPaths(const char *pArgv0) + bool LoadPathsFromFile(const char *pArgv0) { // check current directory IOHANDLE File = io_open("storage.cfg", IOFLAG_READ); @@ -143,51 +127,58 @@ class CStorage : public IStorage str_append(aBuffer, "/storage.cfg"); File = io_open(aBuffer, IOFLAG_READ); } - - if(Pos >= IO_MAX_PATH_LENGTH || !File) - { - dbg_msg("storage", "couldn't open storage.cfg"); - return; - } } CLineReader LineReader; if(!LineReader.OpenFile(File)) { - dbg_msg("storage", "couldn't open storage.cfg"); - return; + 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) { - AddPath(pLineWithoutPrefix); + if(!AddPath(pLineWithoutPrefix) && !m_NumPaths) + { + log_error("storage", "failed to add path for the user directory"); + return false; + } } } if(!m_NumPaths) - dbg_msg("storage", "no paths found in storage.cfg"); + { + log_error("storage", "no usable paths found in storage.cfg"); + } + return true; } - void AddDefaultPaths() + bool AddDefaultPaths() { - AddPath("$USERDIR"); + 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; } - void AddPath(const char *pPath) + bool AddPath(const char *pPath) { if(!pPath[0]) { log_error("storage", "cannot add empty path"); - return; + 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; + return false; } if(!str_comp(pPath, "$USERDIR")) @@ -195,11 +186,13 @@ class CStorage : public IStorage if(m_aUserdir[0]) { str_copy(m_aaStoragePaths[m_NumPaths++], m_aUserdir); - dbg_msg("storage", "added path '$USERDIR' ('%s')", 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")) @@ -207,37 +200,68 @@ class CStorage : public IStorage if(m_aDatadir[0]) { str_copy(m_aaStoragePaths[m_NumPaths++], m_aDatadir); - dbg_msg("storage", "added path '$DATADIR' ('%s')", 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'; - dbg_msg("storage", "added path '$CURRENTDIR' ('%s')", m_aCurrentdir); + 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); - dbg_msg("storage", "added path '%s'", 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 FindDatadir(const char *pArgv0) + void FindDataDirectory(const char *pArgv0) { // 1) use data-dir in PWD if present if(fs_is_dir("data/mapres")) @@ -296,7 +320,7 @@ class CStorage : public IStorage for(const char *pDir : apDirs) { - char aBuf[128]; + char aBuf[IO_MAX_PATH_LENGTH]; str_format(aBuf, sizeof(aBuf), "%s/data/mapres", pDir); if(fs_is_dir(aBuf)) { @@ -307,10 +331,20 @@ class CStorage : public IStorage } #endif - dbg_msg("storage", "warning: no data directory found"); + 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 FindBinarydir(const char *pArgv0) + void FindBinaryDirectory(const char *pArgv0) { #if defined(BINARY_DIR) str_copy(m_aBinarydir, BINARY_DIR, sizeof(m_aBinarydir)); @@ -761,7 +795,9 @@ class CStorage : public IStorage bool Success = !fs_remove(aBuffer); if(!Success) - dbg_msg("storage", "failed to remove: %s", aBuffer); + { + log_error("storage", "failed to remove file: %s", aBuffer); + } return Success; } @@ -774,7 +810,9 @@ class CStorage : public IStorage bool Success = !fs_removedir(aBuffer); if(!Success) - dbg_msg("storage", "failed to remove: %s", aBuffer); + { + log_error("storage", "failed to remove folder: %s", aBuffer); + } return Success; } @@ -785,7 +823,9 @@ class CStorage : public IStorage bool Success = !fs_remove(aBuffer); if(!Success) - dbg_msg("storage", "failed to remove binary: %s", aBuffer); + { + log_error("storage", "failed to remove binary file: %s", aBuffer); + } return Success; } @@ -800,7 +840,9 @@ class CStorage : public IStorage bool Success = !fs_rename(aOldBuffer, aNewBuffer); if(!Success) - dbg_msg("storage", "failed to rename: %s -> %s", aOldBuffer, aNewBuffer); + { + log_error("storage", "failed to rename file: %s -> %s", aOldBuffer, aNewBuffer); + } return Success; } @@ -813,13 +855,15 @@ class CStorage : public IStorage if(fs_makedir_rec_for(aNewBuffer) < 0) { - dbg_msg("storage", "cannot create folder for: %s", aNewBuffer); + log_error("storage", "failed to create folders for: %s", aNewBuffer); return false; } bool Success = !fs_rename(aOldBuffer, aNewBuffer); if(!Success) - dbg_msg("storage", "failed to rename: %s -> %s", aOldBuffer, aNewBuffer); + { + log_error("storage", "failed to rename binary file: %s -> %s", aOldBuffer, aNewBuffer); + } return Success; } @@ -832,7 +876,9 @@ class CStorage : public IStorage bool Success = !fs_makedir(aBuffer); if(!Success) - dbg_msg("storage", "failed to create folder: %s", aBuffer); + { + log_error("storage", "failed to create folder: %s", aBuffer); + } return Success; } @@ -866,14 +912,13 @@ class CStorage : public IStorage return pBuffer; } - static IStorage *Create(int StorageType, int NumArgs, const char **ppArguments) + static IStorage *Create(EInitializationType InitializationType, int NumArgs, const char **ppArguments) { CStorage *pStorage = new CStorage(); - if(pStorage && pStorage->Init(StorageType, NumArgs, ppArguments)) + if(!pStorage->Init(InitializationType, NumArgs, ppArguments)) { - dbg_msg("storage", "initialisation failed"); delete pStorage; - pStorage = nullptr; + return nullptr; } return pStorage; } @@ -907,32 +952,30 @@ const char *IStorage::FormatTmpPath(char *aBuf, unsigned BufSize, const char *pP return aBuf; } -IStorage *CreateStorage(int StorageType, int NumArgs, const char **ppArguments) +IStorage *CreateStorage(IStorage::EInitializationType InitializationType, int NumArgs, const char **ppArguments) { - return CStorage::Create(StorageType, NumArgs, ppArguments); + return CStorage::Create(InitializationType, NumArgs, ppArguments); } IStorage *CreateLocalStorage() { CStorage *pStorage = new CStorage(); - if(pStorage) + if(!pStorage->FindCurrentDirectory() || + !pStorage->AddPath("$CURRENTDIR")) { - if(!fs_getcwd(pStorage->m_aCurrentdir, sizeof(pStorage->m_aCurrentdir))) - { - delete pStorage; - return NULL; - } - pStorage->AddPath("$CURRENTDIR"); + delete pStorage; + return nullptr; } return pStorage; } + IStorage *CreateTempStorage(const char *pDirectory) { CStorage *pStorage = new CStorage(); - if(!pStorage) + if(!pStorage->AddPath(pDirectory)) { + delete pStorage; return nullptr; } - pStorage->AddPath(pDirectory); return pStorage; } diff --git a/src/engine/storage.h b/src/engine/storage.h index eeb146588ca..b0a5ec3aa7f 100644 --- a/src/engine/storage.h +++ b/src/engine/storage.h @@ -39,10 +39,13 @@ class IStorage : public IInterface * GetCompletePath, FileExists and FolderExists. */ TYPE_ALL_OR_ABSOLUTE = -4, + }; - STORAGETYPE_BASIC = 0, - STORAGETYPE_SERVER, - STORAGETYPE_CLIENT, + enum class EInitializationType + { + BASIC, + SERVER, + CLIENT, }; virtual int NumPaths() const = 0; @@ -73,7 +76,7 @@ class IStorage : public IInterface static const char *FormatTmpPath(char *aBuf, unsigned BufSize, const char *pPath); }; -extern IStorage *CreateStorage(int StorageType, int NumArgs, const char **ppArguments); +extern IStorage *CreateStorage(IStorage::EInitializationType InitializationType, int NumArgs, const char **ppArguments); extern IStorage *CreateLocalStorage(); extern IStorage *CreateTempStorage(const char *pDirectory); diff --git a/src/tools/dummy_map.cpp b/src/tools/dummy_map.cpp index 2c58fd9ca2e..1ecac8f428c 100644 --- a/src/tools/dummy_map.cpp +++ b/src/tools/dummy_map.cpp @@ -110,7 +110,7 @@ int main(int argc, const char **argv) { CCmdlineFix CmdlineFix(&argc, &argv); log_set_global_logger_default(); - IStorage *pStorage = CreateStorage(IStorage::STORAGETYPE_SERVER, argc, argv); + IStorage *pStorage = CreateStorage(IStorage::EInitializationType::SERVER, argc, argv); if(!pStorage) return -1; CreateEmptyMap(pStorage); diff --git a/src/tools/map_convert_07.cpp b/src/tools/map_convert_07.cpp index b64d051dca8..84383b1d943 100644 --- a/src/tools/map_convert_07.cpp +++ b/src/tools/map_convert_07.cpp @@ -114,7 +114,7 @@ int main(int argc, const char **argv) return -1; } - IStorage *pStorage = CreateStorage(IStorage::STORAGETYPE_BASIC, argc, argv); + IStorage *pStorage = CreateStorage(IStorage::EInitializationType::BASIC, argc, argv); if(!pStorage) { dbg_msg("map_convert_07", "error loading storage"); diff --git a/src/tools/map_optimize.cpp b/src/tools/map_optimize.cpp index bb810423336..a9fab2a7664 100644 --- a/src/tools/map_optimize.cpp +++ b/src/tools/map_optimize.cpp @@ -78,7 +78,7 @@ int main(int argc, const char **argv) CCmdlineFix CmdlineFix(&argc, &argv); log_set_global_logger_default(); - IStorage *pStorage = CreateStorage(IStorage::STORAGETYPE_BASIC, argc, argv); + IStorage *pStorage = CreateStorage(IStorage::EInitializationType::BASIC, argc, argv); if(!pStorage || argc <= 1 || argc > 3) { dbg_msg("map_optimize", "Invalid parameters or other unknown error."); diff --git a/src/tools/map_replace_image.cpp b/src/tools/map_replace_image.cpp index 2c2ba68a89d..ede0a180a1a 100644 --- a/src/tools/map_replace_image.cpp +++ b/src/tools/map_replace_image.cpp @@ -79,7 +79,7 @@ int main(int argc, const char **argv) return -1; } - IStorage *pStorage = CreateStorage(IStorage::STORAGETYPE_BASIC, argc, argv); + IStorage *pStorage = CreateStorage(IStorage::EInitializationType::BASIC, argc, argv); if(!pStorage) { dbg_msg("map_replace_image", "error loading storage"); diff --git a/src/tools/map_resave.cpp b/src/tools/map_resave.cpp index cc16f16233b..b01f2c44e38 100644 --- a/src/tools/map_resave.cpp +++ b/src/tools/map_resave.cpp @@ -68,7 +68,7 @@ int main(int argc, const char **argv) return -1; } - IStorage *pStorage = CreateStorage(IStorage::STORAGETYPE_BASIC, argc, argv); + IStorage *pStorage = CreateStorage(IStorage::EInitializationType::BASIC, argc, argv); if(!pStorage) { log_error(TOOL_NAME, "Error creating basic storage");