summaryrefslogtreecommitdiffstats
path: root/src/foundation/FileTools.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/foundation/FileTools.cpp')
-rw-r--r--src/foundation/FileTools.cpp550
1 files changed, 550 insertions, 0 deletions
diff --git a/src/foundation/FileTools.cpp b/src/foundation/FileTools.cpp
new file mode 100644
index 0000000..c1733a6
--- /dev/null
+++ b/src/foundation/FileTools.cpp
@@ -0,0 +1,550 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "foundation/FileTools.h"
+#include "foundation/Utils.h"
+#include <string.h>
+#
+#ifdef EA_PLATFORM_WINDOWS
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#else // posix
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#endif
+#include <QDir>
+#include <QFile>
+#include <QUrl>
+
+using namespace qt3ds::foundation;
+
+namespace {
+// State machine where you can add a character
+// and it will tell you how many characters to erase
+struct SPathStateMachine
+{
+ struct States
+ {
+ enum Enum {
+ NoState = 0, // Don't care
+ Slash, // Last char was either a forward or backward slash
+ Period, // Last char was a period
+ TwoPeriods, // Last two characters were periods
+ };
+ };
+ struct Actions
+ {
+ enum Enum {
+ NoAction = 0,
+ DeleteBack1Slash,
+ DeleteBack2Slashes,
+ };
+ };
+
+ States::Enum m_State;
+
+ SPathStateMachine()
+ : m_State(States::NoState)
+ {
+ }
+
+ Actions::Enum AnalyzeChar(char32_t inChar)
+ {
+ switch (inChar) {
+ case '\\':
+ case '/':
+ switch (m_State) {
+ case States::NoState:
+ m_State = States::Slash;
+ break;
+ case States::Period:
+ m_State = States::Slash;
+ return Actions::DeleteBack1Slash;
+
+ case States::TwoPeriods:
+ m_State = States::Slash;
+ return Actions::DeleteBack2Slashes;
+ case States::Slash:
+ return Actions::DeleteBack1Slash;
+ }
+ break;
+ case '.':
+ switch (m_State) {
+ case States::Slash:
+ case States::NoState:
+ m_State = States::Period;
+ break;
+ case States::Period:
+ m_State = States::TwoPeriods;
+ break;
+ case States::TwoPeriods:
+ break;
+ }
+ break;
+ default:
+ m_State = States::NoState;
+ break;
+ }
+ return Actions::NoAction;
+ }
+};
+
+template <typename TStrType>
+inline bool DoDeleteBack1Slash(TStr::size_type &idx, TStrType &ioPath)
+{
+ TStr::size_type slashLoc = ioPath.rfind('/', idx - 1);
+ if ((slashLoc != TStr::npos) && (slashLoc > 2)
+ // and the next *two* characters aren't both dots.
+ && ((ioPath[slashLoc - 1] != '.') || (ioPath[slashLoc - 2] != '.'))) {
+
+ ioPath.erase(ioPath.begin() + slashLoc, ioPath.begin() + idx);
+ idx = slashLoc;
+ return true;
+ }
+ return false;
+}
+
+template <typename TStrType>
+void NormalizePathT(TStrType &ioPath)
+{
+ TStr::size_type pathLen = ioPath.size();
+ SPathStateMachine theStateMachine;
+ for (TStr::size_type idx = 0; idx < pathLen; ++idx) {
+ char8_t &currentChar = ioPath[idx];
+ if (currentChar == '\\')
+ currentChar = '/';
+ SPathStateMachine::Actions::Enum action = theStateMachine.AnalyzeChar(currentChar);
+ switch (action) {
+ case SPathStateMachine::Actions::DeleteBack2Slashes:
+ if (DoDeleteBack1Slash(idx, ioPath))
+ DoDeleteBack1Slash(idx, ioPath);
+ pathLen = ioPath.size();
+ break;
+
+ case SPathStateMachine::Actions::DeleteBack1Slash:
+ DoDeleteBack1Slash(idx, ioPath);
+ pathLen = ioPath.size();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+bool IsAbsolute(const char8_t *inPath, size_t inLen)
+{
+ if (inLen > 2 && inPath[1] == ':')
+ return true;
+ else if (inLen > 1 && (inPath[0] == '\\' || inPath[0] == '/'))
+ return true;
+ return false;
+}
+
+template <typename TStrType>
+void CombineBaseAndRelativeT(const char8_t *inBase, const char8_t *inRelative, TStrType &outString)
+{
+ if (IsAbsolute(inRelative, StrLen(inRelative))) {
+ outString.assign(nonNull(inRelative));
+ } else {
+ if (inRelative && *inRelative) {
+ if (inRelative[0] == '#')
+ outString.assign(inRelative);
+ else {
+ if (IsAbsolute(inRelative, strlen(inRelative))) {
+ outString.assign(inRelative);
+ } else {
+ outString = inBase ? inBase : "";
+ if (outString.size())
+ outString.append("/");
+ outString.append(inRelative ? inRelative : (const char8_t *)L"");
+ }
+ NormalizePathT(outString);
+ }
+ }
+ }
+}
+
+template <typename TStrType>
+void GetRelativeFromBaseT(TStrType &inBaseStr, TStrType &inRelativeStr, TStrType &outString)
+{
+ outString.clear();
+ NormalizePathT(inBaseStr);
+ NormalizePathT(inRelativeStr);
+ if (inBaseStr.size() == 0) {
+ outString.assign(inRelativeStr.c_str());
+ return;
+ }
+ if (inRelativeStr.size() == 0) {
+ outString.clear();
+ return;
+ }
+ // find longest common string
+ const char8_t *inBase = inBaseStr.c_str();
+ const char8_t *baseEnd = inBaseStr.c_str() + inBaseStr.size();
+ const char8_t *inRelative = inRelativeStr.c_str();
+ size_t relativeLen = inRelativeStr.size();
+ const char8_t *relativeEnd = inRelative + relativeLen;
+
+ for (; inRelative < relativeEnd && inBase < baseEnd && *inRelative == *inBase;
+ ++inRelative, ++inBase)
+ ;
+
+ // They had nothing in common.
+ if (inBase == inBaseStr.c_str()) {
+ outString.assign(inRelativeStr.c_str());
+ return;
+ }
+
+ if (inRelative && (*inRelative == '\\' || *inRelative == '/'))
+ ++inRelative;
+
+ const char *common = inBase;
+ if (common == NULL || *common == 0) {
+ outString.assign("./");
+ outString.append(inRelative);
+ NormalizePathT(outString);
+ return;
+ }
+ // Backtrack to the nearest slash.
+ while (*common && *common != '\\' && *common != '/')
+ --common;
+
+ bool foundNonSlash = false;
+ for (; common != baseEnd; ++common) {
+ if (*common != '\\' && *common != '/') {
+ if (foundNonSlash == false)
+ outString.append("..\\");
+ foundNonSlash = true;
+ } else
+ foundNonSlash = false;
+ }
+ if (inRelative < relativeEnd) {
+ if (outString.size() == 0)
+ outString.assign("./");
+ outString.append(inRelative);
+ }
+ NormalizePathT(outString);
+}
+}
+
+void CFileTools::NormalizePath(TStr &ioPath)
+{
+ NormalizePathT(ioPath);
+}
+
+void CFileTools::NormalizePath(eastl::string &ioPath)
+{
+ NormalizePathT(ioPath);
+}
+
+QString CFileTools::NormalizePathForQtUsage(const QString &path)
+{
+ // path can be a file path or a qrc URL string.
+
+ QString filePath = QDir::cleanPath(path);
+
+ filePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
+
+ if (filePath.startsWith(QLatin1String("./")))
+ return filePath.mid(2);
+
+ if (filePath.startsWith(QLatin1String("qrc:/")))
+ return filePath.mid(3);
+ else
+ return filePath;
+}
+
+void CFileTools::CombineBaseAndRelative(const char8_t *inBase, const char8_t *inRelative,
+ TStr &outString)
+{
+ CombineBaseAndRelativeT(inBase, inRelative, outString);
+}
+
+void CFileTools::CombineBaseAndRelative(const char8_t *inBase, const char8_t *inRelative,
+ eastl::string &outString)
+{
+ CombineBaseAndRelativeT(inBase, inRelative, outString);
+}
+
+void CFileTools::GetRelativeFromBase(TStr &inBaseStr, TStr &inRelativeStr, TStr &outString)
+{
+ GetRelativeFromBaseT(inBaseStr, inRelativeStr, outString);
+}
+
+void CFileTools::GetRelativeFromBase(eastl::string &inBaseStr, eastl::string &inRelativeStr,
+ eastl::string &outString)
+{
+ GetRelativeFromBaseT(inBaseStr, inRelativeStr, outString);
+}
+
+bool CFileTools::RequiresCombineBaseAndRelative(const char8_t *inPath)
+{
+ if (inPath && *inPath)
+ return inPath[0] == '.';
+ return false;
+}
+
+template <typename TStrType>
+void ToPlatformPathT(TStrType &outString)
+{
+#ifndef EA_PLATFORM_WINDOWS
+ for (TStr::size_type pos = outString.find('\\'); pos != TStr::npos;
+ pos = outString.find('\\', pos + 1))
+ outString.replace(outString.begin() + pos, outString.begin() + pos + 1, "/");
+#else
+ (void)outString;
+#endif
+}
+
+void CFileTools::ToPlatformPath(TStr &outString)
+{
+ ToPlatformPathT(outString);
+}
+
+void CFileTools::ToPlatformPath(eastl::string &outString)
+{
+ ToPlatformPathT(outString);
+}
+
+CRegisteredString CFileTools::RemapPathToBinaryFormat(TStr &inPath, TStr &inPresentationDir,
+ TStr &ioWorkspaceStr,
+ IStringTable &inStringTable)
+{
+ GetRelativeFromBase(inPresentationDir, inPath, ioWorkspaceStr);
+ CRegisteredString theNewStr = inStringTable.RegisterStr(ioWorkspaceStr.c_str());
+ theNewStr.Remap(inStringTable.GetRemapMap());
+ return theNewStr;
+}
+
+CRegisteredString CFileTools::RemapPathFromBinaryFormat(CRegisteredString inPath,
+ const char8_t *inPresDir,
+ TStr &ioWorkspaceStr,
+ const CStrTableOrDataRef &inRef,
+ IStringTable &inStringTable)
+{
+ inPath.Remap(inRef);
+ if (RequiresCombineBaseAndRelative(inPath.c_str())) {
+ CombineBaseAndRelative(inPresDir, inPath, ioWorkspaceStr);
+ return inStringTable.RegisterStr(ioWorkspaceStr.c_str());
+ }
+ return inPath;
+}
+
+void CFileTools::GetDirectory(eastl::string &ioPath)
+{
+ eastl::string::size_type theSlashPos = ioPath.find_last_of("\\/");
+ if (theSlashPos == eastl::string::npos) {
+ ioPath.clear();
+ return;
+ }
+ ioPath.resize(theSlashPos);
+}
+
+bool CFileTools::DirectoryExists(const char8_t *inPath)
+{
+#ifdef EA_PLATFORM_WINDOWS
+ DWORD theAtts = GetFileAttributesA(inPath);
+ return theAtts != INVALID_FILE_ATTRIBUTES && (theAtts & FILE_ATTRIBUTE_DIRECTORY);
+#else // Posix style check for directory
+ int status;
+ struct stat st_buf;
+ status = stat(inPath, &st_buf);
+ if (status == 0 && S_ISDIR(st_buf.st_mode))
+ return true;
+ return false;
+#endif
+}
+
+bool CFileTools::FileExists(const char8_t *inPath)
+{
+#ifdef EA_PLATFORM_WINDOWS
+ DWORD theAtts = GetFileAttributesA(inPath);
+ return theAtts != INVALID_FILE_ATTRIBUTES;
+#else // Posix style check for directory
+ int status;
+ struct stat st_buf;
+ status = stat(inPath, &st_buf);
+ if (status == 0)
+ return true;
+ return false;
+#endif
+}
+
+eastl::string CFileTools::GetFileOrAssetPath(const char8_t *inPath)
+{
+ QFile tmp(inPath);
+ if (tmp.exists())
+ return inPath;
+ return eastl::string("assets:/") + inPath;
+}
+
+void CFileTools::SetStreamPosition(QIODevice& device, qint64 inOffset,
+ qt3ds::foundation::SeekPosition::Enum inEnum)
+{
+ if (inEnum == qt3ds::foundation::SeekPosition::Begin)
+ device.seek(inOffset);
+ else if (inEnum == qt3ds::foundation::SeekPosition::Current)
+ device.seek(device.pos() + inOffset);
+ else if (inEnum == qt3ds::foundation::SeekPosition::End)
+ device.seek(device.size() + inOffset);
+}
+
+void CFileTools::GetExtension(const char8_t *inPath, eastl::string &outExt)
+{
+ outExt.assign(nonNull(inPath));
+ size_t dotPos = outExt.find_last_of('.');
+ if (dotPos != eastl::string::npos)
+ outExt.erase(outExt.begin(), outExt.begin() + dotPos + 1);
+}
+
+void CFileTools::Split(const char8_t *inPath, eastl::string &outDir, eastl::string &outFileStem,
+ eastl::string &outExtension)
+{
+ outDir.assign(nonNull(inPath));
+ NormalizePath(outDir);
+ outFileStem = outDir;
+ GetDirectory(outDir);
+ size_t lenDiff = outFileStem.size() - outDir.size();
+ if (lenDiff > 0) {
+ if (outDir.size())
+ outFileStem = outFileStem.substr(outDir.size() + 1);
+
+ eastl::string::size_type lastDot = outFileStem.find_last_of('.');
+ if (lastDot != eastl::string::npos) {
+ outExtension = outFileStem.substr(lastDot + 1);
+ outFileStem.resize(lastDot);
+ }
+ }
+}
+
+#ifdef EA_PLATFORM_WINDOWS
+void CFileTools::GetDirectoryEntries(const eastl::string &inPath,
+ eastl::vector<eastl::string> &outFiles)
+{
+ if (inPath.size() == 0)
+ return;
+ eastl::string tempPath(inPath);
+ NormalizePath(tempPath);
+ for (eastl::string::size_type pos = tempPath.find_first_of('/'); pos != eastl::string::npos;
+ pos = tempPath.find_first_of('/', pos + 1))
+ tempPath[pos] = '\\';
+ if (tempPath.back() != '\\')
+ tempPath.append("\\");
+ tempPath.append(1, '*');
+ WIN32_FIND_DATAA ffd;
+ HANDLE hFind = FindFirstFileA(tempPath.c_str(), &ffd);
+ outFiles.clear();
+ if (INVALID_HANDLE_VALUE == hFind)
+ return;
+
+ do {
+ if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0)
+ continue;
+ outFiles.push_back(eastl::string(ffd.cFileName));
+ } while (FindNextFileA(hFind, &ffd) != 0);
+}
+#else
+void CFileTools::GetDirectoryEntries(const eastl::string &inPath,
+ eastl::vector<eastl::string> &outFiles)
+{
+ if (inPath.size() == 0)
+ return;
+ eastl::string tempPath(inPath);
+ NormalizePath(tempPath);
+ struct dirent *dent;
+ DIR *srcdir = opendir(tempPath.c_str());
+ if (srcdir) {
+ while ((dent = readdir(srcdir)) != NULL) {
+ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
+ continue;
+ outFiles.push_back(eastl::string(dent->d_name));
+ }
+ closedir(srcdir);
+ }
+}
+#endif
+
+bool CFileTools::CreateDir(const eastl::string &inPath, bool inRecurse)
+{
+ if (DirectoryExists(inPath.c_str()))
+ return true;
+
+ eastl::string temp(inPath);
+ GetDirectory(temp);
+ if (temp.size() && !DirectoryExists(temp.c_str())) {
+ if (inRecurse)
+ CreateDir(temp, inRecurse);
+ else
+ return false;
+ }
+
+#ifdef EA_PLATFORM_WINDOWS
+ BOOL result = CreateDirectoryA(inPath.c_str(), NULL);
+ return result != 0;
+#else
+ int result = mkdir(inPath.c_str(), 0777);
+ return result == 0;
+#endif
+}
+
+void CFileTools::AppendDirectoryInPathToFile(eastl::string &ioPath, const char8_t *dirName)
+{
+ eastl::string::size_type lastSlash = ioPath.find_last_of("\\/");
+ if (lastSlash != eastl::string::npos) {
+ if (dirName == NULL)
+ dirName = ""; // avoid crashes on null strings
+ ioPath.insert(lastSlash + 1, "/");
+ ioPath.insert(lastSlash + 1, dirName);
+ } else {
+ ioPath.insert(0, "/");
+ ioPath.insert(0, dirName);
+ }
+}
+
+void CFileTools::RemoveLastDirectoryInPathToFile(eastl::string &ioPath)
+{
+ eastl::string::size_type lastSlash = ioPath.find_last_of("\\/");
+ if (lastSlash != eastl::string::npos) {
+ eastl::string::size_type secondToLastSlash = ioPath.find_last_of("\\/", lastSlash - 1);
+ if (secondToLastSlash != eastl::string::npos)
+ ioPath = ioPath.erase(secondToLastSlash, lastSlash - secondToLastSlash);
+ }
+}
+
+void CFileTools::SetExtension(eastl::string &ioPath, const char8_t *inExt)
+{
+ eastl::string::size_type thePos = ioPath.find_last_of(".");
+ if (thePos != eastl::string::npos) {
+ ++thePos;
+ ioPath = ioPath.replace(thePos, ioPath.size() - thePos, inExt);
+ }
+}