X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=fs.c;h=2b51ec910905be9261e3f7c7ad5d743569f59c01;hb=ceb0443a38d14772e6bacb4c6215b6b209cf8bff;hp=25789e0235888c1f58d199fd65c30184e6711e70;hpb=17d3e4a60fbe0fb9d56f953f0253e04f0a2d0db4;p=xonotic%2Fdarkplaces.git diff --git a/fs.c b/fs.c index 25789e02..2b51ec91 100644 --- a/fs.c +++ b/fs.c @@ -49,6 +49,10 @@ #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 # define O_BINARY 0 @@ -105,6 +109,66 @@ static filedesc_t FILEDESC_DUP(const char *filename, filedesc_t fd) { } #endif + +/* This code seems to have originally been written with the assumption that + * read(..., n) returns n on success. This is not the case (refer to + * ). + * Ditto for write. + */ + +/* +==================== +ReadAll + +Read exactly length bytes from fd into buf. If end of file is reached, +the number of bytes read is returned. If an error occurred, that error +is returned. Note that if an error is returned, any previously read +data is lost. +==================== +*/ +static fs_offset_t ReadAll(const filedesc_t fd, void *const buf, const size_t length) +{ + char *const p = (char *)buf; + size_t cursor = 0; + do + { + const fs_offset_t result = FILEDESC_READ(fd, p + cursor, length - cursor); + if (result < 0) // Error + return result; + if (result == 0) // EOF + break; + cursor += result; + } while (cursor < length); + return cursor; +} + +/* +==================== +WriteAll + +Write exactly length bytes to fd from buf. +If an error occurred, that error is returned. +==================== +*/ +static fs_offset_t WriteAll(const filedesc_t fd, const void *const buf, const size_t length) +{ + const char *const p = (const char *)buf; + size_t cursor = 0; + do + { + const fs_offset_t result = FILEDESC_WRITE(fd, p + cursor, length - cursor); + if (result < 0) // Error + return result; + cursor += result; + } while (cursor < length); + return cursor; +} + +#undef FILEDESC_READ +#define FILEDESC_READ ReadAll +#undef FILEDESC_WRITE +#define FILEDESC_WRITE WriteAll + /** \page fs File System All of Quake's data access is through a hierchal file system, but the contents @@ -385,6 +449,7 @@ int fs_all_gamedirs_count = 0; cvar_t scr_screenshot_name = {CF_CLIENT | CF_PERSISTENT, "scr_screenshot_name","dp", "prefix name for saved screenshots (changes based on -game commandline, as well as which game mode is running; the date is encoded using strftime escapes)"}; cvar_t fs_empty_files_in_pack_mark_deletions = {CF_CLIENT | CF_SERVER, "fs_empty_files_in_pack_mark_deletions", "0", "if enabled, empty files in a pak/pk3 count as not existing but cancel the search in further packs, effectively allowing patch pak/pk3 files to 'delete' files"}; +cvar_t fs_unload_dlcache = {CF_CLIENT, "fs_unload_dlcache", "1", "if enabled, unload dlcache's loaded pak/pk3 files when changing server and/or map WARNING: disabling unloading can cause servers to override assets of other servers, \"memory leaking\" by dlcache assets never unloading and many more issues"}; cvar_t cvar_fs_gamedir = {CF_CLIENT | CF_SERVER | CF_READONLY | CF_PERSISTENT, "fs_gamedir", "", "the list of currently selected gamedirs (use the 'gamedir' command to change this)"}; @@ -438,10 +503,10 @@ static dllhandle_t zlib_dll = NULL; #endif #ifdef WIN32 -static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath); +static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath); static dllfunction_t shfolderfuncs[] = { - {"SHGetFolderPathA", (void **) &qSHGetFolderPath}, + {"SHGetFolderPathW", (void **) &qSHGetFolderPath}, {NULL, NULL} }; static const char* shfolderdllnames [] = @@ -451,7 +516,7 @@ static const char* shfolderdllnames [] = }; static dllhandle_t shfolder_dll = NULL; -const GUID qFOLDERID_SavedGames = {0x4C5C32FF, 0xBB9D, 0x43b0, {0xB5, 0xB4, 0x2D, 0x72, 0xE5, 0x4E, 0xAA, 0xA4}}; +const GUID qFOLDERID_SavedGames = {0x4C5C32FF, 0xBB9D, 0x43b0, {0xB5, 0xB4, 0x2D, 0x72, 0xE5, 0x4E, 0xAA, 0xA4}}; #define qREFKNOWNFOLDERID const GUID * #define qKF_FLAG_CREATE 0x8000 #define qKF_FLAG_NO_ALIAS 0x1000 @@ -926,14 +991,30 @@ 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 if(Sys_CheckParm("-readonly")) return; #if WIN32 - if (_mkdir (path) == -1) + widen(path, pathw); + if (_wmkdir (pathw) == -1) #else if (mkdir (path, 0777) == -1) #endif @@ -945,7 +1026,6 @@ static void FS_mkdir (const char *path) } } - /* ============ FS_CreatePath @@ -1414,6 +1494,9 @@ void FS_UnloadPacks_dlcache(void) { searchpath_t *search = fs_searchpaths, *searchprev = fs_searchpaths, *searchnext; + if (!fs_unload_dlcache.integer) + return; + while (search) { searchnext = search->next; @@ -1513,13 +1596,16 @@ void FS_Rescan (void) // add back the selfpack as new first item FS_AddSelfPack(); - // set the default screenshot name to either the mod name or the - // gamemode screenshot name - if (strcmp(com_modname, gamedirname1)) - Cvar_SetQuick (&scr_screenshot_name, com_modname); - else - Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname); - + if (cls.state != ca_dedicated) + { + // set the default screenshot name to either the mod name or the + // gamemode screenshot name + if (strcmp(com_modname, gamedirname1)) + Cvar_SetQuick (&scr_screenshot_name, com_modname); + else + Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname); + } + if((i = Sys_CheckParm("-modname")) && i < sys.argc - 1) strlcpy(com_modname, sys.argv[i+1], sizeof(com_modname)); @@ -1740,7 +1826,7 @@ const char *FS_CheckGameDir(const char *gamedir) ret = FS_SysCheckGameDir(va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, gamedir), buf, sizeof(buf)); if(ret) return ret; - + return fs_checkgamedir_missing; } @@ -1773,7 +1859,7 @@ static void FS_ListGameDirs(void) continue; if(!*info) continue; - stringlistappend(&list2, list.strings[i]); + stringlistappend(&list2, list.strings[i]); } stringlistfreecontents(&list); @@ -1849,13 +1935,15 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use return -1; #elif defined(WIN32) - char *homedir; + char homedir[WSTRBUF]; + wchar *homedirw; #if _MSC_VER >= 1400 - size_t homedirlen; + size_t homedirwlen; #endif - TCHAR mydocsdir[MAX_PATH + 1]; + wchar_t mydocsdirw[WSTRBUF]; + char mydocsdir[WSTRBUF]; wchar_t *savedgamesdirw; - char savedgamesdir[MAX_OSPATH]; + char savedgamesdir[WSTRBUF] = {0}; int fd; char vabuf[1024]; @@ -1871,22 +1959,25 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use if (!shfolder_dll) Sys_LoadDependency(shfolderdllnames, &shfolder_dll, shfolderfuncs); mydocsdir[0] = 0; - if (qSHGetFolderPath && qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK) + if (qSHGetFolderPath && qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdirw) == S_OK) { + narrow(mydocsdirw, mydocsdir); dpsnprintf(userdir, userdirsize, "%s/My Games/%s/", mydocsdir, gameuserdirname); break; } #if _MSC_VER >= 1400 - _dupenv_s(&homedir, &homedirlen, "USERPROFILE"); - if(homedir) + _wdupenv_s(&homedirw, &homedirwlen, L"USERPROFILE"); + narrow(homedirw, homedir); + if(homedir[0]) { dpsnprintf(userdir, userdirsize, "%s/.%s/", homedir, gameuserdirname); - free(homedir); + free(homedirw); break; } #else - homedir = getenv("USERPROFILE"); - if(homedir) + homedirw = _wgetenv(L"USERPROFILE"); + narrow(homedirw, homedir); + if(homedir[0]) { dpsnprintf(userdir, userdirsize, "%s/.%s/", homedir, gameuserdirname); break; @@ -1911,12 +2002,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) { - memset(savedgamesdir, 0, sizeof(savedgamesdir)); -#if _MSC_VER >= 1400 - wcstombs_s(NULL, savedgamesdir, sizeof(savedgamesdir), savedgamesdirw, sizeof(savedgamesdir)-1); -#else - wcstombs(savedgamesdir, savedgamesdirw, sizeof(savedgamesdir)-1); -#endif + narrow(savedgamesdirw, savedgamesdir); qCoTaskMemFree(savedgamesdirw); } qCoUninitialize(); @@ -2011,6 +2097,7 @@ void FS_Init_Commands(void) { Cvar_RegisterVariable (&scr_screenshot_name); Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions); + Cvar_RegisterVariable (&fs_unload_dlcache); Cvar_RegisterVariable (&cvar_fs_gamedir); Cmd_AddCommand(CF_SHARED, "gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)"); @@ -2279,6 +2366,9 @@ 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 // Parse the mode string switch (mode[0]) @@ -2330,10 +2420,11 @@ static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qbo handle = SDL_RWFromFile(filepath, mode); #else # ifdef WIN32 + widen(filepath, filepathw); # if _MSC_VER >= 1400 - _sopen_s(&handle, filepath, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE); + _wsopen_s(&handle, filepathw, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE); # else - handle = _sopen (filepath, 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); @@ -2557,7 +2648,7 @@ int FS_CheckNastyPath (const char *path, qbool isgamedir) ==================== FS_SanitizePath -Sanitize path (replace non-portable characters +Sanitize path (replace non-portable characters with portable ones in-place, etc) ==================== */ @@ -2714,7 +2805,7 @@ static qfile_t *FS_OpenReadFile (const char *filename, qbool quiet, qbool nonblo if(count < 0) return NULL; linkbuf[count] = 0; - + // Now combine the paths... mergeslash = strrchr(filename, '/'); mergestart = linkbuf; @@ -3592,8 +3683,10 @@ int FS_SysFileType (const char *path) # ifndef INVALID_FILE_ATTRIBUTES # define INVALID_FILE_ATTRIBUTES ((DWORD)-1) # endif - - DWORD result = GetFileAttributes(path); + wchar pathw[WSTRBUF] = {0}; + DWORD result; + widen(path, pathw); + result = GetFileAttributesW(pathw); if(result == INVALID_FILE_ATTRIBUTES) return FS_FILETYPE_NONE; @@ -3946,7 +4039,7 @@ void FS_Which_f(cmd_state_t *cmd) { Con_Printf("usage:\n%s \n", Cmd_Argv(cmd, 0)); return; - } + } filename = Cmd_Argv(cmd, 1); sp = FS_FindFile(filename, &index, true); if (!sp) { @@ -4099,7 +4192,7 @@ unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflat Mem_Free(tmp); return NULL; } - + if(qz_deflateEnd(&strm) != Z_OK) { Con_Printf("FS_Deflate: deflateEnd failed\n"); @@ -4126,7 +4219,7 @@ unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflat memcpy(out, tmp, strm.total_out); Mem_Free(tmp); - + return out; } @@ -4193,7 +4286,7 @@ unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflat case Z_STREAM_END: case Z_OK: break; - + case Z_STREAM_ERROR: Con_Print("FS_Inflate: stream error!\n"); break; @@ -4209,7 +4302,7 @@ unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflat default: Con_Print("FS_Inflate: unknown error!\n"); break; - + } if(ret != Z_OK && ret != Z_STREAM_END) { @@ -4237,6 +4330,6 @@ unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflat Mem_Free(outbuf.data); *inflated_size = (size_t)outbuf.cursize; - + return out; }