Files
UnrealEngine/Engine/Source/ThirdParty/MaterialX/MaterialX-1.38.10/source/MaterialXFormat/File.cpp
2025-05-18 13:04:45 +08:00

381 lines
8.7 KiB
C++

//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//
#include <MaterialXFormat/File.h>
#include <MaterialXFormat/Environ.h>
#include <MaterialXCore/Exception.h>
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <direct.h>
#else
#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
#endif
#if defined(__linux__)
#include <linux/limits.h>
#elif defined(__FreeBSD__)
#include <sys/syslimits.h>
#elif defined(__APPLE__)
#include <mach-o/dyld.h>
#endif
#include <array>
#include <cctype>
#include <cerrno>
#include <cstring>
MATERIALX_NAMESPACE_BEGIN
const string VALID_SEPARATORS = "/\\";
const char PREFERRED_SEPARATOR_WINDOWS = '\\';
const char PREFERRED_SEPARATOR_POSIX = '/';
const string CURRENT_PATH_STRING = ".";
const string PARENT_PATH_STRING = "..";
#if defined(_WIN32)
const string PATH_LIST_SEPARATOR = ";";
#else
const string PATH_LIST_SEPARATOR = ":";
#endif
const string MATERIALX_SEARCH_PATH_ENV_VAR = "MATERIALX_SEARCH_PATH";
inline bool hasWindowsDriveSpecifier(const string& val)
{
return (val.length() > 1 && std::isalpha((unsigned char) val[0]) && (val[1] == ':'));
}
//
// FilePath methods
//
void FilePath::assign(const string& str)
{
_type = TypeRelative;
_vec = splitString(str, VALID_SEPARATORS);
if (!str.empty())
{
if (str[0] == PREFERRED_SEPARATOR_POSIX)
{
_type = TypeAbsolute;
}
else if (str.size() >= 2)
{
if (hasWindowsDriveSpecifier(str))
{
_type = TypeAbsolute;
}
else if (str[0] == '\\' && str[1] == '\\')
{
_type = TypeNetwork;
}
}
}
}
string FilePath::asString(Format format) const
{
string str;
if (format == FormatPosix && isAbsolute())
{
// Don't prepend a POSIX separator on a Windows absolute path
if (_vec.empty() || !hasWindowsDriveSpecifier(_vec[0]))
{
str += "/";
}
}
else if (format == FormatWindows && _type == TypeNetwork)
{
str += "\\\\";
}
for (size_t i = 0; i < _vec.size(); i++)
{
str += _vec[i];
if (i + 1 < _vec.size())
{
if (format == FormatPosix)
{
str += PREFERRED_SEPARATOR_POSIX;
}
else
{
str += PREFERRED_SEPARATOR_WINDOWS;
}
}
}
return str;
}
FilePath FilePath::operator/(const FilePath& rhs) const
{
if (rhs.isAbsolute())
{
throw Exception("Appended path must be relative.");
}
FilePath combined(*this);
for (const string& str : rhs._vec)
{
combined._vec.push_back(str);
}
return combined;
}
FilePath FilePath::getNormalized() const
{
FilePath res;
for (const string& str : _vec)
{
if (str == CURRENT_PATH_STRING)
{
continue;
}
if (str == PARENT_PATH_STRING && !res.isEmpty() && res[res.size() - 1] != PARENT_PATH_STRING)
{
res._vec.pop_back();
continue;
}
res._vec.push_back(str);
}
res._type = _type;
return res;
}
bool FilePath::exists() const
{
#if defined(_WIN32)
uint32_t result = GetFileAttributesA(asString().c_str());
return result != INVALID_FILE_ATTRIBUTES;
#else
struct stat sb;
return stat(asString().c_str(), &sb) == 0;
#endif
}
bool FilePath::isDirectory() const
{
#if defined(_WIN32)
uint32_t result = GetFileAttributesA(asString().c_str());
if (result == INVALID_FILE_ATTRIBUTES)
return false;
return (result & FILE_ATTRIBUTE_DIRECTORY) != 0;
#else
struct stat sb;
if (stat(asString().c_str(), &sb))
return false;
return S_ISDIR(sb.st_mode);
#endif
}
FilePathVec FilePath::getFilesInDirectory(const string& extension) const
{
FilePathVec files;
#if defined(_WIN32)
WIN32_FIND_DATAA fd;
string wildcard = "*." + extension;
HANDLE hFind = FindFirstFileA((*this / wildcard).asString().c_str(), &fd);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
files.emplace_back(fd.cFileName);
}
} while (FindNextFileA(hFind, &fd));
FindClose(hFind);
}
#else
DIR* dir = opendir(asString().c_str());
if (dir)
{
while (struct dirent* entry = readdir(dir))
{
if (entry->d_type != DT_DIR && FilePath(entry->d_name).getExtension() == extension)
{
files.push_back(FilePath(entry->d_name));
}
}
closedir(dir);
}
#endif
return files;
}
FilePathVec FilePath::getSubDirectories() const
{
if (!isDirectory())
{
return FilePathVec();
}
FilePathVec dirs{ *this };
#if defined(_WIN32)
WIN32_FIND_DATAA fd;
string wildcard = "*";
HANDLE hFind = FindFirstFileA((*this / wildcard).asString().c_str(), &fd);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
string path = fd.cFileName;
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (path != "." && path != ".."))
{
FilePath newDir = *this / path;
FilePathVec newDirs = newDir.getSubDirectories();
dirs.insert(dirs.end(), newDirs.begin(), newDirs.end());
}
} while (FindNextFileA(hFind, &fd));
FindClose(hFind);
}
#else
DIR* dir = opendir(asString().c_str());
if (dir)
{
while (struct dirent* entry = readdir(dir))
{
string path = entry->d_name;
if (path == "." || path == "..")
{
continue;
}
auto d_type = entry->d_type;
FilePath newDir = *this / path;
if (d_type == DT_UNKNOWN)
{
if (newDir.isDirectory())
{
d_type = DT_DIR;
}
}
if (d_type == DT_DIR)
{
FilePathVec newDirs = newDir.getSubDirectories();
dirs.insert(dirs.end(), newDirs.begin(), newDirs.end());
}
}
closedir(dir);
}
#endif
return dirs;
}
void FilePath::createDirectory() const
{
#if defined(_WIN32)
_mkdir(asString().c_str());
#else
mkdir(asString().c_str(), 0777);
#endif
}
bool FilePath::setCurrentPath()
{
#if defined(_WIN32)
return (_chdir(asString().c_str()) == 0);
#else
return (chdir(asString().c_str()) == 0);
#endif
}
FilePath FilePath::getCurrentPath()
{
#if defined(_WIN32)
std::array<char, MAX_PATH> buf;
if (!GetCurrentDirectoryA(MAX_PATH, buf.data()))
{
throw Exception("Error in getCurrentPath: " + std::to_string(GetLastError()));
}
return FilePath(buf.data());
#else
std::array<char, PATH_MAX> buf;
if (getcwd(buf.data(), PATH_MAX) == NULL)
{
throw Exception("Error in getCurrentPath: " + string(strerror(errno)));
}
return FilePath(buf.data());
#endif
}
FilePath FilePath::getModulePath()
{
#if defined(_WIN32)
vector<char> buf(MAX_PATH);
while (true)
{
uint32_t reqSize = GetModuleFileNameA(NULL, buf.data(), (uint32_t) buf.size());
if (!reqSize)
{
throw Exception("Error in getModulePath: " + std::to_string(GetLastError()));
}
else if ((size_t) reqSize >= buf.size())
{
buf.resize(buf.size() * 2);
}
else
{
return FilePath(buf.data()).getParentPath();
}
}
#elif defined(__APPLE__)
vector<char> buf(PATH_MAX);
while (true)
{
uint32_t reqSize = static_cast<uint32_t>(buf.size());
if (_NSGetExecutablePath(buf.data(), &reqSize) == -1)
{
buf.resize((size_t) reqSize);
}
else
{
return FilePath(buf.data()).getParentPath();
}
}
#else
vector<char> buf(PATH_MAX);
while (true)
{
ssize_t reqSize = readlink("/proc/self/exe", buf.data(), buf.size());
if (reqSize == -1)
{
throw Exception("Error in getModulePath: " + string(strerror(errno)));
}
else if ((size_t) reqSize >= buf.size())
{
buf.resize(buf.size() * 2);
}
else
{
buf.data()[reqSize] = '\0';
return FilePath(buf.data()).getParentPath();
}
}
#endif
}
FileSearchPath getEnvironmentPath(const string& sep)
{
string searchPathEnv = getEnviron(MATERIALX_SEARCH_PATH_ENV_VAR);
return FileSearchPath(searchPathEnv, sep);
}
MATERIALX_NAMESPACE_END