From 0f1572d8333712f6ecae682a848f2a3f24be1721 Mon Sep 17 00:00:00 2001 From: juvinious Date: Thu, 1 Feb 2024 13:44:04 -0500 Subject: [PATCH] windows filesystem fixes (#67) Co-authored-by: juvinious --- .gitignore | 8 +- src/r-tech1/file-system.cpp | 217 ++++++++++++++++++++-------------- src/r-tech1/system.cpp | 70 +++++++++-- src/test/thread/futures.cpp | 5 + src/test/thread/meson.build | 2 +- src/test/util/file-system.cpp | 5 +- 6 files changed, 205 insertions(+), 102 deletions(-) diff --git a/.gitignore b/.gitignore index 94c5a7720..4f9249071 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ -build -./paintown +build* +mingw-bin +paintown .scon* .tmp peg_*.py @@ -8,4 +9,5 @@ peg_*.py *.log *.swp # mac -.DS_Store \ No newline at end of file +.DS_Store +.vscode diff --git a/src/r-tech1/file-system.cpp b/src/r-tech1/file-system.cpp index 3cd432292..263ce71fa 100644 --- a/src/r-tech1/file-system.cpp +++ b/src/r-tech1/file-system.cpp @@ -32,7 +32,9 @@ #include "libs/7z/7zAlloc.h" #include "libs/7z/7zCrc.h" -//#ifdef WINDOWS +#ifdef WINDOWS +#include +#endif // https://github.com/gulrak/filesystem/ #include "libs/filesystem/fs-wrapper.h" #include "libs/filesystem/glob.h" @@ -462,20 +464,27 @@ System::System(){ System::~System(){ } +static int FileCounter = 0; vector System::getFiles(const Filesystem::AbsolutePath & dataPath, const Filesystem::RelativePath & find, bool caseInsensitive){ + DebugLog3 << "Is find: " << find.path() << " a file? (" << find.isFile() << ")" << std::endl; if (find.isFile()){ - DebugLog2 << "File (" << find.getFilename().path() << ") passed returning directory: " << find.path() << std::endl; - return getFiles(dataPath, find.path(), caseInsensitive); + DebugLog3 << "File (" << find.getFilename().path() << ") passed returning directory: " << find.path() << std::endl; + std::vector files = getFiles(dataPath, find.path(), caseInsensitive); + DebugLog3 << "Got total files: " << files.size() << " iteration: " << FileCounter << std::endl; + FileCounter++; + return files; } /* split the path into its consituent parts - * a/b/c -> a/b and c - * search for a/b, then search for c in the results - */ + * a/b/c -> a/b and c + * search for a/b, then search for c in the results + */ Filesystem::RelativePath directory = find.getDirectory(); Filesystem::RelativePath file = find.getFilename(); + DebugLog3 << "Checking directory: " << directory.path() << " for file: " << file.path() << std::endl; vector more = getFiles(dataPath, directory, caseInsensitive); + DebugLog3 << "Total files: " << more.size() << " found." << std::endl; vector out; for (vector::iterator it = more.begin(); it != more.end(); it++){ Filesystem::AbsolutePath path = *it; @@ -1729,104 +1738,134 @@ vector Filesystem::getFiles(const AbsolutePath & dataP vector more = virtualDirectory.findFiles(dataPath, find, caseInsensitive); files.insert(files.end(), more.begin(), more.end()); -// FIXME Can't seem to get glob to work in windows, it returns 0 files try { #ifndef WINDOWS - // Convert to type std::filesystem::path - //fs::path path = dataPath.path(); - //path /= find; - - DebugLog2 << "Looking for file: " << find << " in dataPath: " << dataPath.path() << " (" << find << ")" << std::endl; - // for (fs::path & globFile : glob::glob(path.generic_string())){ - for (fs::path & globFile : glob::glob1(dataPath.path(), find, false)){ - DebugLog2 << "Got datapath: " << dataPath.path().c_str() << " globFile: " << globFile.c_str() << std::endl; - files.push_back(AbsolutePath(dataPath.join(Filesystem::RelativePath(globFile.string())))); - } -#else - DebugLog2 << "Looking for file: " << find << " in dataPath: " << dataPath.path() << " (" << dataPath.path() + "/" + find << ")" << std::endl; - class Globber - { - public: - Globber(const std::string & pattern, const std::string & path): - pattern(createRegex(pattern)), - origin(path){ - doMatches(path); + // Convert to type std::filesystem::path + //fs::path path = dataPath.path(); + //path /= find; + + DebugLog2 << "Looking for file: " << find << " in dataPath: " << dataPath.path() << " (" << find << ")" << std::endl; + // for (fs::path & globFile : glob::glob(path.generic_string())){ + for (fs::path & globFile : glob::glob1(dataPath.path(), find, false)){ + DebugLog2 << "Got datapath: " << dataPath.path().c_str() << " globFile: " << globFile.c_str() << std::endl; + files.push_back(AbsolutePath(dataPath.join(Filesystem::RelativePath(globFile.string())))); } - ~Globber(){ - } - std::vector matches; - private: - void doMatches(const std::string & path){ - try { - for (const fs::directory_entry & entry : fs::directory_iterator(path)){ - fs::path relative_path = fs::relative(entry, origin); - DebugLog2 << "Entry: " << entry.path() << " relative path: " << relative_path << std::endl; - if (fs::is_directory(entry)){ - DebugLog2 << "Searching directory: " << entry.path() << std::endl; - doMatches(entry.path().string()); - } else if (fs::is_regular_file(entry) && std::regex_match(relative_path.string(), pattern)) { - matches.emplace_back(entry.path().string()); - DebugLog2 << "Found pattern match on entry: " << relative_path << std::endl; - } else { - DebugLog2 << "No match found for string: " << relative_path << std::endl; +#else + DebugLog2 << "Looking for file: " << find << " in dataPath: " << dataPath.path() << " (" << find << ")" << std::endl; + class Globber + { + private: + typedef struct { + std::string filename; + std::string path; + bool isDirectory; + } File; + public: + Globber(const std::string & pattern, const std::string & path): + pattern(createRegex(pattern)), + origin(path){ + doMatches(path); + } + ~Globber(){ + } + std::vector matches; + private: + void doMatches(const std::string & path){ + try { + + DebugLog3 << "String: " << origin << " path: " << path << std::endl; + for (const File & entry: this->getFiles(path)){ + DebugLog3 << "Entry: " << entry.filename << std::endl; + bool matched = std::regex_match(entry.filename, pattern); + + // Do not recurse + /* + if (entry.isDirectory && matched){ + //DebugLog2 << "Searching directory: " << entry.path() << std::endl; + DebugLog2 << "Searching directory: " << entry.filename << std::endl; + //doMatches(entry.path().string()); + doMatches(entry.path); + //} else if (fs::is_regular_file(entry) && std::regex_match(relative_path.string(), pattern)) { + } else if (matched){ + */ + if (matched){ + DebugLog3 << "Found pattern match on entry: " << entry.filename << std::endl; + matches.emplace_back(entry.filename); + } else { + DebugLog3 << "No match found for string: " << entry.path << std::endl; + } } + } catch (...){ + DebugLog2 << "Error parsing directories." << std::endl; } - } catch (...){ - DebugLog2 << "Error parsing directories." << std::endl; } - } - std::string createRegex(const std::string & glob){ - std::string regexStr = "^"; - for (char ch : glob) { - switch (ch) { - case '*': - regexStr += ".*"; - break; - case '?': - regexStr += "."; - break; - case '.': - regexStr += "\\."; - break; - default: - regexStr += ch; + // Function to iterate over a directory using Windows API + const std::vector getFiles(const std::string & path) { + //DebugLog << "Starting get files from path: " << path << std::endl; + std::vector files; + + WIN32_FIND_DATA findFileData; + HANDLE hFind = FindFirstFile((path + "\\*").c_str(), &findFileData); + + if (hFind != INVALID_HANDLE_VALUE) { + do { + File file = { + std::string(findFileData.cFileName), + path + "/" + std::string(findFileData.cFileName), + (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0 + }; + if (file.isDirectory && + (file.filename == "." || file.filename == "..")) { + // Skip directories . and .. + continue; + } + //DebugLog << "File: " << file.filename << std::endl; + files.emplace_back(file); + } while (FindNextFile(hFind, &findFileData) != 0); + + FindClose(hFind); + } else { + DebugLog2 << "Error opening directory: " << GetLastError() << std::endl; } + return files; } - regexStr += "$"; - DebugLog2 << "Regex string: " << regexStr << std::endl; - return regexStr; - } - std::regex pattern; - std::string origin; - }; + std::string createRegex(const std::string & glob){ + std::string regexStr = "^"; + for (char ch : glob) { + switch (ch) { + case '*': + regexStr += ".*"; + break; + case '?': + regexStr += "."; + break; + case '.': + regexStr += "\\."; + break; + default: + regexStr += ch; + } + } + regexStr += "$"; + DebugLog3 << "Regex string: " << regexStr << std::endl; + return regexStr; + } - Globber glob(find, dataPath.path()); - for (std::string globFile : glob.matches){ - DebugLog2 << "Got datapath: " << dataPath.path().c_str() << " globFile: " << globFile.c_str() << std::endl; - files.push_back(AbsolutePath(dataPath.join(Filesystem::RelativePath(globFile)))); - } + std::regex pattern; + std::string origin; + }; + Globber glob(find, dataPath.path()); + for (std::string globFile : glob.matches){ + DebugLog2 << "Got datapath: " << dataPath.path() << " globFile: " << globFile << std::endl; + files.emplace_back(AbsolutePath(dataPath.join(Filesystem::RelativePath(globFile)))); + } + DebugLog2 << "Got total matched files: " << files.size() << std::endl; #endif } catch(fs::filesystem_error &ex){ DebugLog2 << "Directory or dataPath: " << dataPath.path() << " does not exist. Reason: " << ex.what() << std::endl; } - /* - for (map >::iterator it = overlays.begin(); it != overlays.end(); it++){ - AbsolutePath path = it->first; - if (it->second == NULL){ - continue; - } - // Global::debug(0) << "Check " << path.path() << " (" << path.getDirectory().path() << ") vs directory " << dataPath.path() << " wildcard " << find << " to " << path.getFilename().path() << std::endl; - if (path.getDirectory() == dataPath && - file_matches(path.getLastComponent().c_str(), find.c_str())){ - // Global::debug(0) << "Found overlay " << path.path() << " in " << dataPath.path() << " for wildcard " << find << std::endl; - files.push_back(path); - } - } - */ - - // Global::debug(0) << "Warning: Filesystem::getFiles() is not implemented yet for SDL" << endl; return files; #endif } diff --git a/src/r-tech1/system.cpp b/src/r-tech1/system.cpp index 63654ee7f..3619832c6 100644 --- a/src/r-tech1/system.cpp +++ b/src/r-tech1/system.cpp @@ -3,6 +3,7 @@ #include #include #include +#include "r-tech1/debug.h" #ifdef USE_SDL #include @@ -121,34 +122,89 @@ void System::startMemoryUsage(){ #else -// FIXME For the time being on cross-build, needs to be corrected +uint64_t System::getModificationTime(const std::string & path){ +#ifndef WINDOWS + fs::file_time_type lastWriteTime = fs::last_write_time(filePath); + std::chrono::time_point timePoint = time_point_cast(lastWriteTime); + return static_cast(timePoint.time_since_epoch().count()); +#else + HANDLE hFile = CreateFile(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile != INVALID_HANDLE_VALUE) { + FILETIME modificationTime; + if (GetFileTime(hFile, nullptr, nullptr, &modificationTime)) { + CloseHandle(hFile); -uint64_t System::getModificationTime(const std::string & path){ - - // FIXME figure out how to convert to unit64_t and remove auto - //auto p = fs::path(path); - //auto time = fs::last_write_time(p); + ULARGE_INTEGER uli; + uli.LowPart = modificationTime.dwLowDateTime; + uli.HighPart = modificationTime.dwHighDateTime; - return 0; + return uli.QuadPart; + } else { + DebugLog2 << "Error getting file modification time: " << GetLastError() << std::endl; + CloseHandle(hFile); + return 0; // Return 0 or handle error as appropriate for your use case + } + } else { + DebugLog2 << "Error opening file: " << GetLastError() << std::endl; + return 0; // Return 0 or handle error as appropriate for your use case + } +#endif } void System::makeDirectory(const std::string & path){ +#ifndef WINDOWS fs::path p = fs::path(path); fs::create_directory(p); +#else + if (CreateDirectory(path.c_str(), nullptr) || ERROR_ALREADY_EXISTS == GetLastError()) { + return; + } else { + DebugLog2 << "Error creating directory: " << GetLastError() << std::endl; + } +#endif } bool System::isDirectory(const std::string & path){ +#ifndef WINDOWS return fs::is_directory(fs::path(path)); +#else + DWORD attributes = GetFileAttributes(path.c_str()); + if (attributes != INVALID_FILE_ATTRIBUTES) { + return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + } + return false; +#endif } bool System::readable(const std::string & path){ +#ifndef WINDOWS fs::path p = fs::path(path); fs::perms permissions = fs::status(p).permissions(); return ((permissions & fs::perms::owner_read) != fs::perms::none && (permissions & fs::perms::group_read) != fs::perms::none && (permissions & fs::perms::others_read) != fs::perms::none); +#else + DWORD attributes = GetFileAttributes(path.c_str()); + if (attributes != INVALID_FILE_ATTRIBUTES) { + // Check if the file is readable + if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + HANDLE hFile = CreateFile(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile != INVALID_HANDLE_VALUE) { + CloseHandle(hFile); + return true; + } else { + DebugLog2 << "Error opening file: " << GetLastError() << std::endl; + return false; + } + } + + // For directories, assume readable + return true; + } + return false; +#endif } uint64_t System::currentMilliseconds(){ diff --git a/src/test/thread/futures.cpp b/src/test/thread/futures.cpp index 6b86639f4..389a4dda8 100644 --- a/src/test/thread/futures.cpp +++ b/src/test/thread/futures.cpp @@ -43,7 +43,12 @@ void test2(){ } } +#ifndef WINDOWS int main(){ +#else +#include +int main(int argv, char *args[]){ +#endif test1(); test2(); } diff --git a/src/test/thread/meson.build b/src/test/thread/meson.build index ba031853d..d44c2ebd0 100644 --- a/src/test/thread/meson.build +++ b/src/test/thread/meson.build @@ -5,6 +5,6 @@ includes = include_directories('../..') executable('test-futures', futures_test, include_directories: [includes], dependencies: test_dependencies, - link_with: [rtech1_library], + link_with: [test_linkages, rtech1_library], link_args: test_link_args, ) diff --git a/src/test/util/file-system.cpp b/src/test/util/file-system.cpp index ef32a7ae3..769c606b2 100644 --- a/src/test/util/file-system.cpp +++ b/src/test/util/file-system.cpp @@ -56,8 +56,9 @@ void testGetFiles(){ DebugLog << "Setting relative path m*/*.txt" << std::endl; Filesystem::RelativePath relativePath = Filesystem::RelativePath("m*/*.txt"); std::vector paths = Storage::instance().getFiles(dataPath, relativePath, false); + DebugLog << "Got total matched files: " << paths.size() << std::endl; for (std::vector::iterator it = paths.begin(); it != paths.end(); it++){ - std::cout << it->path() << std::endl; + DebugLog << it->path() << std::endl; } } @@ -121,7 +122,7 @@ int main(){ #include int main(int argv, char *args[]){ #endif - Global::setDebug(3); + Global::setDebug(2); testGetFiles(); testZip(); testLastComponent();