X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=fs.c;h=bea6cbcd586ed9432ab0ebec6a8201a37254b4c5;hb=02cc3403ad217f5566a416771725b6aac97d4559;hp=15fe262d2ff620a9a778e305f1fea5b8fd29d185;hpb=48669036b4be979d997c27227942ed9c430277d5;p=xonotic%2Fdarkplaces.git diff --git a/fs.c b/fs.c index 15fe262d..bea6cbcd 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 @@ -318,9 +382,10 @@ typedef struct pack_s char filename [MAX_OSPATH]; char shortname [MAX_QPATH]; filedesc_t handle; - int ignorecase; ///< PK3 ignores case + qbool ignorecase; ///< PK3 ignores case int numfiles; - qboolean vpack; + qbool vpack; + qbool dlcache; packfile_t *files; } pack_t; //@} @@ -343,11 +408,11 @@ FUNCTION PROTOTYPES ============================================================================= */ -void FS_Dir_f(void); -void FS_Ls_f(void); -void FS_Which_f(void); +void FS_Dir_f(cmd_state_t *cmd); +void FS_Ls_f(cmd_state_t *cmd); +void FS_Which_f(cmd_state_t *cmd); -static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet); +static searchpath_t *FS_FindFile (const char *name, int *index, const char **canonicalname, qbool quiet); static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack, fs_offset_t offset, fs_offset_t packsize, fs_offset_t realsize, int flags); @@ -382,9 +447,10 @@ char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH]; gamedir_t *fs_all_gamedirs = NULL; int fs_all_gamedirs_count = 0; -cvar_t scr_screenshot_name = {CVAR_NORESETTODEFAULTS, "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 = {0, "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 cvar_fs_gamedir = {CVAR_READONLY | CVAR_NORESETTODEFAULTS, "fs_gamedir", "", "the list of currently selected gamedirs (use the 'gamedir' command to change this)"}; +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)"}; /* @@ -437,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 [] = @@ -450,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 @@ -495,7 +561,7 @@ Unload the Zlib DLL static void PK3_CloseLibrary (void) { #ifndef LINK_TO_ZLIB - Sys_UnloadLibrary (&zlib_dll); + Sys_FreeLibrary (&zlib_dll); #endif } @@ -507,7 +573,7 @@ PK3_OpenLibrary Try to load the Zlib DLL ==================== */ -static qboolean PK3_OpenLibrary (void) +static qbool PK3_OpenLibrary (void) { #ifdef LINK_TO_ZLIB return true; @@ -535,7 +601,7 @@ static qboolean PK3_OpenLibrary (void) return true; // Load the DLL - return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs); + return Sys_LoadDependency (dllnames, &zlib_dll, zlibfuncs); #endif } @@ -546,7 +612,7 @@ FS_HasZlib See if zlib is available ==================== */ -qboolean FS_HasZlib(void) +qbool FS_HasZlib(void) { #ifdef LINK_TO_ZLIB return true; @@ -563,7 +629,7 @@ PK3_GetEndOfCentralDir Extract the end of the central directory from a PK3 package ==================== */ -static qboolean PK3_GetEndOfCentralDir (const char *packfile, filedesc_t packhandle, pk3_endOfCentralDir_t *eocd) +static qbool PK3_GetEndOfCentralDir (const char *packfile, filedesc_t packhandle, pk3_endOfCentralDir_t *eocd) { fs_offset_t filesize, maxsize; unsigned char *buffer, *ptr; @@ -682,7 +748,7 @@ static int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd) return -1; } - namesize = BuffLittleShort (&ptr[28]); // filename length + namesize = (unsigned short)BuffLittleShort (&ptr[28]); // filename length // Check encryption, compression, and attributes // 1st uint8 : general purpose bit flag @@ -699,7 +765,7 @@ static int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd) if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0) { // Still enough bytes for the name? - if (namesize < 0 || remaining < namesize || namesize >= (int)sizeof (*pack->files)) + if (remaining < namesize || namesize >= (int)sizeof (*pack->files)) { Mem_Free (central_dir); return -1; @@ -743,7 +809,7 @@ static int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd) // Skip the name, additionnal field, and comment // 1er uint16 : extra field length // 2eme uint16 : file comment length - count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]); + count = namesize + (unsigned short)BuffLittleShort (&ptr[30]) + (unsigned short)BuffLittleShort (&ptr[32]); ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count; remaining -= count; } @@ -762,7 +828,7 @@ FS_LoadPackPK3 Create a package entry associated with a PK3 file ==================== */ -static pack_t *FS_LoadPackPK3FromFD (const char *packfile, filedesc_t packhandle, qboolean silent) +static pack_t *FS_LoadPackPK3FromFD (const char *packfile, filedesc_t packhandle, qbool silent) { pk3_endOfCentralDir_t eocd; pack_t *pack; @@ -798,7 +864,7 @@ static pack_t *FS_LoadPackPK3FromFD (const char *packfile, filedesc_t packhandle // Create a package structure in memory pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t)); pack->ignorecase = true; // PK3 ignores case - strlcpy (pack->filename, packfile, sizeof (pack->filename)); + dp_strlcpy (pack->filename, packfile, sizeof (pack->filename)); pack->handle = packhandle; pack->numfiles = eocd.nbentries; pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t)); @@ -816,7 +882,7 @@ static pack_t *FS_LoadPackPK3FromFD (const char *packfile, filedesc_t packhandle return pack; } -static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qboolean nonblocking); +static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qbool nonblocking); static pack_t *FS_LoadPackPK3 (const char *packfile) { filedesc_t packhandle; @@ -834,7 +900,7 @@ PK3_GetTrueFileOffset Find where the true file data offset is ==================== */ -static qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack) +static qbool PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack) { unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE]; fs_offset_t count; @@ -916,7 +982,7 @@ static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack, memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile)); pack->numfiles++; - strlcpy (pfile->name, name, sizeof (pfile->name)); + dp_strlcpy (pfile->name, name, sizeof (pfile->name)); pfile->offset = offset; pfile->packsize = packsize; pfile->realsize = realsize; @@ -925,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(COM_CheckParm("-readonly")) +#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 @@ -944,7 +1026,6 @@ static void FS_mkdir (const char *path) } } - /* ============ FS_CreatePath @@ -976,7 +1057,7 @@ FS_Path_f ============ */ -static void FS_Path_f (void) +static void FS_Path_f(cmd_state_t *cmd) { searchpath_t *s; @@ -1059,7 +1140,7 @@ static pack_t *FS_LoadPackPAK (const char *packfile) pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t)); pack->ignorecase = true; // PAK is sensitive in Quake1 but insensitive in Quake2 - strlcpy (pack->filename, packfile, sizeof (pack->filename)); + dp_strlcpy (pack->filename, packfile, sizeof (pack->filename)); pack->handle = packhandle; pack->numfiles = 0; pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t)); @@ -1095,7 +1176,7 @@ static pack_t *FS_LoadPackVirtual (const char *dirname) pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t)); pack->vpack = true; pack->ignorecase = false; - strlcpy (pack->filename, dirname, sizeof(pack->filename)); + dp_strlcpy (pack->filename, dirname, sizeof(pack->filename)); pack->handle = FILEDESC_INVALID; pack->numfiles = -1; pack->files = NULL; @@ -1118,7 +1199,7 @@ FS_AddPack_Fullpath * plain directories. * */ -static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qboolean *already_loaded, qboolean keep_plain_dirs) +static qbool FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qbool *already_loaded, qbool keep_plain_dirs, qbool dlcache) { searchpath_t *search; pack_t *pak = NULL; @@ -1138,11 +1219,11 @@ static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, if(already_loaded) *already_loaded = false; - if(!strcasecmp(ext, "pk3dir")) + if(!strcasecmp(ext, "pk3dir") || !strcasecmp(ext, "dpkdir")) pak = FS_LoadPackVirtual (pakfile); else if(!strcasecmp(ext, "pak")) pak = FS_LoadPackPAK (pakfile); - else if(!strcasecmp(ext, "pk3")) + else if(!strcasecmp(ext, "pk3") || !strcasecmp(ext, "dpk")) pak = FS_LoadPackPK3 (pakfile); else if(!strcasecmp(ext, "obb")) // android apk expansion pak = FS_LoadPackPK3 (pakfile); @@ -1151,7 +1232,7 @@ static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, if(pak) { - strlcpy(pak->shortname, shortname, sizeof(pak->shortname)); + dp_strlcpy(pak->shortname, shortname, sizeof(pak->shortname)); //Con_DPrintf(" Registered pack with short name %s\n", shortname); if(keep_plain_dirs) @@ -1194,25 +1275,26 @@ static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, fs_searchpaths = search; } search->pack = pak; + search->pack->dlcache = dlcache; if(pak->vpack) { dpsnprintf(search->filename, sizeof(search->filename), "%s/", pakfile); - // if shortname ends with "pk3dir", strip that suffix to make it just "pk3" + // if shortname ends with "pk3dir" or "dpkdir", strip that suffix to make it just "pk3" or "dpk" // same goes for the name inside the pack structure l = strlen(pak->shortname); if(l >= 7) - if(!strcasecmp(pak->shortname + l - 7, ".pk3dir")) + if(!strcasecmp(pak->shortname + l - 7, ".pk3dir") || !strcasecmp(pak->shortname + l - 7, ".dpkdir")) pak->shortname[l - 3] = 0; l = strlen(pak->filename); if(l >= 7) - if(!strcasecmp(pak->filename + l - 7, ".pk3dir")) + if(!strcasecmp(pak->filename + l - 7, ".pk3dir") || !strcasecmp(pak->filename + l - 7, ".dpkdir")) pak->filename[l - 3] = 0; } return true; } else { - Con_Printf("unable to load pak \"%s\"\n", pakfile); + Con_Printf(CON_ERROR "unable to load pak \"%s\"\n", pakfile); return false; } } @@ -1232,7 +1314,7 @@ FS_AddPack * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of * plain directories. */ -qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs) +qbool FS_AddPack(const char *pakfile, qbool *already_loaded, qbool keep_plain_dirs, qbool dlcache) { char fullpath[MAX_OSPATH]; int index; @@ -1242,7 +1324,7 @@ qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep *already_loaded = false; // then find the real name... - search = FS_FindFile(pakfile, &index, true); + search = FS_FindFile(pakfile, &index, NULL, true); if(!search || search->pack) { Con_Printf("could not find pak \"%s\"\n", pakfile); @@ -1251,7 +1333,7 @@ qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile); - return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs); + return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs, dlcache); } @@ -1263,13 +1345,14 @@ Sets fs_gamedir, adds the directory to the head of the path, then loads and adds pak1.pak pak2.pak ... ================ */ -static void FS_AddGameDirectory (const char *dir) +static void FS_AddGameDirectory (const char *dir, qbool set_fs_gamedir) { int i; stringlist_t list; searchpath_t *search; - strlcpy (fs_gamedir, dir, sizeof (fs_gamedir)); + if (set_fs_gamedir) + dp_strlcpy (fs_gamedir, dir, sizeof (fs_gamedir)); stringlistinit(&list); listdirectory(&list, "", dir); @@ -1280,16 +1363,17 @@ static void FS_AddGameDirectory (const char *dir) { if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak")) { - FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false); + FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false, false); } } // add any PK3 package in the directory for (i = 0;i < list.numstrings;i++) { - if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3") || !strcasecmp(FS_FileExtension(list.strings[i]), "obb") || !strcasecmp(FS_FileExtension(list.strings[i]), "pk3dir")) + if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3") || !strcasecmp(FS_FileExtension(list.strings[i]), "obb") || !strcasecmp(FS_FileExtension(list.strings[i]), "pk3dir") + || !strcasecmp(FS_FileExtension(list.strings[i]), "dpk") || !strcasecmp(FS_FileExtension(list.strings[i]), "dpkdir")) { - FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false); + FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false, false); } } @@ -1298,7 +1382,7 @@ static void FS_AddGameDirectory (const char *dir) // Add the directory to the search path // (unpacked files have the priority over packed files) search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t)); - strlcpy (search->filename, dir, sizeof (search->filename)); + dp_strlcpy (search->filename, dir, sizeof (search->filename)); search->next = fs_searchpaths; fs_searchpaths = search; } @@ -1309,14 +1393,14 @@ static void FS_AddGameDirectory (const char *dir) FS_AddGameHierarchy ================ */ -static void FS_AddGameHierarchy (const char *dir) +static void FS_AddGameHierarchy (const char *dir, qbool set_fs_gamedir) { char vabuf[1024]; // Add the common game directory - FS_AddGameDirectory (va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, dir)); + FS_AddGameDirectory (va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, dir), set_fs_gamedir); if (*fs_userdir) - FS_AddGameDirectory(va(vabuf, sizeof(vabuf), "%s%s/", fs_userdir, dir)); + FS_AddGameDirectory(va(vabuf, sizeof(vabuf), "%s%s/", fs_userdir, dir), set_fs_gamedir); } @@ -1329,6 +1413,10 @@ const char *FS_FileExtension (const char *in) { const char *separator, *backslash, *colon, *dot; + dot = strrchr(in, '.'); + if (dot == NULL) + return ""; + separator = strrchr(in, '/'); backslash = strrchr(in, '\\'); if (!separator || separator < backslash) @@ -1337,8 +1425,7 @@ const char *FS_FileExtension (const char *in) if (!separator || separator < colon) separator = colon; - dot = strrchr(in, '.'); - if (dot == NULL || (separator && (dot < separator))) + if (separator && (dot < separator)) return ""; return dot + 1; @@ -1395,6 +1482,49 @@ static void FS_ClearSearchPath (void) } } +/* +================ +FS_UnloadPacks_dlcache + +Like FS_ClearSearchPath() but unloads only the packs loaded from dlcache +so we don't need to use a full FS_Rescan() to prevent +content from the previous server and/or map from interfering with the next +================ +*/ +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; + if (search->pack && search->pack->dlcache) + { + Con_DPrintf("Unloading pack: %s\n", search->pack->shortname); + + // remove it from the search path list + if (search == fs_searchpaths) + fs_searchpaths = search->next; + else + searchprev->next = search->next; + + // close the file + FILEDESC_CLOSE(search->pack->handle); + // free any memory associated with it + if (search->pack->files) + Mem_Free(search->pack->files); + Mem_Free(search->pack); + Mem_Free(search); + } + else + searchprev = search; + search = searchnext; + } +} + static void FS_AddSelfPack(void) { if(fs_selfpack) @@ -1416,8 +1546,8 @@ FS_Rescan void FS_Rescan (void) { int i; - qboolean fs_modified = false; - qboolean reset = false; + qbool fs_modified = false; + qbool reset = false; char gamedirbuf[MAX_INPUTLINE]; char vabuf[1024]; @@ -1431,59 +1561,64 @@ void FS_Rescan (void) // add the game-specific paths // gamedirname1 (typically id1) - FS_AddGameHierarchy (gamedirname1); + FS_AddGameHierarchy (gamedirname1, true); // update the com_modname (used for server info) if (gamedirname2 && gamedirname2[0]) - strlcpy(com_modname, gamedirname2, sizeof(com_modname)); + dp_strlcpy(com_modname, gamedirname2, sizeof(com_modname)); else - strlcpy(com_modname, gamedirname1, sizeof(com_modname)); + dp_strlcpy(com_modname, gamedirname1, sizeof(com_modname)); - // add the game-specific path, if any + // add the secondary game-specific path, if any // (only used for mission packs and the like, which should set fs_modified) if (gamedirname2 && gamedirname2[0]) { fs_modified = true; - FS_AddGameHierarchy (gamedirname2); + FS_AddGameHierarchy (gamedirname2, true); } // -game // Adds basedir/gamedir as an override game // LadyHavoc: now supports multiple -game directories // set the com_modname (reported in server info) + // bones_was_here: does NOT set fs_gamedir in FS_AddGameHierarchy() + // so that we still save files in the right place. *gamedirbuf = 0; for (i = 0;i < fs_numgamedirs;i++) { fs_modified = true; - FS_AddGameHierarchy (fs_gamedirs[i]); + FS_AddGameHierarchy (fs_gamedirs[i], false); // update the com_modname (used server info) - strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname)); + dp_strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname)); if(i) - strlcat(gamedirbuf, va(vabuf, sizeof(vabuf), " %s", fs_gamedirs[i]), sizeof(gamedirbuf)); + dp_strlcat(gamedirbuf, va(vabuf, sizeof(vabuf), " %s", fs_gamedirs[i]), sizeof(gamedirbuf)); else - strlcpy(gamedirbuf, fs_gamedirs[i], sizeof(gamedirbuf)); + dp_strlcpy(gamedirbuf, fs_gamedirs[i], sizeof(gamedirbuf)); } Cvar_SetQuick(&cvar_fs_gamedir, gamedirbuf); // so QC or console code can query it // 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((i = COM_CheckParm("-modname")) && i < com_argc - 1) - strlcpy(com_modname, com_argv[i+1], sizeof(com_modname)); + 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) + dp_strlcpy(com_modname, sys.argv[i+1], sizeof(com_modname)); // If "-condebug" is in the command line, remove the previous log file - if (COM_CheckParm ("-condebug") != 0) + if (Sys_CheckParm ("-condebug") != 0) unlink (va(vabuf, sizeof(vabuf), "%s/qconsole.log", fs_gamedir)); // look for the pop.lmp file and set registered to true if it is found if (FS_FileExists("gfx/pop.lmp")) - Cvar_Set ("registered", "1"); + Cvar_SetValueQuick(®istered, 1); switch(gamemode) { case GAME_NORMAL: @@ -1513,7 +1648,7 @@ void FS_Rescan (void) W_UnloadAll(); } -static void FS_Rescan_f(void) +static void FS_Rescan_f(cmd_state_t *cmd) { FS_Rescan(); } @@ -1523,8 +1658,8 @@ static void FS_Rescan_f(void) FS_ChangeGameDirs ================ */ -extern qboolean vid_opened; -qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing) +extern qbool vid_opened; +qbool FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qbool complain, qbool failmissing) { int i; const char *p; @@ -1563,30 +1698,26 @@ qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean } } - Host_SaveConfig(); + Host_SaveConfig(CONFIGFILENAME); fs_numgamedirs = numgamedirs; for (i = 0;i < fs_numgamedirs;i++) - strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i])); + dp_strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i])); // reinitialize filesystem to detect the new paks FS_Rescan(); if (cls.demoplayback) { - CL_Disconnect_f(); + CL_Disconnect(); cls.demonum = 0; } // unload all sounds so they will be reloaded from the new files as needed - S_UnloadAllSounds_f(); - - // close down the video subsystem, it will start up again when the config finishes... - VID_Stop(); - vid_opened = false; + S_UnloadAllSounds_f(cmd_local); - // restart the video subsystem after the config is executed - Cbuf_InsertText("\nloadconfig\nvid_restart\n\n"); + // reset everything that can be and reload configs + Cbuf_InsertText(cmd_local, "\nloadconfig\n"); return true; } @@ -1596,13 +1727,13 @@ qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean FS_GameDir_f ================ */ -static void FS_GameDir_f (void) +static void FS_GameDir_f(cmd_state_t *cmd) { int i; int numgamedirs; char gamedirs[MAX_GAMEDIRS][MAX_QPATH]; - if (Cmd_Argc() < 2) + if (Cmd_Argc(cmd) < 2) { Con_Printf("gamedirs active:"); for (i = 0;i < fs_numgamedirs;i++) @@ -1611,7 +1742,7 @@ static void FS_GameDir_f (void) return; } - numgamedirs = Cmd_Argc() - 1; + numgamedirs = Cmd_Argc(cmd) - 1; if (numgamedirs > MAX_GAMEDIRS) { Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS); @@ -1619,7 +1750,7 @@ static void FS_GameDir_f (void) } for (i = 0;i < numgamedirs;i++) - strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i])); + dp_strlcpy(gamedirs[i], Cmd_Argv(cmd, i+1), sizeof(gamedirs[i])); if ((cls.state == ca_connected && !cls.demoplayback) || sv.active) { @@ -1636,7 +1767,7 @@ static void FS_GameDir_f (void) static const char *FS_SysCheckGameDir(const char *gamedir, char *buf, size_t buflength) { - qboolean success; + qbool success; qfile_t *f; stringlist_t list; fs_offset_t n; @@ -1698,7 +1829,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; } @@ -1731,7 +1862,7 @@ static void FS_ListGameDirs(void) continue; if(!*info) continue; - stringlistappend(&list2, list.strings[i]); + stringlistappend(&list2, list.strings[i]); } stringlistfreecontents(&list); @@ -1746,8 +1877,8 @@ static void FS_ListGameDirs(void) continue; if(!*info) continue; - strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[fs_all_gamedirs_count].name)); - strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[fs_all_gamedirs_count].description)); + dp_strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[fs_all_gamedirs_count].name)); + dp_strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[fs_all_gamedirs_count].description)); ++fs_all_gamedirs_count; } } @@ -1765,11 +1896,11 @@ static void COM_InsertFlags(const char *buf) { const char **new_argv; int i = 0; int args_left = 256; - new_argv = (const char **)Mem_Alloc(fs_mempool, sizeof(*com_argv) * (com_argc + args_left + 2)); - if(com_argc == 0) + new_argv = (const char **)Mem_Alloc(fs_mempool, sizeof(*sys.argv) * (sys.argc + args_left + 2)); + if(sys.argc == 0) new_argv[0] = "dummy"; // Can't really happen. else - new_argv[0] = com_argv[0]; + new_argv[0] = sys.argv[0]; ++i; p = buf; while(COM_ParseToken_Console(&p)) @@ -1778,62 +1909,20 @@ static void COM_InsertFlags(const char *buf) { if(i > args_left) break; q = (char *)Mem_Alloc(fs_mempool, sz); - strlcpy(q, com_token, sz); + dp_strlcpy(q, com_token, sz); new_argv[i] = q; ++i; } // Now: i <= args_left + 1. - if (com_argc >= 1) + if (sys.argc >= 1) { - memcpy((char *)(&new_argv[i]), &com_argv[1], sizeof(*com_argv) * (com_argc - 1)); - i += com_argc - 1; + memcpy((char *)(&new_argv[i]), &sys.argv[1], sizeof(*sys.argv) * (sys.argc - 1)); + i += sys.argc - 1; } - // Now: i <= args_left + (com_argc || 1). + // Now: i <= args_left + (sys.argc || 1). new_argv[i] = NULL; - com_argv = new_argv; - com_argc = i; -} - -/* -================ -FS_Init_SelfPack -================ -*/ -void FS_Init_SelfPack (void) -{ - PK3_OpenLibrary (); - fs_mempool = Mem_AllocPool("file management", 0, NULL); - - // Load darkplaces.opt from the FS. - if (!COM_CheckParm("-noopt")) - { - char *buf = (char *) FS_SysLoadFile("darkplaces.opt", tempmempool, true, NULL); - if(buf) - COM_InsertFlags(buf); - Mem_Free(buf); - } - -#ifndef USE_RWOPS - // Provide the SelfPack. - if (!COM_CheckParm("-noselfpack")) - { - if (com_selffd >= 0) - { - fs_selfpack = FS_LoadPackPK3FromFD(com_argv[0], com_selffd, true); - if(fs_selfpack) - { - FS_AddSelfPack(); - if (!COM_CheckParm("-noopt")) - { - char *buf = (char *) FS_LoadFile("darkplaces.opt", tempmempool, true, NULL); - if(buf) - COM_InsertFlags(buf); - Mem_Free(buf); - } - } - } - } -#endif + sys.argv = new_argv; + sys.argc = i; } static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t userdirsize) @@ -1843,19 +1932,21 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use { // fs_basedir is "" by default, to utilize this you can simply add your gamedir to the Resources in xcode // fs_userdir stores configurations to the Documents folder of the app - strlcpy(userdir, "../Documents/", MAX_OSPATH); + dp_strlcpy(userdir, "../Documents/", MAX_OSPATH); return 1; } 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]; @@ -1865,28 +1956,31 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use default: return -1; case USERDIRMODE_NOHOME: - strlcpy(userdir, fs_basedir, userdirsize); + dp_strlcpy(userdir, fs_basedir, userdirsize); break; case USERDIRMODE_MYGAMES: if (!shfolder_dll) - Sys_LoadLibrary(shfolderdllnames, &shfolder_dll, shfolderfuncs); + 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; @@ -1895,9 +1989,9 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use return -1; case USERDIRMODE_SAVEDGAMES: if (!shell32_dll) - Sys_LoadLibrary(shell32dllnames, &shell32_dll, shell32funcs); + Sys_LoadDependency(shell32dllnames, &shell32_dll, shell32funcs); if (!ole32_dll) - Sys_LoadLibrary(ole32dllnames, &ole32_dll, ole32funcs); + Sys_LoadDependency(ole32dllnames, &ole32_dll, ole32funcs); if (qSHGetKnownFolderPath && qCoInitializeEx && qCoTaskMemFree && qCoUninitialize) { savedgamesdir[0] = 0; @@ -1911,12 +2005,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(); @@ -1938,7 +2027,7 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use default: return -1; case USERDIRMODE_NOHOME: - strlcpy(userdir, fs_basedir, userdirsize); + dp_strlcpy(userdir, fs_basedir, userdirsize); break; case USERDIRMODE_HOME: homedir = getenv("HOME"); @@ -2007,12 +2096,22 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use #endif } -/* -================ -FS_Init -================ -*/ -void FS_Init (void) +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)"); + Cmd_AddCommand(CF_SHARED, "fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes"); + Cmd_AddCommand(CF_SHARED, "path", FS_Path_f, "print searchpath (game directories and archives)"); + Cmd_AddCommand(CF_SHARED, "dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line"); + Cmd_AddCommand(CF_SHARED, "ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line"); + Cmd_AddCommand(CF_SHARED, "which", FS_Which_f, "accepts a file name as argument and reports where the file is taken from"); +} + +static void FS_Init_Dir (void) { const char *p; int i; @@ -2024,10 +2123,10 @@ void FS_Init (void) // -basedir // Overrides the system supplied base directory (under GAMENAME) // COMMANDLINEOPTION: Filesystem: -basedir chooses what base directory the game data is in, inside this there should be a data directory for the game (for example id1) - i = COM_CheckParm ("-basedir"); - if (i && i < com_argc-1) + i = Sys_CheckParm ("-basedir"); + if (i && i < sys.argc-1) { - strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir)); + dp_strlcpy (fs_basedir, sys.argv[i+1], sizeof (fs_basedir)); i = (int)strlen (fs_basedir); if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/')) fs_basedir[i-1] = 0; @@ -2036,15 +2135,15 @@ void FS_Init (void) { // If the base directory is explicitly defined by the compilation process #ifdef DP_FS_BASEDIR - strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir)); + dp_strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir)); #elif defined(__ANDROID__) dpsnprintf(fs_basedir, sizeof(fs_basedir), "/sdcard/%s/", gameuserdirname); #elif defined(MACOSX) // FIXME: is there a better way to find the directory outside the .app, without using Objective-C? - if (strstr(com_argv[0], ".app/")) + if (strstr(sys.argv[0], ".app/")) { char *split; - strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir)); + dp_strlcpy(fs_basedir, sys.argv[0], sizeof(fs_basedir)); split = strstr(fs_basedir, ".app/"); if (split) { @@ -2056,7 +2155,7 @@ void FS_Init (void) if (stat(va(vabuf, sizeof(vabuf), "%s/Contents/Resources/%s", fs_basedir, gamedirname1), &statresult) == 0) { // found gamedir inside Resources, use it - strlcat(fs_basedir, "Contents/Resources/", sizeof(fs_basedir)); + dp_strlcat(fs_basedir, "Contents/Resources/", sizeof(fs_basedir)); } else { @@ -2075,17 +2174,17 @@ void FS_Init (void) memset(fs_basedir + sizeof(fs_basedir) - 2, 0, 2); // add a path separator to the end of the basedir if it lacks one if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\') - strlcat(fs_basedir, "/", sizeof(fs_basedir)); + dp_strlcat(fs_basedir, "/", sizeof(fs_basedir)); // Add the personal game directory - if((i = COM_CheckParm("-userdir")) && i < com_argc - 1) - dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/", com_argv[i+1]); - else if (COM_CheckParm("-nohome")) + if((i = Sys_CheckParm("-userdir")) && i < sys.argc - 1) + dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/", sys.argv[i+1]); + else if (Sys_CheckParm("-nohome")) *fs_userdir = 0; // user wants roaming installation, no userdir else { #ifdef DP_FS_USERDIR - strlcpy(fs_userdir, DP_FS_USERDIR, sizeof(fs_userdir)); + dp_strlcpy(fs_userdir, DP_FS_USERDIR, sizeof(fs_userdir)); #else int dirmode; int highestuserdirmode = USERDIRMODE_COUNT - 1; @@ -2097,9 +2196,9 @@ void FS_Init (void) preferreduserdirmode = USERDIRMODE_NOHOME; # endif // check what limitations the user wants to impose - if (COM_CheckParm("-home")) preferreduserdirmode = USERDIRMODE_HOME; - if (COM_CheckParm("-mygames")) preferreduserdirmode = USERDIRMODE_MYGAMES; - if (COM_CheckParm("-savedgames")) preferreduserdirmode = USERDIRMODE_SAVEDGAMES; + if (Sys_CheckParm("-home")) preferreduserdirmode = USERDIRMODE_HOME; + if (Sys_CheckParm("-mygames")) preferreduserdirmode = USERDIRMODE_MYGAMES; + if (Sys_CheckParm("-savedgames")) preferreduserdirmode = USERDIRMODE_SAVEDGAMES; // gather the status of the possible userdirs for (dirmode = 0;dirmode < USERDIRMODE_COUNT;dirmode++) { @@ -2138,32 +2237,32 @@ void FS_Init (void) p = FS_CheckGameDir(gamedirname1); if(!p || p == fs_checkgamedir_missing) - Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1); + Con_Printf(CON_WARN "WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1); if(gamedirname2) { p = FS_CheckGameDir(gamedirname2); if(!p || p == fs_checkgamedir_missing) - Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2); + Con_Printf(CON_WARN "WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2); } // -game // Adds basedir/gamedir as an override game // LadyHavoc: now supports multiple -game directories - for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++) + for (i = 1;i < sys.argc && fs_numgamedirs < MAX_GAMEDIRS;i++) { - if (!com_argv[i]) + if (!sys.argv[i]) continue; - if (!strcmp (com_argv[i], "-game") && i < com_argc-1) + if (!strcmp (sys.argv[i], "-game") && i < sys.argc-1) { i++; - p = FS_CheckGameDir(com_argv[i]); + p = FS_CheckGameDir(sys.argv[i]); if(!p) - Sys_Error("Nasty -game name rejected: %s", com_argv[i]); + Con_Printf("WARNING: Nasty -game name rejected: %s\n", sys.argv[i]); if(p == fs_checkgamedir_missing) - Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]); + Con_Printf(CON_WARN "WARNING: -game %s%s/ not found!\n", fs_basedir, sys.argv[i]); // add the gamedir to the list of active gamedirs - strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs])); + dp_strlcpy (fs_gamedirs[fs_numgamedirs], sys.argv[i], sizeof(fs_gamedirs[fs_numgamedirs])); fs_numgamedirs++; } } @@ -2175,18 +2274,69 @@ void FS_Init (void) fs_mutex = Thread_CreateMutex(); } -void FS_Init_Commands(void) +/* +================ +FS_Init_SelfPack +================ +*/ +void FS_Init_SelfPack (void) { - Cvar_RegisterVariable (&scr_screenshot_name); - Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions); - Cvar_RegisterVariable (&cvar_fs_gamedir); + char *buf; - Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)"); - Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes"); - Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)"); - Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line"); - Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line"); - Cmd_AddCommand ("which", FS_Which_f, "accepts a file name as argument and reports where the file is taken from"); + // Load darkplaces.opt from the FS. + if (!Sys_CheckParm("-noopt")) + { + buf = (char *) FS_SysLoadFile("darkplaces.opt", tempmempool, true, NULL); + if(buf) + { + COM_InsertFlags(buf); + Mem_Free(buf); + } + } + +#ifndef USE_RWOPS + // Provide the SelfPack. + if (!Sys_CheckParm("-noselfpack") && sys.selffd >= 0) + { + fs_selfpack = FS_LoadPackPK3FromFD(sys.argv[0], sys.selffd, true); + if(fs_selfpack) + { + FS_AddSelfPack(); + if (!Sys_CheckParm("-noopt")) + { + buf = (char *) FS_LoadFile("darkplaces.opt", tempmempool, true, NULL); + if(buf) + { + COM_InsertFlags(buf); + Mem_Free(buf); + } + } + } + } +#endif +} + +/* +================ +FS_Init +================ +*/ + +void FS_Init(void) +{ + fs_mempool = Mem_AllocPool("file management", 0, NULL); + + FS_Init_Commands(); + + PK3_OpenLibrary (); + + // initialize the self-pack (must be before COM_InitGameType as it may add command line options) + FS_Init_SelfPack(); + + // detect gamemode from commandline options or executable name + COM_InitGameType(); + + FS_Init_Dir(); } /* @@ -2204,21 +2354,24 @@ void FS_Shutdown (void) PK3_CloseLibrary (); #ifdef WIN32 - Sys_UnloadLibrary (&shfolder_dll); - Sys_UnloadLibrary (&shell32_dll); - Sys_UnloadLibrary (&ole32_dll); + Sys_FreeLibrary (&shfolder_dll); + Sys_FreeLibrary (&shell32_dll); + Sys_FreeLibrary (&ole32_dll); #endif if (fs_mutex) Thread_DestroyMutex(fs_mutex); } -static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qboolean nonblocking) +static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qbool nonblocking) { filedesc_t handle = FILEDESC_INVALID; int mod, opt; unsigned int ind; - qboolean dolock = false; + qbool dolock = false; + #ifdef WIN32 + wchar filepathw[WSTRBUF] = {0}; + #endif // Parse the mode string switch (mode[0]) @@ -2236,7 +2389,7 @@ static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qbo opt = O_CREAT | O_APPEND; break; default: - Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode); + Con_Printf(CON_ERROR "FS_SysOpen(%s, %s): invalid mode\n", filepath, mode); return FILEDESC_INVALID; } for (ind = 1; mode[ind] != '\0'; ind++) @@ -2253,7 +2406,7 @@ static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qbo dolock = true; break; default: - Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n", + Con_Printf(CON_ERROR "FS_SysOpen(%s, %s): unknown character in mode (%c)\n", filepath, mode, mode[ind]); } } @@ -2261,7 +2414,7 @@ static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qbo if (nonblocking) opt |= O_NONBLOCK; - if(COM_CheckParm("-readonly") && mod != O_RDONLY) + if(Sys_CheckParm("-readonly") && mod != O_RDONLY) return FILEDESC_INVALID; #if USE_RWOPS @@ -2270,10 +2423,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); @@ -2296,7 +2450,7 @@ static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qbo return handle; } -int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking) +int FS_SysOpenFD(const char *filepath, const char *mode, qbool nonblocking) { #ifdef USE_RWOPS return -1; @@ -2312,7 +2466,7 @@ FS_SysOpen Internal function used to create a qfile_t and open the relevant non-packed file on disk ==================== */ -qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking) +qfile_t* FS_SysOpen (const char* filepath, const char* mode, qbool nonblocking) { qfile_t* file; @@ -2363,7 +2517,7 @@ static qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind) // No Zlib DLL = no compressed files if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED)) { - Con_Printf("WARNING: can't open the compressed file %s\n" + Con_Printf(CON_WARN "WARNING: can't open the compressed file %s\n" "You need the Zlib DLL to use compressed files\n", pfile->name); return NULL; @@ -2446,7 +2600,7 @@ Return true if the path should be rejected due to one of the following: or are just not a good idea for a mod to be using. ==================== */ -int FS_CheckNastyPath (const char *path, qboolean isgamedir) +int FS_CheckNastyPath (const char *path, qbool isgamedir) { // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless if (!path[0]) @@ -2493,6 +2647,20 @@ int FS_CheckNastyPath (const char *path, qboolean isgamedir) return false; } +/* +==================== +FS_SanitizePath + +Sanitize path (replace non-portable characters +with portable ones in-place, etc) +==================== +*/ +void FS_SanitizePath(char *path) +{ + for (; *path; path++) + if (*path == '\\') + *path = '/'; +} /* ==================== @@ -2504,7 +2672,7 @@ Return the searchpath where the file was found (or NULL) and the file index in the package if relevant ==================== */ -static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet) +static searchpath_t *FS_FindFile (const char *name, int *index, const char **canonicalname, qbool quiet) { searchpath_t *search; pack_t *pak; @@ -2542,15 +2710,18 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet) if (index != NULL) *index = -1; + if (canonicalname) + *canonicalname = NULL; return NULL; } if (!quiet && developer_extra.integer) - Con_DPrintf("FS_FindFile: %s in %s\n", - pak->files[middle].name, pak->filename); + Con_DPrintf("FS_FindFile: %s in %s\n", pak->files[middle].name, pak->filename); if (index != NULL) *index = middle; + if (canonicalname) + *canonicalname = pak->files[middle].name; return search; } @@ -2572,6 +2743,8 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet) if (index != NULL) *index = -1; + if (canonicalname) + *canonicalname = name; return search; } } @@ -2582,6 +2755,8 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet) if (index != NULL) *index = -1; + if (canonicalname) + *canonicalname = NULL; return NULL; } @@ -2593,12 +2768,12 @@ FS_OpenReadFile Look for a file in the search paths and open it in read-only mode =========== */ -static qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels) +static qfile_t *FS_OpenReadFile (const char *filename, qbool quiet, qbool nonblocking, int symlinkLevels) { searchpath_t *search; int pack_ind; - search = FS_FindFile (filename, &pack_ind, quiet); + search = FS_FindFile (filename, &pack_ind, NULL, quiet); // Not found? if (search == NULL) @@ -2640,7 +2815,7 @@ static qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean if(count < 0) return NULL; linkbuf[count] = 0; - + // Now combine the paths... mergeslash = strrchr(filename, '/'); mergestart = linkbuf; @@ -2708,7 +2883,7 @@ Open a file in the userpath. The syntax is the same as fopen Used for savegame scanning in menu, and all file writing. ==================== */ -qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qboolean quiet) +qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qbool quiet) { char real_path [MAX_OSPATH]; @@ -2735,7 +2910,7 @@ FS_OpenVirtualFile Open a file. The syntax is the same as fopen ==================== */ -qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet) +qfile_t* FS_OpenVirtualFile (const char* filepath, qbool quiet) { qfile_t *result = NULL; if (FS_CheckNastyPath(filepath, false)) @@ -2758,7 +2933,7 @@ FS_FileFromData Open a file. The syntax is the same as fopen ==================== */ -qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet) +qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qbool quiet) { qfile_t* file; file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file)); @@ -2835,7 +3010,7 @@ fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize) { if (FILEDESC_SEEK (file->handle, file->buff_ind - file->buff_len, SEEK_CUR) == -1) { - Con_Printf("WARNING: could not seek in %s.\n", file->filename); + Con_Printf(CON_WARN "WARNING: could not seek in %s.\n", file->filename); } } @@ -2877,7 +3052,7 @@ fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize) { fs_offset_t count, done; - if (buffersize == 0) + if (buffersize == 0 || !buffer) return 0; // Get rid of the ungetc character @@ -3242,9 +3417,9 @@ int FS_Seek (qfile_t* file, fs_offset_t offset, int whence) buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize); // Skip all data until we reach the requested offset - while (offset > file->position) + while (offset > (file->position - file->buff_len + file->buff_ind)) { - fs_offset_t diff = offset - file->position; + fs_offset_t diff = offset - (file->position - file->buff_len + file->buff_ind); fs_offset_t count, len; count = (diff > buffersize) ? buffersize : diff; @@ -3310,7 +3485,7 @@ Loads full content of a qfile_t and closes it. Always appends a 0 byte. ============ */ -static unsigned char *FS_LoadAndCloseQFile (qfile_t *file, const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer) +static unsigned char *FS_LoadAndCloseQFile (qfile_t *file, const char *path, mempool_t *pool, qbool quiet, fs_offset_t *filesizepointer) { unsigned char *buf = NULL; fs_offset_t filesize = 0; @@ -3347,7 +3522,7 @@ Filename are relative to the quake directory. Always appends a 0 byte. ============ */ -unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer) +unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qbool quiet, fs_offset_t *filesizepointer) { qfile_t *file = FS_OpenVirtualFile(path, quiet); return FS_LoadAndCloseQFile(file, path, pool, quiet, filesizepointer); @@ -3362,7 +3537,7 @@ Filename are OS paths. Always appends a 0 byte. ============ */ -unsigned char *FS_SysLoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer) +unsigned char *FS_SysLoadFile (const char *path, mempool_t *pool, qbool quiet, fs_offset_t *filesizepointer) { qfile_t *file = FS_SysOpen(path, "rb", false); return FS_LoadAndCloseQFile(file, path, pool, quiet, filesizepointer); @@ -3376,7 +3551,7 @@ FS_WriteFile The filename will be prefixed by the current game directory ============ */ -qboolean FS_WriteFileInBlocks (const char *filename, const void *const *data, const fs_offset_t *len, size_t count) +qbool FS_WriteFileInBlocks (const char *filename, const void *const *data, const fs_offset_t *len, size_t count) { qfile_t *file; size_t i; @@ -3399,7 +3574,7 @@ qboolean FS_WriteFileInBlocks (const char *filename, const void *const *data, co return true; } -qboolean FS_WriteFile (const char *filename, const void *data, fs_offset_t len) +qbool FS_WriteFile (const char *filename, const void *data, fs_offset_t len) { return FS_WriteFileInBlocks(filename, &data, &len, 1); } @@ -3463,7 +3638,7 @@ void FS_DefaultExtension (char *path, const char *extension, size_t size_path) src--; } - strlcat (path, extension, size_path); + dp_strlcat (path, extension, size_path); } @@ -3479,7 +3654,7 @@ int FS_FileType (const char *filename) searchpath_t *search; char fullpath[MAX_OSPATH]; - search = FS_FindFile (filename, NULL, true); + search = FS_FindFile (filename, NULL, NULL, true); if(!search) return FS_FILETYPE_NONE; @@ -3496,11 +3671,15 @@ int FS_FileType (const char *filename) FS_FileExists Look for a file in the packages and in the filesystem +Returns its canonical name (VFS path with correct capitalisation) if found, else NULL. +If the file is found outside a pak, this will be the same pointer as passed in. ================== */ -qboolean FS_FileExists (const char *filename) +const char *FS_FileExists (const char *filename) { - return (FS_FindFile (filename, NULL, true) != NULL); + const char *canonicalname; + + return FS_FindFile(filename, NULL, &canonicalname, true) ? canonicalname : NULL; } @@ -3518,8 +3697,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; @@ -3544,7 +3725,7 @@ int FS_SysFileType (const char *path) #endif } -qboolean FS_SysFileExists (const char *path) +qbool FS_SysFileExists (const char *path) { return FS_SysFileType (path) != FS_FILETYPE_NONE; } @@ -3556,7 +3737,7 @@ FS_Search Allocate and fill a search structure with information on matching filenames. =========== */ -fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet) +fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet, const char *packfile) { fssearch_t *search; searchpath_t *searchpath; @@ -3564,7 +3745,8 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet) int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex; stringlist_t resultlist; stringlist_t dirlist; - const char *slash, *backslash, *colon, *separator; + stringlist_t matchedSet, foundSet; + const char *start, *slash, *backslash, *colon, *separator; char *basepath; for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++) @@ -3598,10 +3780,15 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet) { // look through all the pak file elements pak = searchpath->pack; + if(packfile) + { + if(strcmp(packfile, pak->shortname)) + continue; + } for (i = 0;i < pak->numfiles;i++) { char temp[MAX_OSPATH]; - strlcpy(temp, pak->files[i].name, sizeof(temp)); + dp_strlcpy(temp, pak->files[i].name, sizeof(temp)); while (temp[0]) { if (matchpattern(temp, (char *)pattern, true)) @@ -3634,8 +3821,10 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet) } else { - stringlist_t matchedSet, foundSet; - const char *start = pattern; + if(packfile) + continue; + + start = pattern; stringlistinit(&matchedSet); stringlistinit(&foundSet); @@ -3671,7 +3860,7 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet) // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string) // copy everything up except nextseperator - strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1))); + dp_ustr2stp(subpattern, sizeof(subpattern), pattern, nextseparator - pattern); // find the last '/' before the wildcard prevseparator = strrchr( subpattern, '/' ); if (!prevseparator) @@ -3680,13 +3869,13 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet) prevseparator++; // copy everything from start to the previous including the '/' (before the wildcard) // everything up to start is already included in the path of matchedSet's entries - strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1))); + dp_ustr2stp(subpath, sizeof(subpath), start, (prevseparator - subpattern) - (start - pattern)); // for each entry in matchedSet try to open the subdirectories specified in subpath for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) { char temp[MAX_OSPATH]; - strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) ); - strlcat( temp, subpath, sizeof(temp) ); + dp_strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) ); + dp_strlcat( temp, subpath, sizeof(temp) ); listdirectory( &foundSet, searchpath->filename, temp ); } if( dirlistindex == 0 ) { @@ -3772,7 +3961,7 @@ static int FS_ListDirectory(const char *pattern, int oneperline) const char *name; char linebuf[MAX_INPUTLINE]; fssearch_t *search; - search = FS_Search(pattern, true, true); + search = FS_Search(pattern, true, true, NULL); if (!search) return 0; numfiles = search->numfilenames; @@ -3829,44 +4018,44 @@ static int FS_ListDirectory(const char *pattern, int oneperline) return (int)numfiles; } -static void FS_ListDirectoryCmd (const char* cmdname, int oneperline) +static void FS_ListDirectoryCmd (cmd_state_t *cmd, const char* cmdname, int oneperline) { const char *pattern; - if (Cmd_Argc() >= 3) + if (Cmd_Argc(cmd) >= 3) { Con_Printf("usage:\n%s [path/pattern]\n", cmdname); return; } - if (Cmd_Argc() == 2) - pattern = Cmd_Argv(1); + if (Cmd_Argc(cmd) == 2) + pattern = Cmd_Argv(cmd, 1); else pattern = "*"; if (!FS_ListDirectory(pattern, oneperline)) Con_Print("No files found.\n"); } -void FS_Dir_f(void) +void FS_Dir_f(cmd_state_t *cmd) { - FS_ListDirectoryCmd("dir", true); + FS_ListDirectoryCmd(cmd, "dir", true); } -void FS_Ls_f(void) +void FS_Ls_f(cmd_state_t *cmd) { - FS_ListDirectoryCmd("ls", false); + FS_ListDirectoryCmd(cmd, "ls", false); } -void FS_Which_f(void) +void FS_Which_f(cmd_state_t *cmd) { const char *filename; int index; searchpath_t *sp; - if (Cmd_Argc() != 2) + if (Cmd_Argc(cmd) != 2) { - Con_Printf("usage:\n%s \n", Cmd_Argv(0)); + Con_Printf("usage:\n%s \n", Cmd_Argv(cmd, 0)); return; - } - filename = Cmd_Argv(1); - sp = FS_FindFile(filename, &index, true); + } + filename = Cmd_Argv(cmd, 1); + sp = FS_FindFile(filename, &index, NULL, true); if (!sp) { Con_Printf("%s isn't anywhere\n", filename); return; @@ -3886,7 +4075,7 @@ void FS_Which_f(void) const char *FS_WhichPack(const char *filename) { int index; - searchpath_t *sp = FS_FindFile(filename, &index, true); + searchpath_t *sp = FS_FindFile(filename, &index, NULL, true); if(sp && sp->pack) return sp->pack->shortname; else if(sp) @@ -3904,7 +4093,7 @@ Look for a proof of purchase file file in the requested package If it is found, this file should NOT be downloaded. ==================== */ -qboolean FS_IsRegisteredQuakePack(const char *name) +qbool FS_IsRegisteredQuakePack(const char *name) { searchpath_t *search; pack_t *pak; @@ -4017,7 +4206,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"); @@ -4044,7 +4233,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; } @@ -4111,7 +4300,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; @@ -4127,7 +4316,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) { @@ -4155,6 +4344,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; }