From ede8de92f6d98c704a8df96f031f6a72724fe0c3 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sat, 1 Aug 2020 14:00:58 +1000 Subject: [PATCH] FileSystem: Use wide strings for FindFiles and StatFile on Windows --- src/common/file_system.cpp | 60 +++++++++++++++++++++++++------------- src/common/string_util.cpp | 44 +++++++++++++++++++--------- src/common/string_util.h | 2 ++ 3 files changed, 73 insertions(+), 33 deletions(-) diff --git a/src/common/file_system.cpp b/src/common/file_system.cpp index dbe854dc0..2cd17ba1d 100644 --- a/src/common/file_system.cpp +++ b/src/common/file_system.cpp @@ -700,8 +700,8 @@ static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, co tempStr = StringUtil::StdStringFromFormat("%s\\*", OriginPath); } - WIN32_FIND_DATA wfd; - HANDLE hFind = FindFirstFileA(tempStr.c_str(), &wfd); + WIN32_FIND_DATAW wfd; + HANDLE hFind = FindFirstFileW(StringUtil::UTF8StringToWideString(tempStr).c_str(), &wfd); if (hFind == INVALID_HANDLE_VALUE) return 0; @@ -715,21 +715,28 @@ static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, co wildCardMatchAll = !(std::strcmp(Pattern, "*")); } + // holder for utf-8 conversion + std::string utf8_filename; + utf8_filename.reserve(countof(wfd.cFileName) * 2); + // iterate results do { if (wfd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN && !(Flags & FILESYSTEM_FIND_HIDDEN_FILES)) continue; - if (wfd.cFileName[0] == '.') + if (wfd.cFileName[0] == L'.') { - if (wfd.cFileName[1] == '\0' || (wfd.cFileName[1] == '.' && wfd.cFileName[2] == '\0')) + if (wfd.cFileName[1] == L'\0' || (wfd.cFileName[1] == L'.' && wfd.cFileName[2] == L'\0')) continue; if (!(Flags & FILESYSTEM_FIND_HIDDEN_FILES)) continue; } + if (!StringUtil::WideStringToUTF8String(utf8_filename, wfd.cFileName)) + continue; + FILESYSTEM_FIND_DATA outData; outData.Attributes = 0; @@ -741,11 +748,11 @@ static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, co if (ParentPath != nullptr) { const std::string recurseDir = StringUtil::StdStringFromFormat("%s\\%s", ParentPath, Path); - nFiles += RecursiveFindFiles(OriginPath, recurseDir.c_str(), wfd.cFileName, Pattern, Flags, pResults); + nFiles += RecursiveFindFiles(OriginPath, recurseDir.c_str(), utf8_filename.c_str(), Pattern, Flags, pResults); } else { - nFiles += RecursiveFindFiles(OriginPath, Path, wfd.cFileName, Pattern, Flags, pResults); + nFiles += RecursiveFindFiles(OriginPath, Path, utf8_filename.c_str(), Pattern, Flags, pResults); } } @@ -766,12 +773,12 @@ static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, co // match the filename if (hasWildCards) { - if (!wildCardMatchAll && !StringUtil::WildcardMatch(wfd.cFileName, Pattern)) + if (!wildCardMatchAll && !StringUtil::WildcardMatch(utf8_filename.c_str(), Pattern)) continue; } else { - if (std::strcmp(wfd.cFileName, Pattern) != 0) + if (std::strcmp(utf8_filename.c_str(), Pattern) != 0) continue; } @@ -781,20 +788,20 @@ static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, co { if (ParentPath != nullptr) outData.FileName = - StringUtil::StdStringFromFormat("%s\\%s\\%s\\%s", OriginPath, ParentPath, Path, wfd.cFileName); + StringUtil::StdStringFromFormat("%s\\%s\\%s\\%s", OriginPath, ParentPath, Path, utf8_filename.c_str()); else if (Path != nullptr) - outData.FileName = StringUtil::StdStringFromFormat("%s\\%s\\%s", OriginPath, Path, wfd.cFileName); + outData.FileName = StringUtil::StdStringFromFormat("%s\\%s\\%s", OriginPath, Path, utf8_filename.c_str()); else - outData.FileName = StringUtil::StdStringFromFormat("%s\\%s", OriginPath, wfd.cFileName); + outData.FileName = StringUtil::StdStringFromFormat("%s\\%s", OriginPath, utf8_filename.c_str()); } else { if (ParentPath != nullptr) - outData.FileName = StringUtil::StdStringFromFormat("%s\\%s\\%s", ParentPath, Path, wfd.cFileName); + outData.FileName = StringUtil::StdStringFromFormat("%s\\%s\\%s", ParentPath, Path, utf8_filename.c_str()); else if (Path != nullptr) - outData.FileName = StringUtil::StdStringFromFormat("%s\\%s", Path, wfd.cFileName); + outData.FileName = StringUtil::StdStringFromFormat("%s\\%s", Path, utf8_filename.c_str()); else - outData.FileName = wfd.cFileName; + outData.FileName = utf8_filename; } outData.ModificationTime.SetWindowsFileTime(&wfd.ftLastWriteTime); @@ -802,7 +809,7 @@ static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, co nFiles++; pResults->push_back(std::move(outData)); - } while (FindNextFileA(hFind, &wfd) == TRUE); + } while (FindNextFileW(hFind, &wfd) == TRUE); FindClose(hFind); return nFiles; @@ -822,14 +829,27 @@ bool FileSystem::FindFiles(const char* Path, const char* Pattern, u32 Flags, Fin return (RecursiveFindFiles(Path, nullptr, nullptr, Pattern, Flags, pResults) > 0); } -bool FileSystem::StatFile(const char* Path, FILESYSTEM_STAT_DATA* pStatData) +bool FileSystem::StatFile(const char* path, FILESYSTEM_STAT_DATA* pStatData) { // has a path - if (Path[0] == '\0') + if (path[0] == '\0') return false; + // convert to wide string + int len = static_cast(std::strlen(path)); + int wlen = MultiByteToWideChar(CP_UTF8, 0, path, len, nullptr, 0); + if (wlen <= 0) + return false; + + wchar_t* wpath = static_cast(alloca(sizeof(wchar_t) * (wlen + 1))); + wlen = MultiByteToWideChar(CP_UTF8, 0, path, len, wpath, wlen); + if (wlen <= 0) + return false; + + wpath[wlen] = 0; + // determine attributes for the path. if it's a directory, things have to be handled differently.. - DWORD fileAttributes = GetFileAttributesA(Path); + DWORD fileAttributes = GetFileAttributesW(wpath); if (fileAttributes == INVALID_FILE_ATTRIBUTES) return false; @@ -837,12 +857,12 @@ bool FileSystem::StatFile(const char* Path, FILESYSTEM_STAT_DATA* pStatData) HANDLE hFile; if (fileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - hFile = CreateFileA(Path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, + hFile = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); } else { - hFile = CreateFileA(Path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, + hFile = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, 0, nullptr); } diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index a9863bde7..420830d25 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -167,33 +167,51 @@ std::size_t Strlcpy(char* dst, const std::string_view& src, std::size_t size) std::wstring UTF8StringToWideString(const std::string_view& str) { - int wlen = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.length()), nullptr, 0); - if (wlen <= 0) - return {}; - std::wstring ret; - ret.resize(wlen); - if (MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.length()), ret.data(), wlen) <= 0) + if (!UTF8StringToWideString(ret, str)) return {}; return ret; } +bool UTF8StringToWideString(std::wstring& dest, const std::string_view& str) +{ + int wlen = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.length()), nullptr, 0); + if (wlen < 0) + return false; + + dest.resize(wlen); + if (wlen > 0 && MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.length()), dest.data(), wlen) < 0) + return false; + + return true; +} + std::string WideStringToUTF8String(const std::wstring_view& str) { - int mblen = WideCharToMultiByte(CP_UTF8, 0, str.data(), static_cast(str.length()), nullptr, 0, nullptr, nullptr); - if (mblen <= 0) - return {}; - std::string ret; - ret.resize(mblen); - if (WideCharToMultiByte(CP_UTF8, 0, str.data(), static_cast(str.length()), ret.data(), mblen, nullptr, nullptr) < - 0) + if (!WideStringToUTF8String(ret, str)) return {}; return ret; } +bool WideStringToUTF8String(std::string& dest, const std::wstring_view& str) +{ + int mblen = WideCharToMultiByte(CP_UTF8, 0, str.data(), static_cast(str.length()), nullptr, 0, nullptr, nullptr); + if (mblen < 0) + return false; + + dest.resize(mblen); + if (mblen > 0 && WideCharToMultiByte(CP_UTF8, 0, str.data(), static_cast(str.length()), dest.data(), mblen, + nullptr, nullptr) < 0) + { + return false; + } + + return true; +} + #endif } // namespace StringUtil diff --git a/src/common/string_util.h b/src/common/string_util.h index d2058e644..891c689b8 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h @@ -117,9 +117,11 @@ ALWAYS_INLINE static bool StartsWith(const std::string_view& str, const char* pr /// Converts the specified UTF-8 string to a wide string. std::wstring UTF8StringToWideString(const std::string_view& str); +bool UTF8StringToWideString(std::wstring& dest, const std::string_view& str); /// Converts the specified wide string to a UTF-8 string. std::string WideStringToUTF8String(const std::wstring_view& str); +bool WideStringToUTF8String(std::string& dest, const std::wstring_view& str); #endif