From 1cd725586f0274dee5ebf26c0e30e018af0d96c6 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 28 Jun 2019 20:49:33 +0800 Subject: [PATCH] Add functions to handle file in utf8 encoded name --- lib/irrlicht/source/Irrlicht/CFileSystem.cpp | 62 +++------ lib/irrlicht/source/Irrlicht/CReadFile.cpp | 7 +- lib/irrlicht/source/Irrlicht/CWriteFile.cpp | 8 +- sources.cmake | 2 +- src/io/file_manager.cpp | 34 +++-- src/utils/file_utils.cpp | 129 +++++++++++++++++++ src/utils/file_utils.hpp | 51 ++++++++ 7 files changed, 226 insertions(+), 67 deletions(-) create mode 100644 src/utils/file_utils.cpp create mode 100644 src/utils/file_utils.hpp diff --git a/lib/irrlicht/source/Irrlicht/CFileSystem.cpp b/lib/irrlicht/source/Irrlicht/CFileSystem.cpp index 331804618..67e1a509d 100644 --- a/lib/irrlicht/source/Irrlicht/CFileSystem.cpp +++ b/lib/irrlicht/source/Irrlicht/CFileSystem.cpp @@ -498,16 +498,10 @@ const io::path& CFileSystem::getWorkingDirectory() #if defined(_IRR_WINDOWS_CE_PLATFORM_) // does not need this #elif defined(_IRR_WINDOWS_API_) - fschar_t tmp[_MAX_PATH]; - #if defined(_IRR_WCHAR_FILESYSTEM ) - _wgetcwd(tmp, _MAX_PATH); - WorkingDirectory[FILESYSTEM_NATIVE] = tmp; - WorkingDirectory[FILESYSTEM_NATIVE].replace(L'\\', L'/'); - #else - _getcwd(tmp, _MAX_PATH); - WorkingDirectory[FILESYSTEM_NATIVE] = tmp; - WorkingDirectory[FILESYSTEM_NATIVE].replace('\\', '/'); - #endif + wchar_t tmp[_MAX_PATH]; + _wgetcwd(tmp, _MAX_PATH); + WorkingDirectory[FILESYSTEM_NATIVE] = StringUtils::wideToUtf8(tmp).c_str(); + WorkingDirectory[FILESYSTEM_NATIVE].replace('\\', '/'); #endif #if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_)) @@ -572,18 +566,10 @@ bool CFileSystem::changeWorkingDirectoryTo(const io::path& newDirectory) #if defined(_IRR_WINDOWS_CE_PLATFORM_) success = true; -#elif defined(_MSC_VER) - #if defined(_IRR_WCHAR_FILESYSTEM) - success = (_wchdir(newDirectory.c_str()) == 0); - #else - success = (_chdir(newDirectory.c_str()) == 0); - #endif +#elif defined(_IRR_WINDOWS_API_) + success = (_wchdir(StringUtils::utf8ToWide(newDirectory.c_str()).c_str()) == 0); #else - #if defined(_IRR_WCHAR_FILESYSTEM) - success = (_wchdir(newDirectory.c_str()) == 0); - #else - success = (chdir(newDirectory.c_str()) == 0); - #endif + success = (chdir(newDirectory.c_str()) == 0); #endif } @@ -596,18 +582,12 @@ io::path CFileSystem::getAbsolutePath(const io::path& filename) const #if defined(_IRR_WINDOWS_CE_PLATFORM_) return filename; #elif defined(_IRR_WINDOWS_API_) - fschar_t *p=0; - fschar_t fpath[_MAX_PATH]; - #if defined(_IRR_WCHAR_FILESYSTEM ) - p = _wfullpath(fpath, filename.c_str(), _MAX_PATH); - core::stringw tmp(p); - tmp.replace(L'\\', L'/'); - #else - p = _fullpath(fpath, filename.c_str(), _MAX_PATH); - core::stringc tmp(p); - tmp.replace('\\', '/'); - #endif - return tmp; + wchar_t *p=0; + wchar_t fpath[_MAX_PATH]; + p = _wfullpath(fpath, StringUtils::utf8ToWide(filename.c_str()).c_str(), _MAX_PATH); + core::stringw tmp(p); + tmp.replace(L'\\', L'/'); + return StringUtils::wideToUtf8(tmp).c_str(); #elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_)) c8* p=0; c8 fpath[4096]; @@ -965,19 +945,15 @@ bool CFileSystem::existFile(const io::path& filename) const #else _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; #if defined(_MSC_VER) - #if defined(_IRR_WCHAR_FILESYSTEM) - return (_waccess(filename.c_str(), 0) != -1); - #else - return (_access(filename.c_str(), 0) != -1); - #endif + return (_waccess(StringUtils::utf8ToWide(filename.c_str()).c_str(), 0) != -1); #elif defined(F_OK) - #if defined(_IRR_WCHAR_FILESYSTEM) - return (_waccess(filename.c_str(), F_OK) != -1); - #else - return (access(filename.c_str(), F_OK) != -1); + #if defined(_IRR_WINDOWS_API_) + return (_waccess(StringUtils::utf8ToWide(filename.c_str()).c_str(), F_OK) != -1); + #else + return (access(filename.c_str(), F_OK) != -1); #endif #else - return (access(filename.c_str(), 0) != -1); + return (access(filename.c_str(), 0) != -1); #endif #endif } diff --git a/lib/irrlicht/source/Irrlicht/CReadFile.cpp b/lib/irrlicht/source/Irrlicht/CReadFile.cpp index 2f067e6f8..801754986 100644 --- a/lib/irrlicht/source/Irrlicht/CReadFile.cpp +++ b/lib/irrlicht/source/Irrlicht/CReadFile.cpp @@ -4,6 +4,7 @@ #include "CReadFile.h" +#include "utils/file_utils.hpp" namespace irr { namespace io @@ -73,11 +74,7 @@ void CReadFile::openFile() return; } -#if defined ( _IRR_WCHAR_FILESYSTEM ) - File = _wfopen(Filename.c_str(), L"rb"); -#else - File = fopen(Filename.c_str(), "rb"); -#endif + File = FileUtils::fopenU8Path(Filename.c_str(), "rb"); if (File) { diff --git a/lib/irrlicht/source/Irrlicht/CWriteFile.cpp b/lib/irrlicht/source/Irrlicht/CWriteFile.cpp index f72ef38aa..bcd5c5d72 100644 --- a/lib/irrlicht/source/Irrlicht/CWriteFile.cpp +++ b/lib/irrlicht/source/Irrlicht/CWriteFile.cpp @@ -3,6 +3,8 @@ // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CWriteFile.h" + +#include "utils/file_utils.hpp" #include namespace irr @@ -81,11 +83,7 @@ void CWriteFile::openFile(bool append) return; } -#if defined(_IRR_WCHAR_FILESYSTEM) - File = _wfopen(Filename.c_str(), append ? L"ab" : L"wb"); -#else - File = fopen(Filename.c_str(), append ? "ab" : "wb"); -#endif + File = FileUtils::fopenU8Path(Filename.c_str(), append ? "ab" : "wb"); if (File) { diff --git a/sources.cmake b/sources.cmake index ba4868d71..d4f28ae4d 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/io/file_manager.cpp b/src/io/file_manager.cpp index 6c3ffd507..cd82c0688 100644 --- a/src/io/file_manager.cpp +++ b/src/io/file_manager.cpp @@ -26,6 +26,7 @@ #include "karts/kart_properties_manager.hpp" #include "tracks/track_manager.hpp" #include "utils/command_line.hpp" +#include "utils/file_utils.hpp" #include "utils/log.hpp" #include "utils/string_utils.hpp" @@ -385,7 +386,7 @@ FileManager::~FileManager() continue; } struct stat mystat; - stat(full_path.c_str(), &mystat); + FileUtils::statU8Path(full_path, &mystat); StkTime::TimeType current = StkTime::getTimeSinceEpoch(); if(current - mystat.st_ctime <24*3600) { @@ -797,7 +798,7 @@ bool FileManager::checkAndCreateDirectory(const std::string &path) // Otherwise try to create the directory: #if defined(WIN32) && !defined(__CYGWIN__) - bool error = _mkdir(path.c_str()) != 0; + bool error = _wmkdir(StringUtils::utf8ToWide(path).c_str()) != 0; #else bool error = mkdir(path.c_str(), 0755) != 0; #endif @@ -1192,8 +1193,8 @@ void FileManager::setStdoutDir(const std::string& dir) if (!m_stdout_dir.empty() && m_stdout_dir[m_stdout_dir.size() - 1] != '/') { - m_stdout_dir += "/"; - } + m_stdout_dir += "/"; + } } // setStdoutDir //----------------------------------------------------------------------------- @@ -1215,7 +1216,7 @@ void FileManager::redirectOutput() out_new << logoutfile << "." << i-1; if(fileExists(out_new.str())) { - rename(out_new.str().c_str(), out_old.str().c_str()); + FileUtils::renameU8Path(out_new.str(), out_old.str()); } } // for i in NUM_BACKUPS @@ -1224,7 +1225,7 @@ void FileManager::redirectOutput() std::ostringstream out; out << logoutfile<<".1"; // No good place to log error messages when log is not yet initialised - rename(logoutfile.c_str(), out.str().c_str()); + FileUtils::renameU8Path(logoutfile, out.str()); } //Enable logging of stdout and stderr to logfile @@ -1313,7 +1314,7 @@ bool FileManager::isDirectory(const std::string &path) const // a '/' at the end of the path. if(s[s.size()-1]=='/') s.erase(s.end()-1, s.end()); - if(stat(s.c_str(), &mystat) < 0) return false; + if(FileUtils::statU8Path(s, &mystat) < 0) return false; return S_ISDIR(mystat.st_mode); } // isDirectory @@ -1373,9 +1374,15 @@ bool FileManager::removeFile(const std::string &name) const return true; struct stat mystat; - if(stat(name.c_str(), &mystat) < 0) return false; + if(FileUtils::statU8Path(name, &mystat) < 0) return false; if( S_ISREG(mystat.st_mode)) - return remove(name.c_str())==0; + { +#if defined(WIN32) + return _wremove(StringUtils::utf8ToWide(name).c_str()) == 0; +#else + return remove(name.c_str()) == 0; +#endif + } return false; } // removeFile @@ -1434,10 +1441,10 @@ bool FileManager::removeDirectory(const std::string &name) const */ bool FileManager::copyFile(const std::string &source, const std::string &dest) { - FILE *f_source = fopen(source.c_str(), "rb"); + FILE *f_source = FileUtils::fopenU8Path(source, "rb"); if(!f_source) return false; - FILE *f_dest = fopen(dest.c_str(), "wb"); + FILE *f_dest = FileUtils::fopenU8Path(dest, "wb"); if(!f_dest) { fclose(f_source); @@ -1472,6 +1479,7 @@ bool FileManager::copyFile(const std::string &source, const std::string &dest) fclose(f_dest); return true; } // copyFile + // ---------------------------------------------------------------------------- /** Returns true if the first file is newer than the second. The comparison is * based on the modification time of the two files. @@ -1480,7 +1488,7 @@ bool FileManager::fileIsNewer(const std::string& f1, const std::string& f2) cons { struct stat stat1; struct stat stat2; - stat(f1.c_str(), &stat1); - stat(f2.c_str(), &stat2); + FileUtils::statU8Path(f1, &stat1); + FileUtils::statU8Path(f2, &stat2); return stat1.st_mtime > stat2.st_mtime; } // fileIsNewer diff --git a/src/utils/file_utils.cpp b/src/utils/file_utils.cpp new file mode 100644 index 000000000..8d62ac9a4 --- /dev/null +++ b/src/utils/file_utils.cpp @@ -0,0 +1,129 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2019 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "utils/file_utils.hpp" +#include "utils/log.hpp" +#include "utils/string_utils.hpp" + +#include +#include +#include + +// ---------------------------------------------------------------------------- +#if defined(WIN32) +#include +/** Return a 8.3 filename if u8_path contains unicode character + */ +std::string FileUtils::Private::getShortPath(const std::string& u8_path) +{ + bool has_unicode = false; + for (char c : u8_path) + { + if (static_cast(c) > 127) + { + has_unicode = true; + break; + } + } + if (!has_unicode) + return u8_path; + + irr::core::stringw wpath = StringUtils::utf8ToWide(u8_path); + size_t length = GetShortPathNameW(wpath.c_str(), NULL, 0); + if (length == 0) + { + Log::error("FileUtils", + "Failed to GetShortPathNameW with getting required length."); + return ""; + } + std::vector short_path; + short_path.resize(length); + length = GetShortPathNameW(wpath.c_str(), short_path.data(), length); + if (length == 0) + { + Log::error("FileUtils", + "Failed to GetShortPathNameW with writing short path."); + return ""; + } + short_path.push_back(0); + + std::string result; + // Reserve enough space for conversion + result.resize(length * 4); + length = WideCharToMultiByte(CP_ACP, 0, short_path.data(), -1, &result[0], + result.size(), NULL, NULL); + // Passing -1 as input string length will null terminated the output, so + // length written included a null terminator + result.resize(length - 1); + return result; +} // getShortPath +#endif + +// ---------------------------------------------------------------------------- +/** fopen() with unicode path capability. + */ +FILE* FileUtils::fopenU8Path(const std::string& u8_path, const char* mode) +{ +#if defined(WIN32) + std::vector mode_str; + for (unsigned i = 0; i < strlen(mode); i++) + mode_str.push_back((wchar_t)mode[i]); + mode_str.push_back(0); + return _wfopen(StringUtils::utf8ToWide(u8_path).c_str(), mode_str.data()); +#else + return fopen(u8_path.c_str(), mode); +#endif +} // fopenU8Path + +// ---------------------------------------------------------------------------- +/** stat() with unicode path capability. + */ +int FileUtils::statU8Path(const std::string& u8_path, struct stat *buf) +{ +#if defined(WIN32) + struct _stat st; + int ret = _wstat(StringUtils::utf8ToWide(u8_path).c_str(), &st); + buf->st_dev = st.st_dev; + buf->st_ino = st.st_ino; + buf->st_mode = st.st_mode; + buf->st_nlink = st.st_nlink; + buf->st_uid = st.st_uid; + buf->st_gid = st.st_gid; + buf->st_rdev = st.st_rdev; + buf->st_size = (_off_t)st.st_size; + buf->st_atime = st.st_atime; + buf->st_mtime = st.st_mtime; + buf->st_ctime = st.st_ctime; + return ret; +#else + return stat(u8_path.c_str(), buf); +#endif +} // statU8Path + +// ---------------------------------------------------------------------------- +/** rename() with unicode path capability. + */ +int FileUtils::renameU8Path(const std::string& u8_path_old, + const std::string& u8_path_new) +{ +#if defined(WIN32) + return _wrename(StringUtils::utf8ToWide(u8_path_old).c_str(), + StringUtils::utf8ToWide(u8_path_new).c_str()); +#else + return rename(u8_path_old.c_str(), u8_path_new.c_str()); +#endif +} // renameU8Path diff --git a/src/utils/file_utils.hpp b/src/utils/file_utils.hpp new file mode 100644 index 000000000..542cacfeb --- /dev/null +++ b/src/utils/file_utils.hpp @@ -0,0 +1,51 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2019 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_FILE_UTILS_HPP +#define HEADER_FILE_UTILS_HPP + +#include +#include +#include + +namespace FileUtils +{ + namespace Private + { + std::string getShortPath(const std::string& u8_path); + } + // ------------------------------------------------------------------------ + FILE* fopenU8Path(const std::string& u8_path, const char* mode); + // ------------------------------------------------------------------------ + int statU8Path(const std::string& u8_path, struct stat *buf); + // ------------------------------------------------------------------------ + int renameU8Path(const std::string& u8_path_old, + const std::string& u8_path_new); + // ------------------------------------------------------------------------ + /* Return a path which can be opened in all systems, as long as u8_path + * is unicode encoded. */ + inline std::string getPortablePath(const std::string& u8_path) + { +#ifdef WIN32 + return Private::getShortPath(u8_path); +#else + return u8_path; +#endif + } +} // namespace FileUtils + +#endif