From b2c849892e7cbcaebdec4f8d48d6a0a6ab007fda Mon Sep 17 00:00:00 2001 From: MyNameIsTrez Date: Sat, 6 Jan 2024 06:03:51 +0100 Subject: [PATCH 01/10] Fix FileExists() so it doesn't return true on dirs Add DirectoryExists() Add IsValidModulePath() Add FileRemove() Add DirectoryCreate() Add DirectoryRemove() Add Rename() --- Source/Managers/LuaMan.cpp | 144 ++++++++++++++++++++++++++++++++----- Source/Managers/LuaMan.h | 89 ++++++++++++++++++----- 2 files changed, 198 insertions(+), 35 deletions(-) diff --git a/Source/Managers/LuaMan.cpp b/Source/Managers/LuaMan.cpp index de2cbe7e21..52adb12912 100644 --- a/Source/Managers/LuaMan.cpp +++ b/Source/Managers/LuaMan.cpp @@ -71,8 +71,16 @@ namespace RTE { .def("GetDirectoryList", &LuaStateWrapper::DirectoryList, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) .def("GetFileList", &LuaStateWrapper::FileList, luabind::adopt(luabind::return_value) + luabind::return_stl_iterator) .def("FileExists", &LuaStateWrapper::FileExists) + .def("DirectoryExists", &LuaStateWrapper::DirectoryExists) + .def("IsValidModulePath", &LuaStateWrapper::IsValidModulePath) .def("FileOpen", &LuaStateWrapper::FileOpen) .def("FileClose", &LuaStateWrapper::FileClose) + .def("FileRemove", &LuaStateWrapper::FileRemove) + .def("DirectoryCreate", &LuaStateWrapper::DirectoryCreate1) + .def("DirectoryCreate", &LuaStateWrapper::DirectoryCreate2) + .def("DirectoryRemove", &LuaStateWrapper::DirectoryRemove1) + .def("DirectoryRemove", &LuaStateWrapper::DirectoryRemove2) + .def("Rename", &LuaStateWrapper::Rename) .def("FileReadLine", &LuaStateWrapper::FileReadLine) .def("FileWriteLine", &LuaStateWrapper::FileWriteLine) .def("FileEOF", &LuaStateWrapper::FileEOF), @@ -273,12 +281,20 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Passthrough LuaMan Functions - const std::vector* LuaStateWrapper::DirectoryList(const std::string& relativeDirectory) { return g_LuaMan.DirectoryList(relativeDirectory); } - const std::vector* LuaStateWrapper::FileList(const std::string& relativeDirectory) { return g_LuaMan.FileList(relativeDirectory); } - bool LuaStateWrapper::FileExists(const std::string &fileName) { return g_LuaMan.FileExists(fileName); } - int LuaStateWrapper::FileOpen(const std::string& fileName, const std::string& accessMode) { return g_LuaMan.FileOpen(fileName, accessMode); } + const std::vector* LuaStateWrapper::DirectoryList(const std::string& path) { return g_LuaMan.DirectoryList(path); } + const std::vector* LuaStateWrapper::FileList(const std::string& path) { return g_LuaMan.FileList(path); } + bool LuaStateWrapper::FileExists(const std::string &path) { return g_LuaMan.FileExists(path); } + bool LuaStateWrapper::DirectoryExists(const std::string &path) { return g_LuaMan.DirectoryExists(path); } + bool LuaStateWrapper::IsValidModulePath(const std::string &path) { return g_LuaMan.IsValidModulePath(path); } + int LuaStateWrapper::FileOpen(const std::string& path, const std::string& accessMode) { return g_LuaMan.FileOpen(path, accessMode); } void LuaStateWrapper::FileClose(int fileIndex) { return g_LuaMan.FileClose(fileIndex); } void LuaStateWrapper::FileCloseAll() { return g_LuaMan.FileCloseAll(); } + bool LuaStateWrapper::FileRemove(const std::string& path) { return g_LuaMan.FileRemove(path); } + bool LuaStateWrapper::DirectoryCreate1(const std::string& path) { return g_LuaMan.DirectoryCreate(path, false); } + bool LuaStateWrapper::DirectoryCreate2(const std::string& path, bool recursive) { return g_LuaMan.DirectoryCreate(path, recursive); } + bool LuaStateWrapper::DirectoryRemove1(const std::string& path) { return g_LuaMan.DirectoryRemove(path, false); } + bool LuaStateWrapper::DirectoryRemove2(const std::string& path, bool recursive) { return g_LuaMan.DirectoryRemove(path, recursive); } + bool LuaStateWrapper::Rename(const std::string& oldPath, const std::string& newPath) { return g_LuaMan.Rename(oldPath, newPath); } std::string LuaStateWrapper::FileReadLine(int fileIndex) { return g_LuaMan.FileReadLine(fileIndex); } void LuaStateWrapper::FileWriteLine(int fileIndex, const std::string& line) { return g_LuaMan.FileWriteLine(fileIndex, line); } bool LuaStateWrapper::FileEOF(int fileIndex) { return g_LuaMan.FileEOF(fileIndex); } @@ -923,10 +939,10 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::vector * LuaMan::DirectoryList(const std::string &filePath) { + const std::vector * LuaMan::DirectoryList(const std::string &path) { auto *directoryPaths = new std::vector(); - for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(System::GetWorkingDirectory() + filePath)) { + for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(System::GetWorkingDirectory() + path)) { if (directoryEntry.is_directory()) { directoryPaths->emplace_back(directoryEntry.path().filename().generic_string()); } } return directoryPaths; @@ -934,10 +950,10 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const std::vector * LuaMan::FileList(const std::string &filePath) { + const std::vector * LuaMan::FileList(const std::string &path) { auto *filePaths = new std::vector(); - for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(System::GetWorkingDirectory() + filePath)) { + for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(System::GetWorkingDirectory() + path)) { if (directoryEntry.is_regular_file()) { filePaths->emplace_back(directoryEntry.path().filename().generic_string()); } } return filePaths; @@ -945,18 +961,34 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaMan::FileExists(const std::string &fileName) { - std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(fileName); - if ((fullPath.find("..") == std::string::npos) && (fullPath.find(System::GetModulePackageExtension()) != std::string::npos)) { - return std::filesystem::exists(fullPath); + bool LuaMan::FileExists(const std::string &path) { + std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); + if (IsValidModulePath(fullPath)) { + return std::filesystem::is_regular_file(fullPath); } + return false; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool LuaMan::DirectoryExists(const std::string &path) { + std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); + if (IsValidModulePath(fullPath)) { + return std::filesystem::is_directory(fullPath); + } return false; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int LuaMan::FileOpen(const std::string &fileName, const std::string &accessMode) { + // TODO: Move to ModuleMan, once the ModuleMan PR has been merged + bool LuaMan::IsValidModulePath(const std::string &path) { + return (path.find("..") == std::string::npos) && (path.find(System::GetModulePackageExtension()) != std::string::npos); + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + int LuaMan::FileOpen(const std::string &path, const std::string &accessMode) { if (c_FileAccessModes.find(accessMode) == c_FileAccessModes.end()) { g_ConsoleMan.PrintString("ERROR: Cannot open file, invalid file access mode specified."); return -1; @@ -974,8 +1006,8 @@ namespace RTE { return -1; } - std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(fileName); - if ((fullPath.find("..") == std::string::npos) && (fullPath.find(System::GetModulePackageExtension()) != std::string::npos)) { + std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); + if (IsValidModulePath(fullPath)) { #ifdef _WIN32 FILE *file = fopen(fullPath.c_str(), accessMode.c_str()); @@ -1013,7 +1045,7 @@ namespace RTE { return fileIndex; } } - g_ConsoleMan.PrintString("ERROR: Failed to open file " + fileName); + g_ConsoleMan.PrintString("ERROR: Failed to open file " + path); return -1; } @@ -1034,6 +1066,86 @@ namespace RTE { } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool LuaMan::FileRemove(const std::string& path) { + std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); + if (IsValidModulePath(fullPath) && std::filesystem::is_regular_file(fullPath)) { +#ifdef _WIN32 + return std::filesystem::remove(fullPath); +#else + // TODO: Make sure to try this on Ubuntu + ? +#endif + } + g_ConsoleMan.PrintString("ERROR: Failed to remove file " + path); + return false; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool LuaMan::DirectoryCreate(const std::string& path, bool recursive) { + std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); + if (IsValidModulePath(fullPath)) { + try { +#ifdef _WIN32 + if (recursive) { + return std::filesystem::create_directories(fullPath); + } else { + return std::filesystem::create_directory(fullPath); + } +#else + // TODO: Make sure to try this on Ubuntu + ? +#endif + } catch (const std::filesystem::filesystem_error &e) {} + } + g_ConsoleMan.PrintString("ERROR: Failed to remove directory " + path); + return false; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool LuaMan::DirectoryRemove(const std::string& path, bool recursive) { + std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); + if (IsValidModulePath(fullPath) && std::filesystem::is_directory(fullPath)) { + try { +#ifdef _WIN32 + if (recursive) { + return std::filesystem::remove_all(fullPath) > 0; + } else { + return std::filesystem::remove(fullPath); + } +#else + // TODO: Make sure to try this on Ubuntu + ? +#endif + } catch (const std::filesystem::filesystem_error &e) {} + } + g_ConsoleMan.PrintString("ERROR: Failed to remove directory " + path); + return false; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool LuaMan::Rename(const std::string& oldPath, const std::string& newPath) { + std::string fullOldPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(oldPath); + std::string fullNewPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(newPath); + if (IsValidModulePath(fullOldPath) && IsValidModulePath(fullNewPath)) { + try { +#ifdef _WIN32 + std::filesystem::rename(fullOldPath, fullNewPath); + return true; +#else + // TODO: Make sure to try this on Ubuntu + ? +#endif + } catch (const std::filesystem::filesystem_error &e) {} + } + g_ConsoleMan.PrintString("ERROR: Failed to rename oldPath " + oldPath + " to newPath " + newPath); + return false; + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::string LuaMan::FileReadLine(int fileIndex) { diff --git a/Source/Managers/LuaMan.h b/Source/Managers/LuaMan.h index fe78e0935b..1df08e5131 100644 --- a/Source/Managers/LuaMan.h +++ b/Source/Managers/LuaMan.h @@ -284,12 +284,20 @@ namespace RTE { double PosRand(); #pragma region Passthrough LuaMan Functions - const std::vector* DirectoryList(const std::string& relativeDirectory); - const std::vector* FileList(const std::string& relativeDirectory); - bool FileExists(const std::string &fileName); - int FileOpen(const std::string& fileName, const std::string& accessMode); + const std::vector* DirectoryList(const std::string& path); + const std::vector* FileList(const std::string& path); + bool FileExists(const std::string &path); + bool DirectoryExists(const std::string &path); + bool IsValidModulePath(const std::string &path); + int FileOpen(const std::string& path, const std::string& accessMode); void FileClose(int fileIndex); void FileCloseAll(); + bool FileRemove(const std::string& path); + bool DirectoryCreate1(const std::string& path); + bool DirectoryCreate2(const std::string& path, bool recursive); + bool DirectoryRemove1(const std::string& path); + bool DirectoryRemove2(const std::string& path, bool recursive); + bool Rename(const std::string& oldPath, const std::string& newPath); std::string FileReadLine(int fileIndex); void FileWriteLine(int fileIndex, const std::string& line); bool FileEOF(int fileIndex); @@ -428,36 +436,48 @@ namespace RTE { #pragma region File I/O Handling /// - /// Returns a vector of all the directories in relativeDirectory, which is relative to the working directory. - /// Note that a call to this method overwrites any previously returned vector from DirectoryList() or FileList(). + /// Returns a vector of all the directories in path, which is relative to the working directory. /// - /// Directory path relative to the working directory. - /// A vector of the directories in relativeDirectory. - const std::vector * DirectoryList(const std::string &relativeDirectory); + /// Directory path relative to the working directory. + /// A vector of the directories in path. + const std::vector * DirectoryList(const std::string &path); /// - /// Returns a vector of all the files in relativeDirectory, which is relative to the working directory. - /// Note that a call to this method overwrites any previously returned vector from DirectoryList() or FileList(). + /// Returns a vector of all the files in path, which is relative to the working directory. /// - /// Directory path relative to the working directory. - /// A vector of the files in relativeDirectory. - const std::vector * FileList(const std::string &relativeDirectory); + /// Directory path relative to the working directory. + /// A vector of the files in path. + const std::vector * FileList(const std::string &path); /// /// Returns whether or not the specified file exists. You can only check for files inside .rte folders in the working directory. /// - /// Path to the file. All paths are made absolute by adding current working directory to the specified path. + /// Path to the file. All paths are made absolute by adding current working directory to the specified path. /// Whether or not the specified file exists. - bool FileExists(const std::string &fileName); + bool FileExists(const std::string &path); /// - /// Opens a file or creates one if it does not exist, depending on access mode. You can open files only inside .rte folders in the working directly. You can't open more that c_MaxOpenFiles file simultaneously. + /// Returns whether or not the specified directory exists. You can only check for directories inside .rte folders in the working directory. + /// + /// Path to the directory. All paths are made absolute by adding current working directory to the specified path. + /// Whether or not the specified file exists. + bool DirectoryExists(const std::string &path); + + /// + /// Returns whether or not the path refers to an accessible file or directory. You can only check for files or directories inside .rte directories in the working directory. + /// + /// Path to the file or directory. All paths are made absolute by adding current working directory to the specified path. + /// Whether or not the specified file exists. + bool IsValidModulePath(const std::string &path); + + /// + /// Opens a file or creates one if it does not exist, depending on access mode. You can open files only inside .rte folders in the working directory. You can't open more that c_MaxOpenFiles file simultaneously. /// On Linux will attempt to open a file case insensitively. /// - /// Path to the file. All paths are made absolute by adding current working directory to the specified path. + /// Path to the file. All paths are made absolute by adding current working directory to the specified path. /// File access mode. See 'fopen' for list of modes. /// File index in the opened files array. - int FileOpen(const std::string &fileName, const std::string &accessMode); + int FileOpen(const std::string &path, const std::string &accessMode); /// /// Closes a previously opened file. @@ -470,6 +490,37 @@ namespace RTE { /// void FileCloseAll(); + /// + /// Removes a file. + /// + /// Path to the file. All paths are made absolute by adding current working directory to the specified path. + /// Whether or not the file was removed. + bool FileRemove(const std::string &path); + + /// + /// Creates a directory, optionally recursively. + /// + /// Path to the directory to be created. All paths are made absolute by adding current working directory to the specified path. + /// Whether to recursively create parent directories. + /// Whether or not the directory was removed. + bool DirectoryCreate(const std::string &path, bool recursive); + + /// + /// Removes a directory, optionally recursively. + /// + /// Path to the directory to be removed. All paths are made absolute by adding current working directory to the specified path. + /// Whether to recursively remove files and directories. + /// Whether or not the directory was removed. + bool DirectoryRemove(const std::string &path, bool recursive); + + /// + /// Moves or renames the filesystem object oldPath to newPath. + /// + /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. + /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. + /// Whether or not renaming succeeded. + bool Rename(const std::string &oldPath, const std::string &newPath); + /// /// Reads a line from a file. /// From a35a7d696c9de1a4f49094e1f8200bc6fcc24d1a Mon Sep 17 00:00:00 2001 From: MyNameIsTrez Date: Sun, 14 Jan 2024 10:32:08 +0100 Subject: [PATCH 02/10] Only need to write DirectoryCreate() still --- Source/Managers/LuaMan.cpp | 237 +++++++++++++++++++++++++++++-------- 1 file changed, 188 insertions(+), 49 deletions(-) diff --git a/Source/Managers/LuaMan.cpp b/Source/Managers/LuaMan.cpp index 52adb12912..ccff7134e9 100644 --- a/Source/Managers/LuaMan.cpp +++ b/Source/Managers/LuaMan.cpp @@ -940,10 +940,22 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const std::vector * LuaMan::DirectoryList(const std::string &path) { + std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); auto *directoryPaths = new std::vector(); - for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(System::GetWorkingDirectory() + path)) { - if (directoryEntry.is_directory()) { directoryPaths->emplace_back(directoryEntry.path().filename().generic_string()); } + if (IsValidModulePath(fullPath)) { +#ifndef _WIN32 + auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath) + if (caseInsensitiveFullPath) +#endif + { +#ifndef _WIN32 + fullPath = *caseInsensitiveFullPath; +#endif + for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(fullPath) { + if (directoryEntry.is_directory()) { directoryPaths->emplace_back(directoryEntry.path().filename().generic_string()); } + } + } } return directoryPaths; } @@ -951,10 +963,22 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const std::vector * LuaMan::FileList(const std::string &path) { + std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); auto *filePaths = new std::vector(); - for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(System::GetWorkingDirectory() + path)) { - if (directoryEntry.is_regular_file()) { filePaths->emplace_back(directoryEntry.path().filename().generic_string()); } + if (IsValidModulePath(fullPath)) { +#ifndef _WIN32 + auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath) + if (caseInsensitiveFullPath) +#endif + { +#ifndef _WIN32 + fullPath = *caseInsensitiveFullPath; +#endif + for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(fullPath) { + if (directoryEntry.is_regular_file()) { filePaths->emplace_back(directoryEntry.path().filename().generic_string()); } + } + } } return filePaths; } @@ -964,7 +988,16 @@ namespace RTE { bool LuaMan::FileExists(const std::string &path) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { - return std::filesystem::is_regular_file(fullPath); +#ifndef _WIN32 + auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath) + if (caseInsensitiveFullPath) +#endif + { +#ifndef _WIN32 + fullPath = *caseInsensitiveFullPath; +#endif + return std::filesystem::is_regular_file(fullPath); + } } return false; } @@ -974,7 +1007,16 @@ namespace RTE { bool LuaMan::DirectoryExists(const std::string &path) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { - return std::filesystem::is_directory(fullPath); +#ifndef _WIN32 + auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath) + if (caseInsensitiveFullPath) +#endif + { +#ifndef _WIN32 + fullPath = *caseInsensitiveFullPath; +#endif + return std::filesystem::is_directory(fullPath); + } } return false; } @@ -988,6 +1030,35 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // TODO: Move this shit to some other place + std::optional GetCaseInsensitiveFullPath(const std::string &fullPath) { + std::filesystem::path inspectedPath = System::GetWorkingDirectory(); + const std::filesystem::path relativeFilePath = std::filesystem::path(fullPath).lexically_relative(inspectedPath); + + // Iterate over all path parts + for (std::filesystem::path::const_iterator relativeFilePathIterator = relativeFilePath.begin(); relativeFilePathIterator != relativeFilePath.end(); ++relativeFilePathIterator) { + bool pathPartExists = false; + + // Iterate over all entries in the path part's directory, + // to check if the path part is in there case insensitively + for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) { + if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) { + inspectedPath = filesystemEntryPath; + + // If the path part is found, stop looking for it + pathPartExists = true; + break; + } + } + + if (!pathPartExists) { + return std::nullopt; + } + } + + return std::optional(inspectedPath); + } + int LuaMan::FileOpen(const std::string &path, const std::string &accessMode) { if (c_FileAccessModes.find(accessMode) == c_FileAccessModes.end()) { g_ConsoleMan.PrintString("ERROR: Cannot open file, invalid file access mode specified."); @@ -1008,7 +1079,6 @@ namespace RTE { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { - #ifdef _WIN32 FILE *file = fopen(fullPath.c_str(), accessMode.c_str()); #else @@ -1016,27 +1086,34 @@ namespace RTE { std::filesystem::path inspectedPath = System::GetWorkingDirectory(); const std::filesystem::path relativeFilePath = std::filesystem::path(fullPath).lexically_relative(inspectedPath); + // Iterate over all path parts for (std::filesystem::path::const_iterator relativeFilePathIterator = relativeFilePath.begin(); relativeFilePathIterator != relativeFilePath.end(); ++relativeFilePathIterator) { bool pathPartExists = false; - // Check if a path part (directory or file) exists in the filesystem. + // Iterate over all entries in the path part's directory, + // to check if the path part is in there case insensitively for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) { - if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), (*relativeFilePathIterator).generic_string())) { + if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) { inspectedPath = filesystemEntryPath; + + // If the path part is found, stop looking for it pathPartExists = true; break; } } + if (!pathPartExists) { - // If this is the last part, then all directories in relativeFilePath exist, but the file doesn't. + // If this is the last part, then all directories in relativeFilePath exist, but the file doesn't if (std::next(relativeFilePathIterator) == relativeFilePath.end()) { return fopen((inspectedPath / relativeFilePath.filename()).generic_string().c_str(), accessMode.c_str()); } - // Some directory in relativeFilePath doesn't exist, so the file can't be created. + + // Some directory in relativeFilePath doesn't exist, so the file can't be created return nullptr; } } - // If the file exists, open it. + + // If the file exists, open it return fopen(inspectedPath.generic_string().c_str(), accessMode.c_str()); }(); #endif @@ -1070,13 +1147,19 @@ namespace RTE { bool LuaMan::FileRemove(const std::string& path) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); - if (IsValidModulePath(fullPath) && std::filesystem::is_regular_file(fullPath)) { -#ifdef _WIN32 - return std::filesystem::remove(fullPath); -#else - // TODO: Make sure to try this on Ubuntu - ? + if (IsValidModulePath(fullPath)) { +#ifndef _WIN32 + auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath) + if (caseInsensitiveFullPath) +#endif + { +#ifndef _WIN32 + fullPath = *caseInsensitiveFullPath; #endif + if (std::filesystem::is_regular_file(fullPath)) { + return std::filesystem::remove(fullPath); + } + } } g_ConsoleMan.PrintString("ERROR: Failed to remove file " + path); return false; @@ -1087,18 +1170,62 @@ namespace RTE { bool LuaMan::DirectoryCreate(const std::string& path, bool recursive) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { - try { -#ifdef _WIN32 - if (recursive) { - return std::filesystem::create_directories(fullPath); - } else { - return std::filesystem::create_directory(fullPath); - } -#else - // TODO: Make sure to try this on Ubuntu - ? +#ifndef _WIN32 + // TODO: The goal here is to return the same fullPath, + // but with the parent directories given the real filesys casing + // + // 1. There is no way for this lambda to fail + // 1. Account for the `recursive` argument of this fn + // 2. Let a lambda true if a parent directory has to be created, + // since create_directory() will return false for us in that case + auto caseInsensitiveFullPath = [&fullPath]() -> auto { + // std::filesystem::path inspectedPath = System::GetWorkingDirectory(); + // const std::filesystem::path relativeFilePath = std::filesystem::path(fullPath).lexically_relative(inspectedPath); + + // // Iterate over all path parts + // for (std::filesystem::path::const_iterator relativeFilePathIterator = relativeFilePath.begin(); relativeFilePathIterator != relativeFilePath.end(); ++relativeFilePathIterator) { + // bool pathPartExists = false; + + // // Iterate over all entries in the path part's directory, + // // to check if the path part is in there case insensitively + // for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) { + // if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) { + // inspectedPath = filesystemEntryPath; + + // // If the path part is found, stop looking for it + // pathPartExists = true; + // break; + // } + // } + + // if (!pathPartExists) { + // // If this is the last part, then all directories in relativeFilePath exist, but the file doesn't + // if (std::next(relativeFilePathIterator) == relativeFilePath.end()) { + // return fopen((inspectedPath / relativeFilePath.filename()).generic_string().c_str(), accessMode.c_str()); + // } + + // // Some directory in relativeFilePath doesn't exist, so the file can't be created + // return nullptr; + // } + // } + + // // If the file exists, open it + // return fopen(inspectedPath.generic_string().c_str(), accessMode.c_str()); + }(); + if (caseInsensitiveFullPath) +#endif + { +#ifndef _WIN32 + fullPath = *caseInsensitiveFullPath; #endif - } catch (const std::filesystem::filesystem_error &e) {} + try { + if (recursive) { + return std::filesystem::create_directories(fullPath); + } else { + return std::filesystem::create_directory(fullPath); + } + } catch (const std::filesystem::filesystem_error &e) {} + } } g_ConsoleMan.PrintString("ERROR: Failed to remove directory " + path); return false; @@ -1108,19 +1235,25 @@ namespace RTE { bool LuaMan::DirectoryRemove(const std::string& path, bool recursive) { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); - if (IsValidModulePath(fullPath) && std::filesystem::is_directory(fullPath)) { - try { -#ifdef _WIN32 - if (recursive) { - return std::filesystem::remove_all(fullPath) > 0; - } else { - return std::filesystem::remove(fullPath); - } -#else - // TODO: Make sure to try this on Ubuntu - ? + if (IsValidModulePath(fullPath)) { +#ifndef _WIN32 + auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath) + if (caseInsensitiveFullPath) +#endif + { +#ifndef _WIN32 + fullPath = *caseInsensitiveFullPath; #endif - } catch (const std::filesystem::filesystem_error &e) {} + if (std::filesystem::is_directory(fullPath)) { + try { + if (recursive) { + return std::filesystem::remove_all(fullPath) > 0; + } else { + return std::filesystem::remove(fullPath); + } + } catch (const std::filesystem::filesystem_error &e) {} + } + } } g_ConsoleMan.PrintString("ERROR: Failed to remove directory " + path); return false; @@ -1132,15 +1265,21 @@ namespace RTE { std::string fullOldPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(oldPath); std::string fullNewPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(newPath); if (IsValidModulePath(fullOldPath) && IsValidModulePath(fullNewPath)) { - try { -#ifdef _WIN32 - std::filesystem::rename(fullOldPath, fullNewPath); - return true; -#else - // TODO: Make sure to try this on Ubuntu - ? +#ifndef _WIN32 + auto caseInsensitiveFullOldPath = GetCaseInsensitiveFullPath(fullOldPath) + auto caseInsensitiveFullNewPath = GetCaseInsensitiveFullPath(fullNewPath) + if (caseInsensitiveFullOldPath && caseInsensitiveFullNewPath) +#endif + { +#ifndef _WIN32 + fullOldPath = *caseInsensitiveFullOldPath; + fullNewPath = *caseInsensitiveFullNewPath; #endif - } catch (const std::filesystem::filesystem_error &e) {} + try { + std::filesystem::rename(fullOldPath, fullNewPath); + return true; + } catch (const std::filesystem::filesystem_error &e) {} + } } g_ConsoleMan.PrintString("ERROR: Failed to rename oldPath " + oldPath + " to newPath " + newPath); return false; From 2e25608bcc1df478bc6ffc05389b3854b8a34deb Mon Sep 17 00:00:00 2001 From: MyNameIsTrez Date: Sun, 14 Jan 2024 15:35:30 +0100 Subject: [PATCH 03/10] Passing all Lua filesystem tests on Windows --- Source/Managers/LuaMan.cpp | 80 ++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/Source/Managers/LuaMan.cpp b/Source/Managers/LuaMan.cpp index ccff7134e9..8fadc1dae2 100644 --- a/Source/Managers/LuaMan.cpp +++ b/Source/Managers/LuaMan.cpp @@ -952,7 +952,7 @@ namespace RTE { #ifndef _WIN32 fullPath = *caseInsensitiveFullPath; #endif - for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(fullPath) { + for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(fullPath)) { if (directoryEntry.is_directory()) { directoryPaths->emplace_back(directoryEntry.path().filename().generic_string()); } } } @@ -975,7 +975,7 @@ namespace RTE { #ifndef _WIN32 fullPath = *caseInsensitiveFullPath; #endif - for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(fullPath) { + for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(fullPath)) { if (directoryEntry.is_regular_file()) { filePaths->emplace_back(directoryEntry.path().filename().generic_string()); } } } @@ -1056,7 +1056,7 @@ namespace RTE { } } - return std::optional(inspectedPath); + return std::optional(inspectedPath.generic_string()); } int LuaMan::FileOpen(const std::string &path, const std::string &accessMode) { @@ -1171,46 +1171,42 @@ namespace RTE { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { #ifndef _WIN32 - // TODO: The goal here is to return the same fullPath, + // The goal here is to return the same fullPath, // but with the parent directories given the real filesys casing - // - // 1. There is no way for this lambda to fail - // 1. Account for the `recursive` argument of this fn - // 2. Let a lambda true if a parent directory has to be created, - // since create_directory() will return false for us in that case - auto caseInsensitiveFullPath = [&fullPath]() -> auto { - // std::filesystem::path inspectedPath = System::GetWorkingDirectory(); - // const std::filesystem::path relativeFilePath = std::filesystem::path(fullPath).lexically_relative(inspectedPath); - - // // Iterate over all path parts - // for (std::filesystem::path::const_iterator relativeFilePathIterator = relativeFilePath.begin(); relativeFilePathIterator != relativeFilePath.end(); ++relativeFilePathIterator) { - // bool pathPartExists = false; - - // // Iterate over all entries in the path part's directory, - // // to check if the path part is in there case insensitively - // for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) { - // if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) { - // inspectedPath = filesystemEntryPath; - - // // If the path part is found, stop looking for it - // pathPartExists = true; - // break; - // } - // } - - // if (!pathPartExists) { - // // If this is the last part, then all directories in relativeFilePath exist, but the file doesn't - // if (std::next(relativeFilePathIterator) == relativeFilePath.end()) { - // return fopen((inspectedPath / relativeFilePath.filename()).generic_string().c_str(), accessMode.c_str()); - // } - - // // Some directory in relativeFilePath doesn't exist, so the file can't be created - // return nullptr; - // } - // } - - // // If the file exists, open it - // return fopen(inspectedPath.generic_string().c_str(), accessMode.c_str()); + std::string caseInsensitiveFullPath = [&fullPath]() -> std::string { + std::filesystem::path inspectedPath = System::GetWorkingDirectory(); + const std::filesystem::path relativeFilePath = std::filesystem::path(fullPath).lexically_relative(inspectedPath); + + // Iterate over all path parts + for (std::filesystem::path::const_iterator relativeFilePathIterator = relativeFilePath.begin(); relativeFilePathIterator != relativeFilePath.end(); ++relativeFilePathIterator) { + bool pathPartExists = false; + + // Iterate over all entries in the path part's directory, + // to check if the path part is in there case insensitively + for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) { + if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) { + inspectedPath = filesystemEntryPath; + + // If the path part is found, stop looking for it + pathPartExists = true; + break; + } + } + + if (!pathPartExists) { + // If part of the path exists, append the rest of fullPath its parts + relativeFilePathIterator++; + while (relativeFilePathIterator != relativeFilePath.end()) { + inspectedPath += "/" + relativeFilePathIterator->generic_string(); + relativeFilePathIterator++; + } + + return inspectedPath; + } + } + + // If the entire path exists + return inspectedPath; }(); if (caseInsensitiveFullPath) #endif From f2dfa0374b527c912e000f782b76617cf6325891 Mon Sep 17 00:00:00 2001 From: MyNameIsTrez Date: Sun, 14 Jan 2024 20:54:53 +0100 Subject: [PATCH 04/10] Close to passing the filesystem tests on Linux --- Source/Managers/LuaMan.cpp | 153 ++++++++++++++++++------------------- Source/Managers/LuaMan.h | 15 +++- 2 files changed, 89 insertions(+), 79 deletions(-) diff --git a/Source/Managers/LuaMan.cpp b/Source/Managers/LuaMan.cpp index 8fadc1dae2..9515b6ff7a 100644 --- a/Source/Managers/LuaMan.cpp +++ b/Source/Managers/LuaMan.cpp @@ -360,7 +360,7 @@ namespace RTE { // TODO // It would be nice to assign to least-saturated state, but that's a bit tricky with MO registering... - /*auto itr = std::min_element(m_ScriptStates.begin(), m_ScriptStates.end(), + /*auto itr = std::min_element(m_ScriptStates.begin(), m_ScriptStates.end(), [](const LuaStateWrapper& lhs, const LuaStateWrapper& rhs) { return lhs.GetRegisteredMOs().size() < rhs.GetRegisteredMOs().size(); } ); @@ -375,7 +375,7 @@ namespace RTE { bool success = m_ScriptStates[ourState].GetMutex().try_lock(); RTEAssert(success, "Script mutex was already locked while in a non-multithreaded environment!"); - return &m_ScriptStates[ourState]; + return &m_ScriptStates[ourState]; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -405,13 +405,13 @@ namespace RTE { void LuaMan::ExecuteLuaScriptCallbacks() { std::vector> callbacks; - + // Move our functions into the local buffer to clear the existing callbacks and to lock for as little time as possible { std::scoped_lock lock(m_ScriptCallbacksMutex); callbacks.swap(m_ScriptCallbacks); } - + for (const std::function &callback : callbacks) { callback(); } @@ -871,7 +871,7 @@ namespace RTE { bool LuaStateWrapper::TableEntryIsDefined(const std::string &tableName, const std::string &indexName) { std::lock_guard lock(m_Mutex); - + // Push the table onto the stack, checking if it even exists. lua_getglobal(m_State, tableName.c_str()); if (!lua_istable(m_State, -1)) { @@ -937,6 +937,42 @@ namespace RTE { return stackDescription.str(); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // TODO: Ask Causeless whether this is an appropriate spot for this method + std::optional LuaMan::GetCaseInsensitiveFullPath(const std::string &fullPath) { + std::filesystem::path inspectedPath = System::GetWorkingDirectory(); + const std::filesystem::path relativeFilePath = std::filesystem::path(fullPath).lexically_relative(inspectedPath); + + // Iterate over all path parts + for (std::filesystem::path::const_iterator relativeFilePathIterator = relativeFilePath.begin(); relativeFilePathIterator != relativeFilePath.end(); ++relativeFilePathIterator) { + bool pathPartExists = false; + + // Iterate over all entries in the path part's directory, + // to check if the path part is in there case insensitively + for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) { + if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) { + inspectedPath = filesystemEntryPath; + + // If the path part is found, stop looking for it + pathPartExists = true; + break; + } + } + + if (!pathPartExists) { + // TODO: This should return the same thing DirectoryCreate() does, + // where it just appends the rest of fullPath. + // This way it's the responsibility of the caller to use std::filestem::exists(), + // which allows DirectoryCreate() and Rename() to call this function too, + // since they allow the fullPath and newPath argument to not exist yet. + return NOTasdstd::nullopt; + } + } + + return std::optional(inspectedPath.generic_string()); + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const std::vector * LuaMan::DirectoryList(const std::string &path) { @@ -945,7 +981,7 @@ namespace RTE { if (IsValidModulePath(fullPath)) { #ifndef _WIN32 - auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath) + auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath); if (caseInsensitiveFullPath) #endif { @@ -968,7 +1004,7 @@ namespace RTE { if (IsValidModulePath(fullPath)) { #ifndef _WIN32 - auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath) + auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath); if (caseInsensitiveFullPath) #endif { @@ -989,7 +1025,7 @@ namespace RTE { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { #ifndef _WIN32 - auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath) + auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath); if (caseInsensitiveFullPath) #endif { @@ -1008,7 +1044,7 @@ namespace RTE { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { #ifndef _WIN32 - auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath) + auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath); if (caseInsensitiveFullPath) #endif { @@ -1030,35 +1066,6 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // TODO: Move this shit to some other place - std::optional GetCaseInsensitiveFullPath(const std::string &fullPath) { - std::filesystem::path inspectedPath = System::GetWorkingDirectory(); - const std::filesystem::path relativeFilePath = std::filesystem::path(fullPath).lexically_relative(inspectedPath); - - // Iterate over all path parts - for (std::filesystem::path::const_iterator relativeFilePathIterator = relativeFilePath.begin(); relativeFilePathIterator != relativeFilePath.end(); ++relativeFilePathIterator) { - bool pathPartExists = false; - - // Iterate over all entries in the path part's directory, - // to check if the path part is in there case insensitively - for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) { - if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) { - inspectedPath = filesystemEntryPath; - - // If the path part is found, stop looking for it - pathPartExists = true; - break; - } - } - - if (!pathPartExists) { - return std::nullopt; - } - } - - return std::optional(inspectedPath.generic_string()); - } - int LuaMan::FileOpen(const std::string &path, const std::string &accessMode) { if (c_FileAccessModes.find(accessMode) == c_FileAccessModes.end()) { g_ConsoleMan.PrintString("ERROR: Cannot open file, invalid file access mode specified."); @@ -1149,7 +1156,7 @@ namespace RTE { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { #ifndef _WIN32 - auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath) + auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath); if (caseInsensitiveFullPath) #endif { @@ -1173,47 +1180,39 @@ namespace RTE { #ifndef _WIN32 // The goal here is to return the same fullPath, // but with the parent directories given the real filesys casing - std::string caseInsensitiveFullPath = [&fullPath]() -> std::string { - std::filesystem::path inspectedPath = System::GetWorkingDirectory(); - const std::filesystem::path relativeFilePath = std::filesystem::path(fullPath).lexically_relative(inspectedPath); - - // Iterate over all path parts - for (std::filesystem::path::const_iterator relativeFilePathIterator = relativeFilePath.begin(); relativeFilePathIterator != relativeFilePath.end(); ++relativeFilePathIterator) { - bool pathPartExists = false; - - // Iterate over all entries in the path part's directory, - // to check if the path part is in there case insensitively - for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) { - if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) { - inspectedPath = filesystemEntryPath; - - // If the path part is found, stop looking for it - pathPartExists = true; - break; - } + std::filesystem::path inspectedPath = System::GetWorkingDirectory(); + const std::filesystem::path relativeFilePath = std::filesystem::path(fullPath).lexically_relative(inspectedPath); + + // Iterate over all path parts + for (std::filesystem::path::const_iterator relativeFilePathIterator = relativeFilePath.begin(); relativeFilePathIterator != relativeFilePath.end(); ++relativeFilePathIterator) { + bool pathPartExists = false; + + // Iterate over all entries in the path part's directory, + // to check if the path part is in there case insensitively + for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) { + if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) { + inspectedPath = filesystemEntryPath; + + // If the path part is found, stop looking for it + pathPartExists = true; + break; } + } - if (!pathPartExists) { - // If part of the path exists, append the rest of fullPath its parts + if (!pathPartExists) { + // If part of the path exists, append the rest of fullPath its parts + while (relativeFilePathIterator != relativeFilePath.end()) { + inspectedPath /= relativeFilePathIterator->generic_string(); relativeFilePathIterator++; - while (relativeFilePathIterator != relativeFilePath.end()) { - inspectedPath += "/" + relativeFilePathIterator->generic_string(); - relativeFilePathIterator++; - } - - return inspectedPath; } + + break; } + } - // If the entire path exists - return inspectedPath; - }(); - if (caseInsensitiveFullPath) + fullPath = inspectedPath; #endif { -#ifndef _WIN32 - fullPath = *caseInsensitiveFullPath; -#endif try { if (recursive) { return std::filesystem::create_directories(fullPath); @@ -1233,7 +1232,7 @@ namespace RTE { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { #ifndef _WIN32 - auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath) + auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath); if (caseInsensitiveFullPath) #endif { @@ -1262,8 +1261,8 @@ namespace RTE { std::string fullNewPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(newPath); if (IsValidModulePath(fullOldPath) && IsValidModulePath(fullNewPath)) { #ifndef _WIN32 - auto caseInsensitiveFullOldPath = GetCaseInsensitiveFullPath(fullOldPath) - auto caseInsensitiveFullNewPath = GetCaseInsensitiveFullPath(fullNewPath) + auto caseInsensitiveFullOldPath = GetCaseInsensitiveFullPath(fullOldPath); + auto caseInsensitiveFullNewPath = GetCaseInsensitiveFullPath(fullNewPath); if (caseInsensitiveFullOldPath && caseInsensitiveFullNewPath) #endif { @@ -1338,7 +1337,7 @@ namespace RTE { void LuaMan::StartAsyncGarbageCollection() { ZoneScoped; - + std::vector allStates; allStates.reserve(m_ScriptStates.size() + 1); @@ -1346,7 +1345,7 @@ namespace RTE { for (LuaStateWrapper& wrapper : m_ScriptStates) { allStates.push_back(&wrapper); } - + m_GarbageCollectionTask = BS::multi_future(); for (LuaStateWrapper* luaState : allStates) { m_GarbageCollectionTask.push_back( diff --git a/Source/Managers/LuaMan.h b/Source/Managers/LuaMan.h index 1df08e5131..204bc97776 100644 --- a/Source/Managers/LuaMan.h +++ b/Source/Managers/LuaMan.h @@ -8,6 +8,8 @@ #include "BS_thread_pool.hpp" +#include + #define g_LuaMan LuaMan::Instance() struct lua_State; @@ -82,7 +84,7 @@ namespace RTE { /// /// This LuaStateWrapper's internal lua state. lua_State* GetLuaState() { return m_State; }; - + /// /// Gets m_ScriptTimings. /// @@ -384,7 +386,7 @@ namespace RTE { /// /// A list of threaded script states. LuaStatesArray & GetThreadedScriptStates(); - + /// /// Gets the current thread lua state override that new objects created will be assigned to. /// @@ -435,6 +437,15 @@ namespace RTE { #pragma endregion #pragma region File I/O Handling + /// + /// If a file "foo/Bar.txt" exists, and this method is passed "FOO/BAR.TXT", then this method will return "foo/Bar.txt". + /// This method's purpose is to enable Linux to get the real path using a case-insensitive search. + /// The real path is used by the Lua file I/O handling methods to ensure full Windows compatibility. + /// + /// Path to case-insensitively translate to a real path. + /// An optional that contains the real path, if it existed. + std::optional GetCaseInsensitiveFullPath(const std::string &fullPath); + /// /// Returns a vector of all the directories in path, which is relative to the working directory. /// From ba1922e6845f56286fcba714acb7d2da38f723ba Mon Sep 17 00:00:00 2001 From: MyNameIsTrez Date: Sun, 14 Jan 2024 21:17:25 +0100 Subject: [PATCH 05/10] Pass all Lua tests --- Source/Managers/LuaMan.cpp | 155 ++++++++++--------------------------- Source/Managers/LuaMan.h | 6 +- 2 files changed, 45 insertions(+), 116 deletions(-) diff --git a/Source/Managers/LuaMan.cpp b/Source/Managers/LuaMan.cpp index 9515b6ff7a..ad39b220a9 100644 --- a/Source/Managers/LuaMan.cpp +++ b/Source/Managers/LuaMan.cpp @@ -940,7 +940,7 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TODO: Ask Causeless whether this is an appropriate spot for this method - std::optional LuaMan::GetCaseInsensitiveFullPath(const std::string &fullPath) { + std::string LuaMan::GetCaseInsensitiveFullPath(const std::string &fullPath) { std::filesystem::path inspectedPath = System::GetWorkingDirectory(); const std::filesystem::path relativeFilePath = std::filesystem::path(fullPath).lexically_relative(inspectedPath); @@ -961,16 +961,16 @@ namespace RTE { } if (!pathPartExists) { - // TODO: This should return the same thing DirectoryCreate() does, - // where it just appends the rest of fullPath. - // This way it's the responsibility of the caller to use std::filestem::exists(), - // which allows DirectoryCreate() and Rename() to call this function too, - // since they allow the fullPath and newPath argument to not exist yet. - return NOTasdstd::nullopt; + // If part of the path exists, append the rest of fullPath its parts + while (relativeFilePathIterator != relativeFilePath.end()) { + inspectedPath /= relativeFilePathIterator->generic_string(); + relativeFilePathIterator++; + } + break; } } - return std::optional(inspectedPath.generic_string()); + return inspectedPath.generic_string(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -981,13 +981,10 @@ namespace RTE { if (IsValidModulePath(fullPath)) { #ifndef _WIN32 - auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath); - if (caseInsensitiveFullPath) + fullPath = GetCaseInsensitiveFullPath(fullPath); #endif + if (std::filesystem::exists(fullPath)) { -#ifndef _WIN32 - fullPath = *caseInsensitiveFullPath; -#endif for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(fullPath)) { if (directoryEntry.is_directory()) { directoryPaths->emplace_back(directoryEntry.path().filename().generic_string()); } } @@ -1004,13 +1001,10 @@ namespace RTE { if (IsValidModulePath(fullPath)) { #ifndef _WIN32 - auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath); - if (caseInsensitiveFullPath) + fullPath = GetCaseInsensitiveFullPath(fullPath); #endif + if (std::filesystem::exists(fullPath)) { -#ifndef _WIN32 - fullPath = *caseInsensitiveFullPath; -#endif for (const std::filesystem::directory_entry &directoryEntry : std::filesystem::directory_iterator(fullPath)) { if (directoryEntry.is_regular_file()) { filePaths->emplace_back(directoryEntry.path().filename().generic_string()); } } @@ -1025,15 +1019,9 @@ namespace RTE { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { #ifndef _WIN32 - auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath); - if (caseInsensitiveFullPath) + fullPath = GetCaseInsensitiveFullPath(fullPath); #endif - { -#ifndef _WIN32 - fullPath = *caseInsensitiveFullPath; -#endif - return std::filesystem::is_regular_file(fullPath); - } + return std::filesystem::is_regular_file(fullPath); } return false; } @@ -1044,15 +1032,9 @@ namespace RTE { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { #ifndef _WIN32 - auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath); - if (caseInsensitiveFullPath) -#endif - { -#ifndef _WIN32 - fullPath = *caseInsensitiveFullPath; + fullPath = GetCaseInsensitiveFullPath(fullPath); #endif - return std::filesystem::is_directory(fullPath); - } + return std::filesystem::is_directory(fullPath); } return false; } @@ -1156,16 +1138,10 @@ namespace RTE { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { #ifndef _WIN32 - auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath); - if (caseInsensitiveFullPath) -#endif - { -#ifndef _WIN32 - fullPath = *caseInsensitiveFullPath; + fullPath = GetCaseInsensitiveFullPath(fullPath); #endif - if (std::filesystem::is_regular_file(fullPath)) { - return std::filesystem::remove(fullPath); - } + if (std::filesystem::is_regular_file(fullPath)) { + return std::filesystem::remove(fullPath); } } g_ConsoleMan.PrintString("ERROR: Failed to remove file " + path); @@ -1178,49 +1154,15 @@ namespace RTE { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { #ifndef _WIN32 - // The goal here is to return the same fullPath, - // but with the parent directories given the real filesys casing - std::filesystem::path inspectedPath = System::GetWorkingDirectory(); - const std::filesystem::path relativeFilePath = std::filesystem::path(fullPath).lexically_relative(inspectedPath); - - // Iterate over all path parts - for (std::filesystem::path::const_iterator relativeFilePathIterator = relativeFilePath.begin(); relativeFilePathIterator != relativeFilePath.end(); ++relativeFilePathIterator) { - bool pathPartExists = false; - - // Iterate over all entries in the path part's directory, - // to check if the path part is in there case insensitively - for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) { - if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) { - inspectedPath = filesystemEntryPath; - - // If the path part is found, stop looking for it - pathPartExists = true; - break; - } - } - - if (!pathPartExists) { - // If part of the path exists, append the rest of fullPath its parts - while (relativeFilePathIterator != relativeFilePath.end()) { - inspectedPath /= relativeFilePathIterator->generic_string(); - relativeFilePathIterator++; - } - - break; - } - } - - fullPath = inspectedPath; + fullPath = GetCaseInsensitiveFullPath(fullPath); #endif - { - try { - if (recursive) { - return std::filesystem::create_directories(fullPath); - } else { - return std::filesystem::create_directory(fullPath); - } - } catch (const std::filesystem::filesystem_error &e) {} - } + try { + if (recursive) { + return std::filesystem::create_directories(fullPath); + } else { + return std::filesystem::create_directory(fullPath); + } + } catch (const std::filesystem::filesystem_error &e) {} } g_ConsoleMan.PrintString("ERROR: Failed to remove directory " + path); return false; @@ -1232,22 +1174,16 @@ namespace RTE { std::string fullPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(path); if (IsValidModulePath(fullPath)) { #ifndef _WIN32 - auto caseInsensitiveFullPath = GetCaseInsensitiveFullPath(fullPath); - if (caseInsensitiveFullPath) -#endif - { -#ifndef _WIN32 - fullPath = *caseInsensitiveFullPath; + fullPath = GetCaseInsensitiveFullPath(fullPath); #endif - if (std::filesystem::is_directory(fullPath)) { - try { - if (recursive) { - return std::filesystem::remove_all(fullPath) > 0; - } else { - return std::filesystem::remove(fullPath); - } - } catch (const std::filesystem::filesystem_error &e) {} - } + if (std::filesystem::is_directory(fullPath)) { + try { + if (recursive) { + return std::filesystem::remove_all(fullPath) > 0; + } else { + return std::filesystem::remove(fullPath); + } + } catch (const std::filesystem::filesystem_error &e) {} } } g_ConsoleMan.PrintString("ERROR: Failed to remove directory " + path); @@ -1261,20 +1197,13 @@ namespace RTE { std::string fullNewPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(newPath); if (IsValidModulePath(fullOldPath) && IsValidModulePath(fullNewPath)) { #ifndef _WIN32 - auto caseInsensitiveFullOldPath = GetCaseInsensitiveFullPath(fullOldPath); - auto caseInsensitiveFullNewPath = GetCaseInsensitiveFullPath(fullNewPath); - if (caseInsensitiveFullOldPath && caseInsensitiveFullNewPath) + fullOldPath = GetCaseInsensitiveFullPath(fullOldPath); + fullNewPath = GetCaseInsensitiveFullPath(fullNewPath); #endif - { -#ifndef _WIN32 - fullOldPath = *caseInsensitiveFullOldPath; - fullNewPath = *caseInsensitiveFullNewPath; -#endif - try { - std::filesystem::rename(fullOldPath, fullNewPath); - return true; - } catch (const std::filesystem::filesystem_error &e) {} - } + try { + std::filesystem::rename(fullOldPath, fullNewPath); + return true; + } catch (const std::filesystem::filesystem_error &e) {} } g_ConsoleMan.PrintString("ERROR: Failed to rename oldPath " + oldPath + " to newPath " + newPath); return false; diff --git a/Source/Managers/LuaMan.h b/Source/Managers/LuaMan.h index 204bc97776..1773b38d8e 100644 --- a/Source/Managers/LuaMan.h +++ b/Source/Managers/LuaMan.h @@ -443,8 +443,8 @@ namespace RTE { /// The real path is used by the Lua file I/O handling methods to ensure full Windows compatibility. /// /// Path to case-insensitively translate to a real path. - /// An optional that contains the real path, if it existed. - std::optional GetCaseInsensitiveFullPath(const std::string &fullPath); + /// The real path. If the path doesn't exist, it returns the fullPath argument with all the existing parent directories correctly capitalized. + std::string GetCaseInsensitiveFullPath(const std::string &fullPath); /// /// Returns a vector of all the directories in path, which is relative to the working directory. @@ -598,4 +598,4 @@ namespace RTE { LuaMan & operator=(const LuaMan &rhs) = delete; }; } -#endif \ No newline at end of file +#endif From a5872e1ad439b8e48f3c07b205b032cb08f582d5 Mon Sep 17 00:00:00 2001 From: MyNameIsTrez Date: Sun, 14 Jan 2024 22:11:10 +0100 Subject: [PATCH 06/10] Rename() consistency: don't allow newPath to exist --- Source/Managers/LuaMan.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Source/Managers/LuaMan.cpp b/Source/Managers/LuaMan.cpp index ad39b220a9..be1a11377e 100644 --- a/Source/Managers/LuaMan.cpp +++ b/Source/Managers/LuaMan.cpp @@ -1200,10 +1200,15 @@ namespace RTE { fullOldPath = GetCaseInsensitiveFullPath(fullOldPath); fullNewPath = GetCaseInsensitiveFullPath(fullNewPath); #endif - try { - std::filesystem::rename(fullOldPath, fullNewPath); - return true; - } catch (const std::filesystem::filesystem_error &e) {} + // Ensures parity between Linux which can overwrite an empty directory, while Windows can't + // Ensures parity between Linux which can't rename a directory to a newPath that is a file in order to overwrite it, while Windows can + if (!std::filesystem::exists(fullNewPath)) + { + try { + std::filesystem::rename(fullOldPath, fullNewPath); + return true; + } catch (const std::filesystem::filesystem_error &e) {} + } } g_ConsoleMan.PrintString("ERROR: Failed to rename oldPath " + oldPath + " to newPath " + newPath); return false; From e8aee1ccb4977cf7096ab243db0f262fcd8aa5b0 Mon Sep 17 00:00:00 2001 From: MyNameIsTrez Date: Sun, 14 Jan 2024 22:21:20 +0100 Subject: [PATCH 07/10] Add disclaimer to Rename() --- Source/Managers/LuaMan.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Managers/LuaMan.h b/Source/Managers/LuaMan.h index 1773b38d8e..a4461c84d3 100644 --- a/Source/Managers/LuaMan.h +++ b/Source/Managers/LuaMan.h @@ -526,6 +526,8 @@ namespace RTE { /// /// Moves or renames the filesystem object oldPath to newPath. + /// In order to get consistent behavior across Windows and Linux across all 4 combinations of oldPath and newPath being a directory/file, + /// the newPath isn't allowed to already exist. /// /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. From 80f86b97ca53409c2ef6b4213b099044432c4556 Mon Sep 17 00:00:00 2001 From: MyNameIsTrez Date: Sun, 14 Jan 2024 22:52:49 +0100 Subject: [PATCH 08/10] Split into RenameFile and RenameDirectory --- Source/Managers/LuaMan.cpp | 34 ++++++++++++++++++++++++++++++---- Source/Managers/LuaMan.h | 17 ++++++++++++++--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/Source/Managers/LuaMan.cpp b/Source/Managers/LuaMan.cpp index be1a11377e..f91ec6bb40 100644 --- a/Source/Managers/LuaMan.cpp +++ b/Source/Managers/LuaMan.cpp @@ -80,7 +80,8 @@ namespace RTE { .def("DirectoryCreate", &LuaStateWrapper::DirectoryCreate2) .def("DirectoryRemove", &LuaStateWrapper::DirectoryRemove1) .def("DirectoryRemove", &LuaStateWrapper::DirectoryRemove2) - .def("Rename", &LuaStateWrapper::Rename) + .def("FileRename", &LuaStateWrapper::FileRename) + .def("DirectoryRename", &LuaStateWrapper::DirectoryRename) .def("FileReadLine", &LuaStateWrapper::FileReadLine) .def("FileWriteLine", &LuaStateWrapper::FileWriteLine) .def("FileEOF", &LuaStateWrapper::FileEOF), @@ -294,7 +295,8 @@ namespace RTE { bool LuaStateWrapper::DirectoryCreate2(const std::string& path, bool recursive) { return g_LuaMan.DirectoryCreate(path, recursive); } bool LuaStateWrapper::DirectoryRemove1(const std::string& path) { return g_LuaMan.DirectoryRemove(path, false); } bool LuaStateWrapper::DirectoryRemove2(const std::string& path, bool recursive) { return g_LuaMan.DirectoryRemove(path, recursive); } - bool LuaStateWrapper::Rename(const std::string& oldPath, const std::string& newPath) { return g_LuaMan.Rename(oldPath, newPath); } + bool LuaStateWrapper::FileRename(const std::string& oldPath, const std::string& newPath) { return g_LuaMan.FileRename(oldPath, newPath); } + bool LuaStateWrapper::DirectoryRename(const std::string& oldPath, const std::string& newPath) { return g_LuaMan.DirectoryRename(oldPath, newPath); } std::string LuaStateWrapper::FileReadLine(int fileIndex) { return g_LuaMan.FileReadLine(fileIndex); } void LuaStateWrapper::FileWriteLine(int fileIndex, const std::string& line) { return g_LuaMan.FileWriteLine(fileIndex, line); } bool LuaStateWrapper::FileEOF(int fileIndex) { return g_LuaMan.FileEOF(fileIndex); } @@ -1192,7 +1194,7 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool LuaMan::Rename(const std::string& oldPath, const std::string& newPath) { + bool LuaMan::FileRename(const std::string& oldPath, const std::string& newPath) { std::string fullOldPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(oldPath); std::string fullNewPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(newPath); if (IsValidModulePath(fullOldPath) && IsValidModulePath(fullNewPath)) { @@ -1202,7 +1204,31 @@ namespace RTE { #endif // Ensures parity between Linux which can overwrite an empty directory, while Windows can't // Ensures parity between Linux which can't rename a directory to a newPath that is a file in order to overwrite it, while Windows can - if (!std::filesystem::exists(fullNewPath)) + if (std::filesystem::is_regular_file(fullOldPath) && !std::filesystem::exists(fullNewPath)) + { + try { + std::filesystem::rename(fullOldPath, fullNewPath); + return true; + } catch (const std::filesystem::filesystem_error &e) {} + } + } + g_ConsoleMan.PrintString("ERROR: Failed to rename oldPath " + oldPath + " to newPath " + newPath); + return false; + } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + bool LuaMan::DirectoryRename(const std::string& oldPath, const std::string& newPath) { + std::string fullOldPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(oldPath); + std::string fullNewPath = System::GetWorkingDirectory() + g_PresetMan.GetFullModulePath(newPath); + if (IsValidModulePath(fullOldPath) && IsValidModulePath(fullNewPath)) { +#ifndef _WIN32 + fullOldPath = GetCaseInsensitiveFullPath(fullOldPath); + fullNewPath = GetCaseInsensitiveFullPath(fullNewPath); +#endif + // Ensures parity between Linux which can overwrite an empty directory, while Windows can't + // Ensures parity between Linux which can't rename a directory to a newPath that is a file in order to overwrite it, while Windows can + if (std::filesystem::is_directory(fullOldPath) && !std::filesystem::exists(fullNewPath)) { try { std::filesystem::rename(fullOldPath, fullNewPath); diff --git a/Source/Managers/LuaMan.h b/Source/Managers/LuaMan.h index a4461c84d3..fa3fcf5e03 100644 --- a/Source/Managers/LuaMan.h +++ b/Source/Managers/LuaMan.h @@ -299,7 +299,8 @@ namespace RTE { bool DirectoryCreate2(const std::string& path, bool recursive); bool DirectoryRemove1(const std::string& path); bool DirectoryRemove2(const std::string& path, bool recursive); - bool Rename(const std::string& oldPath, const std::string& newPath); + bool FileRename(const std::string& oldPath, const std::string& newPath); + bool DirectoryRename(const std::string& oldPath, const std::string& newPath); std::string FileReadLine(int fileIndex); void FileWriteLine(int fileIndex, const std::string& line); bool FileEOF(int fileIndex); @@ -525,14 +526,24 @@ namespace RTE { bool DirectoryRemove(const std::string &path, bool recursive); /// - /// Moves or renames the filesystem object oldPath to newPath. + /// Moves or renames the file oldPath to newPath. /// In order to get consistent behavior across Windows and Linux across all 4 combinations of oldPath and newPath being a directory/file, /// the newPath isn't allowed to already exist. /// /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. /// Whether or not renaming succeeded. - bool Rename(const std::string &oldPath, const std::string &newPath); + bool FileRename(const std::string &oldPath, const std::string &newPath); + + /// + /// Moves or renames the directory oldPath to newPath. + /// In order to get consistent behavior across Windows and Linux across all 4 combinations of oldPath and newPath being a directory/file, + /// the newPath isn't allowed to already exist. + /// + /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. + /// Path to the filesystem object. All paths are made absolute by adding current working directory to the specified path. + /// Whether or not renaming succeeded. + bool DirectoryRename(const std::string &oldPath, const std::string &newPath); /// /// Reads a line from a file. From 71ee722a90feb8aa525eede5dfbfbc3db879db91 Mon Sep 17 00:00:00 2001 From: MyNameIsTrez Date: Sun, 14 Jan 2024 22:59:59 +0100 Subject: [PATCH 09/10] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cec94b1c5a..51745d57a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -120,6 +120,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - New `GameActivity::LockControlledActor` Lua function to allow grab player input in the way menus (buy menu/full inventorymenu) do. +- New `LuaMan` Lua I/O functions `DirectoryCreate`, `FileExists`, `DirectoryExists`, `FileRemove`, `DirectoryRemove`, `FileRename`, `DirectoryRename` and `IsValidModulePath`. +
Changed From 629842ba21487132285b06784bc1fb6606e64689 Mon Sep 17 00:00:00 2001 From: MyNameIsTrez Date: Mon, 15 Jan 2024 08:40:05 +0100 Subject: [PATCH 10/10] Move GetCaseInsensitiveFullPath() to RTETools Remove vestigial #include --- Source/Managers/LuaMan.cpp | 36 ------------------------------------ Source/Managers/LuaMan.h | 11 ----------- Source/System/RTETools.cpp | 35 +++++++++++++++++++++++++++++++++++ Source/System/RTETools.h | 9 +++++++++ 4 files changed, 44 insertions(+), 47 deletions(-) diff --git a/Source/Managers/LuaMan.cpp b/Source/Managers/LuaMan.cpp index 6c078fbefe..6f7e86537d 100644 --- a/Source/Managers/LuaMan.cpp +++ b/Source/Managers/LuaMan.cpp @@ -939,42 +939,6 @@ namespace RTE { return stackDescription.str(); } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - // TODO: Ask Causeless whether this is an appropriate spot for this method - std::string LuaMan::GetCaseInsensitiveFullPath(const std::string &fullPath) { - std::filesystem::path inspectedPath = System::GetWorkingDirectory(); - const std::filesystem::path relativeFilePath = std::filesystem::path(fullPath).lexically_relative(inspectedPath); - - // Iterate over all path parts - for (std::filesystem::path::const_iterator relativeFilePathIterator = relativeFilePath.begin(); relativeFilePathIterator != relativeFilePath.end(); ++relativeFilePathIterator) { - bool pathPartExists = false; - - // Iterate over all entries in the path part's directory, - // to check if the path part is in there case insensitively - for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) { - if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) { - inspectedPath = filesystemEntryPath; - - // If the path part is found, stop looking for it - pathPartExists = true; - break; - } - } - - if (!pathPartExists) { - // If part of the path exists, append the rest of fullPath its parts - while (relativeFilePathIterator != relativeFilePath.end()) { - inspectedPath /= relativeFilePathIterator->generic_string(); - relativeFilePathIterator++; - } - break; - } - } - - return inspectedPath.generic_string(); - } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const std::vector * LuaMan::DirectoryList(const std::string &path) { diff --git a/Source/Managers/LuaMan.h b/Source/Managers/LuaMan.h index fa3fcf5e03..d6358c615d 100644 --- a/Source/Managers/LuaMan.h +++ b/Source/Managers/LuaMan.h @@ -8,8 +8,6 @@ #include "BS_thread_pool.hpp" -#include - #define g_LuaMan LuaMan::Instance() struct lua_State; @@ -438,15 +436,6 @@ namespace RTE { #pragma endregion #pragma region File I/O Handling - /// - /// If a file "foo/Bar.txt" exists, and this method is passed "FOO/BAR.TXT", then this method will return "foo/Bar.txt". - /// This method's purpose is to enable Linux to get the real path using a case-insensitive search. - /// The real path is used by the Lua file I/O handling methods to ensure full Windows compatibility. - /// - /// Path to case-insensitively translate to a real path. - /// The real path. If the path doesn't exist, it returns the fullPath argument with all the existing parent directories correctly capitalized. - std::string GetCaseInsensitiveFullPath(const std::string &fullPath); - /// /// Returns a vector of all the directories in path, which is relative to the working directory. /// diff --git a/Source/System/RTETools.cpp b/Source/System/RTETools.cpp index 5c1af24ee3..81f36c635c 100644 --- a/Source/System/RTETools.cpp +++ b/Source/System/RTETools.cpp @@ -218,4 +218,39 @@ namespace RTE { return hash; } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + std::string GetCaseInsensitiveFullPath(const std::string &fullPath) { + std::filesystem::path inspectedPath = System::GetWorkingDirectory(); + const std::filesystem::path relativeFilePath = std::filesystem::path(fullPath).lexically_relative(inspectedPath); + + // Iterate over all path parts + for (std::filesystem::path::const_iterator relativeFilePathIterator = relativeFilePath.begin(); relativeFilePathIterator != relativeFilePath.end(); ++relativeFilePathIterator) { + bool pathPartExists = false; + + // Iterate over all entries in the path part's directory, + // to check if the path part is in there case insensitively + for (const std::filesystem::path &filesystemEntryPath : std::filesystem::directory_iterator(inspectedPath)) { + if (StringsEqualCaseInsensitive(filesystemEntryPath.filename().generic_string(), relativeFilePathIterator->generic_string())) { + inspectedPath = filesystemEntryPath; + + // If the path part is found, stop looking for it + pathPartExists = true; + break; + } + } + + if (!pathPartExists) { + // If part of the path exists, append the rest of fullPath its parts + while (relativeFilePathIterator != relativeFilePath.end()) { + inspectedPath /= relativeFilePathIterator->generic_string(); + relativeFilePathIterator++; + } + break; + } + } + + return inspectedPath.generic_string(); + } } diff --git a/Source/System/RTETools.h b/Source/System/RTETools.h index 9637691d35..b69b4fdcbd 100644 --- a/Source/System/RTETools.h +++ b/Source/System/RTETools.h @@ -292,6 +292,15 @@ namespace RTE { /// Whether the two strings are equal case insensitively. inline bool StringsEqualCaseInsensitive(const std::string_view &strA, const std::string_view &strB) { return std::equal(strA.begin(), strA.end(), strB.begin(), strB.end(), [](char strAChar, char strBChar) { return std::tolower(strAChar) == std::tolower(strBChar); }); } + /// + /// If a file "foo/Bar.txt" exists, and this method is passed "FOO/BAR.TXT", then this method will return "foo/Bar.txt". + /// This method's purpose is to enable Linux to get the real path using a case-insensitive search. + /// The real path is used by the Lua file I/O handling methods to ensure full Windows compatibility. + /// + /// Path to case-insensitively translate to a real path. + /// The real path. If the path doesn't exist, it returns the fullPath argument with all the existing parent directories correctly capitalized. + std::string GetCaseInsensitiveFullPath(const std::string &fullPath); + /// /// Hashes a string in a cross-compiler/platform safe way (std::hash gives different results on different compilers). ///