#include "utils/paths.h" #include #include #include #include #include "appfat.h" #include "utils/file_util.h" #include "utils/log.hpp" #include "utils/sdl_ptrs.h" #ifdef __IPHONEOS__ #include "platform/ios/ios_paths.h" #endif #ifdef NXDK #define NOMINMAX 1 #define WIN32_LEAN_AND_MEAN #include #endif #ifdef USE_SDL1 #include "utils/sdl2_to_1_2_backports.h" #endif namespace devilution { namespace paths { namespace { std::optional basePath; std::optional prefPath; std::optional configPath; std::optional assetsPath; void AddTrailingSlash(std::string &path) { if (!path.empty() && path.back() != DirectorySeparator) path += DirectorySeparator; } std::string FromSDL(char *s) { SDLUniquePtr pinned(s); std::string result = (s != nullptr ? s : ""); if (s == nullptr) { Log("{}", SDL_GetError()); SDL_ClearError(); } return result; } #ifdef NXDK const std::string &NxdkGetPrefPath() { static const std::string Path = []() { const char *path = "E:\\UDATA\\devilutionx\\"; if (CreateDirectoryA(path, nullptr) == FALSE && ::GetLastError() != ERROR_ALREADY_EXISTS) { DirErrorDlg(path); } return path; }(); return Path; } #endif } // namespace const std::string &BasePath() { if (!basePath) { basePath = FromSDL(SDL_GetBasePath()); } return *basePath; } const std::string &PrefPath() { if (!prefPath) { #if defined(__IPHONEOS__) prefPath = FromSDL(IOSGetPrefPath()); #elif defined(NXDK) prefPath = NxdkGetPrefPath(); #else prefPath = FromSDL(SDL_GetPrefPath("diasurgical", "devilution")); #if !defined(__amigaos__) if (FileExistsAndIsWriteable("diablo.ini")) { prefPath = std::string("." DIRECTORY_SEPARATOR_STR); } #endif #endif } return *prefPath; } const std::string &ConfigPath() { if (!configPath) { #if defined(__IPHONEOS__) configPath = FromSDL(IOSGetPrefPath()); #elif defined(NXDK) configPath = NxdkGetPrefPath(); #else configPath = FromSDL(SDL_GetPrefPath("diasurgical", "devilution")); #if !defined(__amigaos__) if (FileExistsAndIsWriteable("diablo.ini")) { configPath = std::string("." DIRECTORY_SEPARATOR_STR); } #endif #endif } return *configPath; } const std::string &AssetsPath() { if (!assetsPath) { #if __EMSCRIPTEN__ assetsPath.emplace("assets/"); #elif defined(NXDK) assetsPath.emplace("D:\\assets\\"); #elif defined(__3DS__) || defined(__SWITCH__) assetsPath.emplace("romfs:/"); #elif defined(__APPLE__) && defined(USE_SDL1) // In `Info.plist` we have // // SDL_FILESYSTEM_BASE_DIR_TYPE // resource // // This means `SDL_GetBasePath()` returns exedir for non-bundled // and the `app_dir.app/Resources/` for bundles. // // Our built-in resources are directly in the `devilutionx.app/Resources` directory // and are normally looked up via a relative lookup in `FindAsset`. // In SDL2, this is implemented by calling `SDL_OpenFPFromBundleOrFallback` // from `SDL_RWFromFile` but SDL1 doesn't do it, so we set the directory explicitly. // // Note that SDL3 reverts to SDL1 behaviour! // https://github.com/libsdl-org/SDL/blob/962268ca21ed10b9cee31198c22681099293f20a/docs/README-migration.md?plain=1#L1623 assetsPath.emplace(FromSDL(SDL_GetBasePath())); #else assetsPath.emplace(FromSDL(SDL_GetBasePath()) + ("assets" DIRECTORY_SEPARATOR_STR)); #endif } return *assetsPath; } void SetBasePath(const std::string &path) { basePath = path; AddTrailingSlash(*basePath); } void SetPrefPath(const std::string &path) { prefPath = path; AddTrailingSlash(*prefPath); } void SetConfigPath(const std::string &path) { configPath = path; AddTrailingSlash(*configPath); } void SetAssetsPath(const std::string &path) { assetsPath = path; AddTrailingSlash(*assetsPath); } } // namespace paths } // namespace devilution