From 251f5141efd45b2cbf39e39d88f9b76f8e491344 Mon Sep 17 00:00:00 2001 From: NaitLee Date: Sun, 7 Jul 2024 03:00:14 +0800 Subject: [PATCH] fix can't load data on windows if data is in non-ascii path (introduced by 1f4743e9), refactor windows unicode support --- fs.c | 84 +++++++++++++++++++++++++++---------------------------- utf8lib.c | 50 +++++++++++++++++++-------------- utf8lib.h | 25 ++++++++++++++--- 3 files changed, 91 insertions(+), 68 deletions(-) diff --git a/fs.c b/fs.c index 6e425971..c5cf5d66 100644 --- a/fs.c +++ b/fs.c @@ -49,9 +49,7 @@ #include "fs.h" #include "wad.h" -#ifdef WIN32 #include "utf8lib.h" -#endif // Win32 requires us to add O_BINARY, but the other OSes don't have it #ifndef O_BINARY @@ -65,7 +63,7 @@ // largefile support for Win32 #ifdef WIN32 -#undef lseek +# undef lseek # define lseek _lseeki64 #endif @@ -78,6 +76,14 @@ # define dup _dup #endif +// windows wchar helpers +#ifdef WIN32 +# define MAX_OSWPATH (MAX_OSPATH * sizeof(wchar)) +# define WPATHDEF(var) wchar var[MAX_OSWPATH] +#else +# define WPATHDEF(var) ; +#endif // WIN32 + #if USE_RWOPS # include typedef SDL_RWops *filedesc_t; @@ -990,32 +996,19 @@ static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack, return pfile; } -#if WIN32 -#define WSTRBUF 4096 -static inline int wstrlen(wchar *wstr) -{ - int len = 0; - while (wstr[len] != 0 && len < WSTRBUF) - ++len; - return len; -} -#define widen(str, wstr) fromwtf8(str, strlen(str), wstr, WSTRBUF) -#define narrow(wstr, str) towtf8(wstr, wstrlen(wstr), str, WSTRBUF) -#endif - static void FS_mkdir (const char *path) { -#if WIN32 - wchar pathw[WSTRBUF] = {0}; -#endif + WPATHDEF(pathw); + if(Sys_CheckParm("-readonly")) return; -#if WIN32 - widen(path, pathw); - if (_wmkdir (pathw) == -1) + WIDE(path, pathw); + +#ifdef WIN32 + if (_wmkdir(pathw) == -1) #else - if (mkdir (path, 0777) == -1) + if (mkdir(path, 0777) == -1) #endif { // No logging for this. The only caller is FS_CreatePath (which @@ -1963,15 +1956,15 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use return -1; #elif defined(WIN32) - char homedir[WSTRBUF]; + char homedir[MAX_OSWPATH]; wchar *homedirw; #if _MSC_VER >= 1400 size_t homedirwlen; #endif - wchar_t mydocsdirw[WSTRBUF]; - char mydocsdir[WSTRBUF]; - wchar_t *savedgamesdirw; - char savedgamesdir[WSTRBUF] = {0}; + char mydocsdir[MAX_OSWPATH]; + wchar mydocsdirw[MAX_OSWPATH]; + char savedgamesdir[MAX_OSWPATH]; + wchar *savedgamesdirw; int fd; char vabuf[1024]; @@ -1989,13 +1982,13 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use mydocsdir[0] = 0; if (qSHGetFolderPath && qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdirw) == S_OK) { - narrow(mydocsdirw, mydocsdir); + NARROW(mydocsdirw, mydocsdir); dpsnprintf(userdir, userdirsize, "%s/My Games/%s/", mydocsdir, gameuserdirname); break; } #if _MSC_VER >= 1400 _wdupenv_s(&homedirw, &homedirwlen, L"USERPROFILE"); - narrow(homedirw, homedir); + NARROW(homedirw, homedir); if(homedir[0]) { dpsnprintf(userdir, userdirsize, "%s/.%s/", homedir, gameuserdirname); @@ -2004,7 +1997,7 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use } #else homedirw = _wgetenv(L"USERPROFILE"); - narrow(homedirw, homedir); + NARROW(homedirw, homedir); if(homedir[0]) { dpsnprintf(userdir, userdirsize, "%s/.%s/", homedir, gameuserdirname); @@ -2030,7 +2023,7 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use */ if (qSHGetKnownFolderPath(&qFOLDERID_SavedGames, qKF_FLAG_CREATE | qKF_FLAG_NO_ALIAS, NULL, &savedgamesdirw) == S_OK) { - narrow(savedgamesdirw, savedgamesdir); + NARROW(savedgamesdirw, savedgamesdir); qCoTaskMemFree(savedgamesdirw); } qCoUninitialize(); @@ -2141,6 +2134,7 @@ static void FS_Init_Dir (void) int i; int numgamedirs; const char *cmdline_gamedirs[MAX_GAMEDIRS]; + WPATHDEF(fs_basedirw); *fs_basedir = 0; *fs_userdir = 0; @@ -2194,8 +2188,13 @@ static void FS_Init_Dir (void) } } #else - // use the working directory + // use the working directory + #ifdef WIN32 + _wgetcwd(fs_basedirw, sizeof(fs_basedirw)); + NARROW(fs_basedirw, fs_basedir); + #else getcwd(fs_basedir, sizeof(fs_basedir)); + #endif #endif } @@ -2380,9 +2379,7 @@ static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qbo int mod, opt; unsigned int ind; qbool dolock = false; - #ifdef WIN32 - wchar filepathw[WSTRBUF] = {0}; - #endif + WPATHDEF(filepathw); // Parse the mode string switch (mode[0]) @@ -2434,14 +2431,14 @@ static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qbo handle = SDL_RWFromFile(filepath, mode); #else # ifdef WIN32 - widen(filepath, filepathw); + WIDE(filepath, filepathw); # if _MSC_VER >= 1400 _wsopen_s(&handle, filepathw, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE); # else - handle = _wsopen (filepathw, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE); + handle = _wsopen(filepathw, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE); # endif # else - handle = open (filepath, mod | opt, 0666); + handle = open(filepath, mod | opt, 0666); if(handle >= 0 && dolock) { struct flock l; @@ -3703,14 +3700,15 @@ Look for a file in the filesystem only */ int FS_SysFileType (const char *path) { -#if WIN32 -// Sajt - some older sdks are missing this define +#ifdef WIN32 + // Sajt - some older sdks are missing this define # ifndef INVALID_FILE_ATTRIBUTES # define INVALID_FILE_ATTRIBUTES ((DWORD)-1) # endif - wchar pathw[WSTRBUF] = {0}; + WPATHDEF(pathw); DWORD result; - widen(path, pathw); + + WIDE(path, pathw); result = GetFileAttributesW(pathw); if(result == INVALID_FILE_ATTRIBUTES) diff --git a/utf8lib.c b/utf8lib.c index 1e34e707..62060132 100644 --- a/utf8lib.c +++ b/utf8lib.c @@ -912,7 +912,7 @@ size_t u8_strpad_colorcodes(char *out, size_t outsize, const char *in, qbool lef /** * Convert Windows "wide" characters to WTF-8 for internal manipulation */ -int towtf8(const wchar *wstr, int wlen, char *cstr, int maxclen) +int towtf8(const wchar *wstr, int wlen, char *str, int maxlen) { int p = 0; int i; @@ -921,24 +921,24 @@ int towtf8(const wchar *wstr, int wlen, char *cstr, int maxclen) wchar point = wstr[i]; if (point < 0x80) { - if (p + 1 >= maxclen) break; - cstr[p++] = point; + if (p + 1 > maxlen) break; + str[p++] = point; } else if (point < 0x800) { - if (p + 2 >= maxclen) break; - cstr[p++] = (0xc0 | ((point >> 6) & 0x1f)); - cstr[p++] = (0x80 | ((point >> 0) & 0x3f)); + if (p + 2 > maxlen) break; + str[p++] = (0xc0 | ((point >> 6) & 0x1f)); + str[p++] = (0x80 | ((point >> 0) & 0x3f)); } else #if WTF8U32 if (point < 0x10000) #endif { - if (p + 3 >= maxclen) break; - cstr[p++] = (0xe0 | ((point >> 12) & 0x0f)); - cstr[p++] = (0x80 | ((point >> 6) & 0x3f)); - cstr[p++] = (0x80 | ((point >> 0) & 0x3f)); + if (p + 3 > maxlen) break; + str[p++] = (0xe0 | ((point >> 12) & 0x0f)); + str[p++] = (0x80 | ((point >> 6) & 0x3f)); + str[p++] = (0x80 | ((point >> 0) & 0x3f)); } #if WTF8U32 else @@ -946,31 +946,31 @@ int towtf8(const wchar *wstr, int wlen, char *cstr, int maxclen) if (point < 0x110000) #endif { - if (p + 4 >= maxclen) break; - cstr[p++] = (0xf0 | ((point >> 18) & 0x07)); - cstr[p++] = (0x80 | ((point >> 12) & 0x3f)); - cstr[p++] = (0x80 | ((point >> 6) & 0x3f)); - cstr[p++] = (0x80 | ((point >> 0) & 0x3f)); + if (p + 4 > maxlen) break; + str[p++] = (0xf0 | ((point >> 18) & 0x07)); + str[p++] = (0x80 | ((point >> 12) & 0x3f)); + str[p++] = (0x80 | ((point >> 6) & 0x3f)); + str[p++] = (0x80 | ((point >> 0) & 0x3f)); } #endif } - cstr[p] = 0x00; + str[p] = 0x00; return p; } /** * Convert WTF-8 string to "wide" characters used by Windows */ -int fromwtf8(const char *cstr, int clen, wchar *wstr, int maxwlen) +int fromwtf8(const char *str, int len, wchar *wstr, int maxwlen) { int p = 0; int i; - for (i = 0; i < clen;) + for (i = 0; i < len;) { - char byte = cstr[i++]; + char byte = str[i++]; wchar point = byte; int length = 1; - if (p + 1 >= maxwlen) break; + if (p + 1 > maxwlen) break; #if WTF8CHECKS if ((byte & 0xf8) == 0xf8) return -1; @@ -1001,7 +1001,7 @@ int fromwtf8(const char *cstr, int clen, wchar *wstr, int maxwlen) #endif while (--length) { - byte = cstr[i++]; + byte = str[i++]; #if WTF8CHECKS if (byte == -1) return -1; else if ((byte & 0xc0) != 0x80) return -1; @@ -1014,6 +1014,14 @@ int fromwtf8(const char *cstr, int clen, wchar *wstr, int maxwlen) return p; } +int wstrlen(const wchar *wstr) +{ + int i; + for (i = 0; wstr[i] != 0; ++i) + ; + return i; +} + #endif // WIN32 diff --git a/utf8lib.h b/utf8lib.h index b50a2065..fb75018d 100644 --- a/utf8lib.h +++ b/utf8lib.h @@ -82,12 +82,29 @@ Uchar u8_tolower(Uchar ch); // WTF-8 encoding to circumvent Windows encodings, be it UTF-16 or random codepages // https://simonsapin.github.io/wtf-8/ -#define WTF8U32 0 // whether to regard wchar as utf-32 -#define WTF8CHECKS 1 // check for extra sanity in conversion steps + typedef wchar_t wchar; -int towtf8(const wchar* wstr, int wlen, char* cstr, int maxclen); -int fromwtf8(const char* cstr, int clen, wchar* wstr, int maxwlen); +// whether to regard wchar as utf-32 +// sizeof(wchar_t) is 2 for win32, we don't have sizeof in macros +#define WTF8U32 0 +// check for extra sanity in conversion steps +#define WTF8CHECKS 1 + +int towtf8(const wchar* wstr, int wlen, char* str, int maxlen); +int fromwtf8(const char* str, int len, wchar* wstr, int maxwlen); +int wstrlen(const wchar* wstr); + +// helpers for wchar code +/* convert given wtf-8 encoded char *str to wchar *wstr, only on win32 */ +#define WIDE(str, wstr) fromwtf8(str, strlen(str), wstr, strlen(str)) +/* convert given wchar *wstr to wtf-8 encoded char *str, only on win32 */ +#define NARROW(wstr, str) towtf8(wstr, wstrlen(wstr), str, wstrlen(wstr) * (WTF8U32 ? 4 : 3)) + +#else + +#define WIDE(str, wstr) ; +#define NARROW(wstr, str) ; #endif // WIN32 -- 2.39.2