/fs.c
C | 4160 lines | 2814 code | 521 blank | 825 comment | 611 complexity | 1fd6f523645c86ace0a9b8f1470e40d3 MD5 | raw file
Possible License(s): GPL-2.0
Large files files are truncated, but you can click here to view the full file
- /*
- DarkPlaces file system
- Copyright (C) 2003-2006 Mathieu Olivier
- 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 2
- 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:
- Free Software Foundation, Inc.
- 59 Temple Place - Suite 330
- Boston, MA 02111-1307, USA
- */
- #include <limits.h>
- #include <fcntl.h>
- #ifdef WIN32
- # include <direct.h>
- # include <io.h>
- # include <shlobj.h>
- # include <sys/stat.h>
- # include <share.h>
- #else
- # include <pwd.h>
- # include <sys/stat.h>
- # include <unistd.h>
- #endif
- #include "quakedef.h"
- #if TARGET_OS_IPHONE
- // include SDL for IPHONEOS code
- # include <SDL.h>
- #endif
- #include "thread.h"
- #include "fs.h"
- #include "wad.h"
- // Win32 requires us to add O_BINARY, but the other OSes don't have it
- #ifndef O_BINARY
- # define O_BINARY 0
- #endif
- // In case the system doesn't support the O_NONBLOCK flag
- #ifndef O_NONBLOCK
- # define O_NONBLOCK 0
- #endif
- // largefile support for Win32
- #ifdef WIN32
- #undef lseek
- # define lseek _lseeki64
- #endif
- // suppress deprecated warnings
- #if _MSC_VER >= 1400
- # define read _read
- # define write _write
- # define close _close
- # define unlink _unlink
- # define dup _dup
- #endif
- #if USE_RWOPS
- # include <SDL.h>
- typedef SDL_RWops *filedesc_t;
- # define FILEDESC_INVALID NULL
- # define FILEDESC_ISVALID(fd) ((fd) != NULL)
- # define FILEDESC_READ(fd,buf,count) ((fs_offset_t)SDL_RWread(fd, buf, 1, count))
- # define FILEDESC_WRITE(fd,buf,count) ((fs_offset_t)SDL_RWwrite(fd, buf, 1, count))
- # define FILEDESC_CLOSE SDL_RWclose
- # define FILEDESC_SEEK SDL_RWseek
- static filedesc_t FILEDESC_DUP(const char *filename, filedesc_t fd) {
- filedesc_t new_fd = SDL_RWFromFile(filename, "rb");
- if (SDL_RWseek(new_fd, SDL_RWseek(fd, 0, RW_SEEK_CUR), RW_SEEK_SET) < 0) {
- SDL_RWclose(new_fd);
- return NULL;
- }
- return new_fd;
- }
- # define unlink(name) Con_DPrintf("Sorry, no unlink support when trying to unlink %s.\n", (name))
- #else
- typedef int filedesc_t;
- # define FILEDESC_INVALID -1
- # define FILEDESC_ISVALID(fd) ((fd) != -1)
- # define FILEDESC_READ read
- # define FILEDESC_WRITE write
- # define FILEDESC_CLOSE close
- # define FILEDESC_SEEK lseek
- static filedesc_t FILEDESC_DUP(const char *filename, filedesc_t fd) {
- return dup(fd);
- }
- #endif
- /** \page fs File System
- All of Quake's data access is through a hierchal file system, but the contents
- of the file system can be transparently merged from several sources.
- The "base directory" is the path to the directory holding the quake.exe and
- all game directories. The sys_* files pass this to host_init in
- quakeparms_t->basedir. This can be overridden with the "-basedir" command
- line parm to allow code debugging in a different directory. The base
- directory is only used during filesystem initialization.
- The "game directory" is the first tree on the search path and directory that
- all generated files (savegames, screenshots, demos, config files) will be
- saved to. This can be overridden with the "-game" command line parameter.
- The game directory can never be changed while quake is executing. This is a
- precaution against having a malicious server instruct clients to write files
- over areas they shouldn't.
- */
- /*
- =============================================================================
- CONSTANTS
- =============================================================================
- */
- // Magic numbers of a ZIP file (big-endian format)
- #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
- #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
- #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
- // Other constants for ZIP files
- #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
- #define ZIP_END_CDIR_SIZE 22
- #define ZIP_CDIR_CHUNK_BASE_SIZE 46
- #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
- #ifdef LINK_TO_ZLIB
- #include <zlib.h>
- #define qz_inflate inflate
- #define qz_inflateEnd inflateEnd
- #define qz_inflateInit2_ inflateInit2_
- #define qz_inflateReset inflateReset
- #define qz_deflateInit2_ deflateInit2_
- #define qz_deflateEnd deflateEnd
- #define qz_deflate deflate
- #define Z_MEMLEVEL_DEFAULT 8
- #else
- // Zlib constants (from zlib.h)
- #define Z_SYNC_FLUSH 2
- #define MAX_WBITS 15
- #define Z_OK 0
- #define Z_STREAM_END 1
- #define Z_STREAM_ERROR (-2)
- #define Z_DATA_ERROR (-3)
- #define Z_MEM_ERROR (-4)
- #define Z_BUF_ERROR (-5)
- #define ZLIB_VERSION "1.2.3"
- #define Z_BINARY 0
- #define Z_DEFLATED 8
- #define Z_MEMLEVEL_DEFAULT 8
- #define Z_NULL 0
- #define Z_DEFAULT_COMPRESSION (-1)
- #define Z_NO_FLUSH 0
- #define Z_SYNC_FLUSH 2
- #define Z_FULL_FLUSH 3
- #define Z_FINISH 4
- // Uncomment the following line if the zlib DLL you have still uses
- // the 1.1.x series calling convention on Win32 (WINAPI)
- //#define ZLIB_USES_WINAPI
- /*
- =============================================================================
- TYPES
- =============================================================================
- */
- /*! Zlib stream (from zlib.h)
- * \warning: some pointers we don't use directly have
- * been cast to "void*" for a matter of simplicity
- */
- typedef struct
- {
- unsigned char *next_in; ///< next input byte
- unsigned int avail_in; ///< number of bytes available at next_in
- unsigned long total_in; ///< total nb of input bytes read so far
- unsigned char *next_out; ///< next output byte should be put there
- unsigned int avail_out; ///< remaining free space at next_out
- unsigned long total_out; ///< total nb of bytes output so far
- char *msg; ///< last error message, NULL if no error
- void *state; ///< not visible by applications
- void *zalloc; ///< used to allocate the internal state
- void *zfree; ///< used to free the internal state
- void *opaque; ///< private data object passed to zalloc and zfree
- int data_type; ///< best guess about the data type: ascii or binary
- unsigned long adler; ///< adler32 value of the uncompressed data
- unsigned long reserved; ///< reserved for future use
- } z_stream;
- #endif
- /// inside a package (PAK or PK3)
- #define QFILE_FLAG_PACKED (1 << 0)
- /// file is compressed using the deflate algorithm (PK3 only)
- #define QFILE_FLAG_DEFLATED (1 << 1)
- /// file is actually already loaded data
- #define QFILE_FLAG_DATA (1 << 2)
- /// real file will be removed on close
- #define QFILE_FLAG_REMOVE (1 << 3)
- #define FILE_BUFF_SIZE 2048
- typedef struct
- {
- z_stream zstream;
- size_t comp_length; ///< length of the compressed file
- size_t in_ind, in_len; ///< input buffer current index and length
- size_t in_position; ///< position in the compressed file
- unsigned char input [FILE_BUFF_SIZE];
- } ztoolkit_t;
- struct qfile_s
- {
- int flags;
- filedesc_t handle; ///< file descriptor
- fs_offset_t real_length; ///< uncompressed file size (for files opened in "read" mode)
- fs_offset_t position; ///< current position in the file
- fs_offset_t offset; ///< offset into the package (0 if external file)
- int ungetc; ///< single stored character from ungetc, cleared to EOF when read
- // Contents buffer
- fs_offset_t buff_ind, buff_len; ///< buffer current index and length
- unsigned char buff [FILE_BUFF_SIZE];
- ztoolkit_t* ztk; ///< For zipped files.
- const unsigned char *data; ///< For data files.
- const char *filename; ///< Kept around for QFILE_FLAG_REMOVE, unused otherwise
- };
- // ------ PK3 files on disk ------ //
- // You can get the complete ZIP format description from PKWARE website
- typedef struct pk3_endOfCentralDir_s
- {
- unsigned int signature;
- unsigned short disknum;
- unsigned short cdir_disknum; ///< number of the disk with the start of the central directory
- unsigned short localentries; ///< number of entries in the central directory on this disk
- unsigned short nbentries; ///< total number of entries in the central directory on this disk
- unsigned int cdir_size; ///< size of the central directory
- unsigned int cdir_offset; ///< with respect to the starting disk number
- unsigned short comment_size;
- fs_offset_t prepended_garbage;
- } pk3_endOfCentralDir_t;
- // ------ PAK files on disk ------ //
- typedef struct dpackfile_s
- {
- char name[56];
- int filepos, filelen;
- } dpackfile_t;
- typedef struct dpackheader_s
- {
- char id[4];
- int dirofs;
- int dirlen;
- } dpackheader_t;
- /*! \name Packages in memory
- * @{
- */
- /// the offset in packfile_t is the true contents offset
- #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
- /// file compressed using the deflate algorithm
- #define PACKFILE_FLAG_DEFLATED (1 << 1)
- /// file is a symbolic link
- #define PACKFILE_FLAG_SYMLINK (1 << 2)
- typedef struct packfile_s
- {
- char name [MAX_QPATH];
- int flags;
- fs_offset_t offset;
- fs_offset_t packsize; ///< size in the package
- fs_offset_t realsize; ///< real file size (uncompressed)
- } packfile_t;
- typedef struct pack_s
- {
- char filename [MAX_OSPATH];
- char shortname [MAX_QPATH];
- filedesc_t handle;
- int ignorecase; ///< PK3 ignores case
- int numfiles;
- qboolean vpack;
- packfile_t *files;
- } pack_t;
- //@}
- /// Search paths for files (including packages)
- typedef struct searchpath_s
- {
- // only one of filename / pack will be used
- char filename[MAX_OSPATH];
- pack_t *pack;
- struct searchpath_s *next;
- } searchpath_t;
- /*
- =============================================================================
- FUNCTION PROTOTYPES
- =============================================================================
- */
- void FS_Dir_f(void);
- void FS_Ls_f(void);
- void FS_Which_f(void);
- static searchpath_t *FS_FindFile (const char *name, int* index, qboolean 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);
- /*
- =============================================================================
- VARIABLES
- =============================================================================
- */
- mempool_t *fs_mempool;
- void *fs_mutex = NULL;
- searchpath_t *fs_searchpaths = NULL;
- const char *const fs_checkgamedir_missing = "missing";
- #define MAX_FILES_IN_PACK 65536
- char fs_userdir[MAX_OSPATH];
- char fs_gamedir[MAX_OSPATH];
- char fs_basedir[MAX_OSPATH];
- static pack_t *fs_selfpack = NULL;
- // list of active game directories (empty if not running a mod)
- int fs_numgamedirs = 0;
- char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
- // list of all gamedirs with modinfo.txt
- 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)"};
- /*
- =============================================================================
- PRIVATE FUNCTIONS - PK3 HANDLING
- =============================================================================
- */
- #ifndef LINK_TO_ZLIB
- // Functions exported from zlib
- #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
- # define ZEXPORT WINAPI
- #else
- # define ZEXPORT
- #endif
- static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
- static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
- static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
- static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
- static int (ZEXPORT *qz_deflateInit2_) (z_stream* strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size);
- static int (ZEXPORT *qz_deflateEnd) (z_stream* strm);
- static int (ZEXPORT *qz_deflate) (z_stream* strm, int flush);
- #endif
- #define qz_inflateInit2(strm, windowBits) \
- qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
- #define qz_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
- qz_deflateInit2_((strm), (level), (method), (windowBits), (memLevel), (strategy), ZLIB_VERSION, sizeof(z_stream))
- #ifndef LINK_TO_ZLIB
- // qz_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
- static dllfunction_t zlibfuncs[] =
- {
- {"inflate", (void **) &qz_inflate},
- {"inflateEnd", (void **) &qz_inflateEnd},
- {"inflateInit2_", (void **) &qz_inflateInit2_},
- {"inflateReset", (void **) &qz_inflateReset},
- {"deflateInit2_", (void **) &qz_deflateInit2_},
- {"deflateEnd", (void **) &qz_deflateEnd},
- {"deflate", (void **) &qz_deflate},
- {NULL, NULL}
- };
- /// Handle for Zlib DLL
- static dllhandle_t zlib_dll = NULL;
- #endif
- #ifdef WIN32
- static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
- static dllfunction_t shfolderfuncs[] =
- {
- {"SHGetFolderPathA", (void **) &qSHGetFolderPath},
- {NULL, NULL}
- };
- static const char* shfolderdllnames [] =
- {
- "shfolder.dll", // IE 4, or Win NT and higher
- NULL
- };
- static dllhandle_t shfolder_dll = NULL;
- 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
- static HRESULT (WINAPI *qSHGetKnownFolderPath) (qREFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath);
- static dllfunction_t shell32funcs[] =
- {
- {"SHGetKnownFolderPath", (void **) &qSHGetKnownFolderPath},
- {NULL, NULL}
- };
- static const char* shell32dllnames [] =
- {
- "shell32.dll", // Vista and higher
- NULL
- };
- static dllhandle_t shell32_dll = NULL;
- static HRESULT (WINAPI *qCoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit);
- static void (WINAPI *qCoUninitialize)(void);
- static void (WINAPI *qCoTaskMemFree)(LPVOID pv);
- static dllfunction_t ole32funcs[] =
- {
- {"CoInitializeEx", (void **) &qCoInitializeEx},
- {"CoUninitialize", (void **) &qCoUninitialize},
- {"CoTaskMemFree", (void **) &qCoTaskMemFree},
- {NULL, NULL}
- };
- static const char* ole32dllnames [] =
- {
- "ole32.dll", // 2000 and higher
- NULL
- };
- static dllhandle_t ole32_dll = NULL;
- #endif
- /*
- ====================
- PK3_CloseLibrary
- Unload the Zlib DLL
- ====================
- */
- static void PK3_CloseLibrary (void)
- {
- #ifndef LINK_TO_ZLIB
- Sys_UnloadLibrary (&zlib_dll);
- #endif
- }
- /*
- ====================
- PK3_OpenLibrary
- Try to load the Zlib DLL
- ====================
- */
- static qboolean PK3_OpenLibrary (void)
- {
- #ifdef LINK_TO_ZLIB
- return true;
- #else
- const char* dllnames [] =
- {
- #if defined(WIN32)
- # ifdef ZLIB_USES_WINAPI
- "zlibwapi.dll",
- "zlib.dll",
- # else
- "zlib1.dll",
- # endif
- #elif defined(MACOSX)
- "libz.dylib",
- #else
- "libz.so.1",
- "libz.so",
- #endif
- NULL
- };
- // Already loaded?
- if (zlib_dll)
- return true;
- // Load the DLL
- return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
- #endif
- }
- /*
- ====================
- FS_HasZlib
- See if zlib is available
- ====================
- */
- qboolean FS_HasZlib(void)
- {
- #ifdef LINK_TO_ZLIB
- return true;
- #else
- PK3_OpenLibrary(); // to be safe
- return (zlib_dll != 0);
- #endif
- }
- /*
- ====================
- 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)
- {
- fs_offset_t filesize, maxsize;
- unsigned char *buffer, *ptr;
- int ind;
- // Get the package size
- filesize = FILEDESC_SEEK (packhandle, 0, SEEK_END);
- if (filesize < ZIP_END_CDIR_SIZE)
- return false;
- // Load the end of the file in memory
- if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
- maxsize = filesize;
- else
- maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
- buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
- FILEDESC_SEEK (packhandle, filesize - maxsize, SEEK_SET);
- if (FILEDESC_READ (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
- {
- Mem_Free (buffer);
- return false;
- }
- // Look for the end of central dir signature around the end of the file
- maxsize -= ZIP_END_CDIR_SIZE;
- ptr = &buffer[maxsize];
- ind = 0;
- while (BuffBigLong (ptr) != ZIP_END_HEADER)
- {
- if (ind == maxsize)
- {
- Mem_Free (buffer);
- return false;
- }
- ind++;
- ptr--;
- }
- memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
- eocd->signature = LittleLong (eocd->signature);
- eocd->disknum = LittleShort (eocd->disknum);
- eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
- eocd->localentries = LittleShort (eocd->localentries);
- eocd->nbentries = LittleShort (eocd->nbentries);
- eocd->cdir_size = LittleLong (eocd->cdir_size);
- eocd->cdir_offset = LittleLong (eocd->cdir_offset);
- eocd->comment_size = LittleShort (eocd->comment_size);
- eocd->prepended_garbage = filesize - (ind + ZIP_END_CDIR_SIZE) - eocd->cdir_offset - eocd->cdir_size; // this detects "SFX" zip files
- eocd->cdir_offset += eocd->prepended_garbage;
- Mem_Free (buffer);
- if (
- eocd->cdir_size > filesize ||
- eocd->cdir_offset >= filesize ||
- eocd->cdir_offset + eocd->cdir_size > filesize
- )
- {
- // Obviously invalid central directory.
- return false;
- }
- return true;
- }
- /*
- ====================
- PK3_BuildFileList
- Extract the file list from a PK3 file
- ====================
- */
- static int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
- {
- unsigned char *central_dir, *ptr;
- unsigned int ind;
- fs_offset_t remaining;
- // Load the central directory in memory
- central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
- if (FILEDESC_SEEK (pack->handle, eocd->cdir_offset, SEEK_SET) == -1)
- {
- Mem_Free (central_dir);
- return -1;
- }
- if(FILEDESC_READ (pack->handle, central_dir, eocd->cdir_size) != (fs_offset_t) eocd->cdir_size)
- {
- Mem_Free (central_dir);
- return -1;
- }
- // Extract the files properties
- // The parsing is done "by hand" because some fields have variable sizes and
- // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
- remaining = eocd->cdir_size;
- pack->numfiles = 0;
- ptr = central_dir;
- for (ind = 0; ind < eocd->nbentries; ind++)
- {
- fs_offset_t namesize, count;
- // Checking the remaining size
- if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
- {
- Mem_Free (central_dir);
- return -1;
- }
- remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
- // Check header
- if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
- {
- Mem_Free (central_dir);
- return -1;
- }
- namesize = BuffLittleShort (&ptr[28]); // filename length
- // Check encryption, compression, and attributes
- // 1st uint8 : general purpose bit flag
- // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
- //
- // LordHavoc: bit 3 would be a problem if we were scanning the archive
- // but is not a problem in the central directory where the values are
- // always real.
- //
- // bit 3 seems to always be set by the standard Mac OSX zip maker
- //
- // 2nd uint8 : external file attributes
- // Check bits 3 (file is a directory) and 5 (file is a volume (?))
- 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))
- {
- Mem_Free (central_dir);
- return -1;
- }
- // WinZip doesn't use the "directory" attribute, so we need to check the name directly
- if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
- {
- char filename [sizeof (pack->files[0].name)];
- fs_offset_t offset, packsize, realsize;
- int flags;
- // Extract the name (strip it if necessary)
- namesize = min(namesize, (int)sizeof (filename) - 1);
- memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
- filename[namesize] = '\0';
- if (BuffLittleShort (&ptr[10]))
- flags = PACKFILE_FLAG_DEFLATED;
- else
- flags = 0;
- offset = (unsigned int)(BuffLittleLong (&ptr[42]) + eocd->prepended_garbage);
- packsize = (unsigned int)BuffLittleLong (&ptr[20]);
- realsize = (unsigned int)BuffLittleLong (&ptr[24]);
- switch(ptr[5]) // C_VERSION_MADE_BY_1
- {
- case 3: // UNIX_
- case 2: // VMS_
- case 16: // BEOS_
- if((BuffLittleShort(&ptr[40]) & 0120000) == 0120000)
- // can't use S_ISLNK here, as this has to compile on non-UNIX too
- flags |= PACKFILE_FLAG_SYMLINK;
- break;
- }
- FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
- }
- }
- // 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]);
- ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
- remaining -= count;
- }
- // If the package is empty, central_dir is NULL here
- if (central_dir != NULL)
- Mem_Free (central_dir);
- return pack->numfiles;
- }
- /*
- ====================
- FS_LoadPackPK3
- Create a package entry associated with a PK3 file
- ====================
- */
- static pack_t *FS_LoadPackPK3FromFD (const char *packfile, filedesc_t packhandle, qboolean silent)
- {
- pk3_endOfCentralDir_t eocd;
- pack_t *pack;
- int real_nb_files;
- if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
- {
- if(!silent)
- Con_Printf ("%s is not a PK3 file\n", packfile);
- FILEDESC_CLOSE(packhandle);
- return NULL;
- }
- // Multi-volume ZIP archives are NOT allowed
- if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
- {
- Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
- FILEDESC_CLOSE(packhandle);
- return NULL;
- }
- // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
- // since eocd.nbentries is an unsigned 16 bits integer
- #if MAX_FILES_IN_PACK < 65535
- if (eocd.nbentries > MAX_FILES_IN_PACK)
- {
- Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
- FILEDESC_CLOSE(packhandle);
- return NULL;
- }
- #endif
- // 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));
- pack->handle = packhandle;
- pack->numfiles = eocd.nbentries;
- pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
- real_nb_files = PK3_BuildFileList (pack, &eocd);
- if (real_nb_files < 0)
- {
- Con_Printf ("%s is not a valid PK3 file\n", packfile);
- FILEDESC_CLOSE(pack->handle);
- Mem_Free(pack);
- return NULL;
- }
- Con_DPrintf("Added packfile %s (%i files)\n", packfile, real_nb_files);
- return pack;
- }
- static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qboolean nonblocking);
- static pack_t *FS_LoadPackPK3 (const char *packfile)
- {
- filedesc_t packhandle;
- packhandle = FS_SysOpenFiledesc (packfile, "rb", false);
- if (!FILEDESC_ISVALID(packhandle))
- return NULL;
- return FS_LoadPackPK3FromFD(packfile, packhandle, false);
- }
- /*
- ====================
- PK3_GetTrueFileOffset
- Find where the true file data offset is
- ====================
- */
- static qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
- {
- unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
- fs_offset_t count;
- // Already found?
- if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
- return true;
- // Load the local file description
- if (FILEDESC_SEEK (pack->handle, pfile->offset, SEEK_SET) == -1)
- {
- Con_Printf ("Can't seek in package %s\n", pack->filename);
- return false;
- }
- count = FILEDESC_READ (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
- if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
- {
- Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
- return false;
- }
- // Skip name and extra field
- pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
- pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
- return true;
- }
- /*
- =============================================================================
- OTHER PRIVATE FUNCTIONS
- =============================================================================
- */
- /*
- ====================
- FS_AddFileToPack
- Add a file to the list of files contained into a package
- ====================
- */
- 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)
- {
- int (*strcmp_funct) (const char* str1, const char* str2);
- int left, right, middle;
- packfile_t *pfile;
- strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
- // Look for the slot we should put that file into (binary search)
- left = 0;
- right = pack->numfiles - 1;
- while (left <= right)
- {
- int diff;
- middle = (left + right) / 2;
- diff = strcmp_funct (pack->files[middle].name, name);
- // If we found the file, there's a problem
- if (!diff)
- Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
- // If we're too far in the list
- if (diff > 0)
- right = middle - 1;
- else
- left = middle + 1;
- }
- // We have to move the right of the list by one slot to free the one we need
- pfile = &pack->files[left];
- memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
- pack->numfiles++;
- strlcpy (pfile->name, name, sizeof (pfile->name));
- pfile->offset = offset;
- pfile->packsize = packsize;
- pfile->realsize = realsize;
- pfile->flags = flags;
- return pfile;
- }
- static void FS_mkdir (const char *path)
- {
- if(COM_CheckParm("-readonly"))
- return;
- #if WIN32
- if (_mkdir (path) == -1)
- #else
- if (mkdir (path, 0777) == -1)
- #endif
- {
- // No logging for this. The only caller is FS_CreatePath (which
- // calls it in ways that will intentionally produce EEXIST),
- // and its own callers always use the directory afterwards and
- // thus will detect failure that way.
- }
- }
- /*
- ============
- FS_CreatePath
- Only used for FS_OpenRealFile.
- ============
- */
- void FS_CreatePath (char *path)
- {
- char *ofs, save;
- for (ofs = path+1 ; *ofs ; ofs++)
- {
- if (*ofs == '/' || *ofs == '\\')
- {
- // create the directory
- save = *ofs;
- *ofs = 0;
- FS_mkdir (path);
- *ofs = save;
- }
- }
- }
- /*
- ============
- FS_Path_f
- ============
- */
- static void FS_Path_f (void)
- {
- searchpath_t *s;
- Con_Print("Current search path:\n");
- for (s=fs_searchpaths ; s ; s=s->next)
- {
- if (s->pack)
- {
- if(s->pack->vpack)
- Con_Printf("%sdir (virtual pack)\n", s->pack->filename);
- else
- Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
- }
- else
- Con_Printf("%s\n", s->filename);
- }
- }
- /*
- =================
- FS_LoadPackPAK
- =================
- */
- /*! Takes an explicit (not game tree related) path to a pak file.
- *Loads the header and directory, adding the files at the beginning
- *of the list so they override previous pack files.
- */
- static pack_t *FS_LoadPackPAK (const char *packfile)
- {
- dpackheader_t header;
- int i, numpackfiles;
- filedesc_t packhandle;
- pack_t *pack;
- dpackfile_t *info;
- packhandle = FS_SysOpenFiledesc(packfile, "rb", false);
- if (!FILEDESC_ISVALID(packhandle))
- return NULL;
- if(FILEDESC_READ (packhandle, (void *)&header, sizeof(header)) != sizeof(header))
- {
- Con_Printf ("%s is not a packfile\n", packfile);
- FILEDESC_CLOSE(packhandle);
- return NULL;
- }
- if (memcmp(header.id, "PACK", 4))
- {
- Con_Printf ("%s is not a packfile\n", packfile);
- FILEDESC_CLOSE(packhandle);
- return NULL;
- }
- header.dirofs = LittleLong (header.dirofs);
- header.dirlen = LittleLong (header.dirlen);
- if (header.dirlen % sizeof(dpackfile_t))
- {
- Con_Printf ("%s has an invalid directory size\n", packfile);
- FILEDESC_CLOSE(packhandle);
- return NULL;
- }
- numpackfiles = header.dirlen / sizeof(dpackfile_t);
- if (numpackfiles < 0 || numpackfiles > MAX_FILES_IN_PACK)
- {
- Con_Printf ("%s has %i files\n", packfile, numpackfiles);
- FILEDESC_CLOSE(packhandle);
- return NULL;
- }
- info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
- FILEDESC_SEEK (packhandle, header.dirofs, SEEK_SET);
- if(header.dirlen != FILEDESC_READ (packhandle, (void *)info, header.dirlen))
- {
- Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
- Mem_Free(info);
- FILEDESC_CLOSE(packhandle);
- return NULL;
- }
- 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));
- pack->handle = packhandle;
- pack->numfiles = 0;
- pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
- // parse the directory
- for (i = 0;i < numpackfiles;i++)
- {
- fs_offset_t offset = (unsigned int)LittleLong (info[i].filepos);
- fs_offset_t size = (unsigned int)LittleLong (info[i].filelen);
- // Ensure a zero terminated file name (required by format).
- info[i].name[sizeof(info[i].name) - 1] = 0;
- FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
- }
- Mem_Free(info);
- Con_DPrintf("Added packfile %s (%i files)\n", packfile, numpackfiles);
- return pack;
- }
- /*
- ====================
- FS_LoadPackVirtual
- Create a package entry associated with a directory file
- ====================
- */
- static pack_t *FS_LoadPackVirtual (const char *dirname)
- {
- pack_t *pack;
- pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
- pack->vpack = true;
- pack->ignorecase = false;
- strlcpy (pack->filename, dirname, sizeof(pack->filename));
- pack->handle = FILEDESC_INVALID;
- pack->numfiles = -1;
- pack->files = NULL;
- Con_DPrintf("Added packfile %s (virtual pack)\n", dirname);
- return pack;
- }
- /*
- ================
- FS_AddPack_Fullpath
- ================
- */
- /*! Adds the given pack to the search path.
- * The pack type is autodetected by the file extension.
- *
- * Returns true if the file was successfully added to the
- * search path or if it was already included.
- *
- * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
- * plain directories.
- *
- */
- static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qboolean *already_loaded, qboolean keep_plain_dirs)
- {
- searchpath_t *search;
- pack_t *pak = NULL;
- const char *ext = FS_FileExtension(pakfile);
- size_t l;
- for(search = fs_searchpaths; search; search = search->next)
- {
- if(search->pack && !strcasecmp(search->pack->filename, pakfile))
- {
- if(already_loaded)
- *already_loaded = true;
- return true; // already loaded
- }
- }
- if(already_loaded)
- *already_loaded = false;
- if(!strcasecmp(ext, "pk3dir"))
- pak = FS_LoadPackVirtual (pakfile);
- else if(!strcasecmp(ext, "pak"))
- pak = FS_LoadPackPAK (pakfile);
- else if(!strcasecmp(ext, "pk3"))
- pak = FS_LoadPackPK3 (pakfile);
- else if(!strcasecmp(ext, "obb")) // android apk expansion
- pak = FS_LoadPackPK3 (pakfile);
- else
- Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
- if(pak)
- {
- strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
- //Con_DPrintf(" Registered pack with short name %s\n", shortname);
- if(keep_plain_dirs)
- {
- // find the first item whose next one is a pack or NULL
- searchpath_t *insertion_point = 0;
- if(fs_searchpaths && !fs_searchpaths->pack)
- {
- insertion_point = fs_searchpaths;
- for(;;)
- {
- if(!insertion_point->next)
- break;
- if(insertion_point->next->pack)
- break;
- insertion_point = insertion_point->next;
- }
- }
- // If insertion_point is NULL, this means that either there is no
- // item in the list yet, or that the very first item is a pack. In
- // that case, we want to insert at the beginning...
- if(!insertion_point)
- {
- search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
- search->next = fs_searchpaths;
- fs_searchpaths = search;
- }
- else
- // otherwise we want to append directly after insertion_point.
- {
- search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
- search->next = insertion_point->next;
- insertion_point->next = search;
- }
- }
- else
- {
- search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
- search->next = fs_searchpaths;
- fs_searchpaths = search;
- }
- search->pack = pak;
- if(pak->vpack)
- {
- dpsnprintf(search->filename, sizeof(search->filename), "%s/", pakfile);
- // if shortname ends with "pk3dir", strip that suffix to make it just "pk3"
- // same goes for the name inside the pack structure
- l = strlen(pak->shortname);
- if(l >= 7)
- if(!strcasecmp(pak->shortname + l - 7, ".pk3dir"))
- pak->shortname[l - 3] = 0;
- l = strlen(pak->filename);
- if(l >= 7)
- if(!strcasecmp(pak->filename + l - 7, ".pk3dir"))
- pak->filename[l - 3] = 0;
- }
- return true;
- }
- else
- {
- Con_Printf("unable to load pak \"%s\"\n", pakfile);
- return false;
- }
- }
- /*
- ================
- FS_AddPack
- ================
- */
- /*! Adds the given pack to the search path and searches for it in the game path.
- * The pack type is autodetected by the file extension.
- *
- * Returns true if the file was successfully added to the
- * search path or if it was already included.
- *
- * 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)
- {
- char fullpath[MAX_OSPATH];
- int index;
- searchpath_t *search;
- if(already_loaded)
- *already_loaded = false;
- // then find the real name...
- search = FS_FindFile(pakfile, &index, true);
- if(!search || search->pack)
- {
- Con_Printf("could not find pak \"%s\"\n", pakfile);
- return false;
- }
- dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
- return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs);
- }
- /*
- ================
- FS_AddGameDirectory
- 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)
- {
- int i;
- stringlist_t list;
- searchpath_t *search;
- strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
- stringlistinit(&list);
- listdirectory(&list, "", dir);
- stringlistsort(&list, false);
- // add any PAK package in the directory
- for (i = 0;i < list.numstrings;i++)
- {
- if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
- {
- FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, 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"))
- {
- FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
- }
- }
- stringlistfreecontents(&list);
- // 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));
- search->next = fs_searchpaths;
- fs_searchpaths = search;
- }
- /*
- ================
- FS_AddGameHierarchy
- ================
- */
- static void FS_AddGameHierarchy (const char *dir)
- {
- char vabuf[1024];
- // Add the common game directory
- FS_AddGameDirectory (va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, dir));
- if (*fs_userdir)
- FS_AddGameDirectory(va(vabuf, sizeof(vabuf), "%s%s/", fs_userdir, dir));
- }
- /*
- ============
- FS_FileExtension
- ============
- */
- const char *FS_FileExtension (const char *in)
- {
- const char *separator, *backslash, *colon, *dot;
- separator = strrchr(in, '/');
- backslash = strrchr(in, '\\');
- if (!separator || separator < backslash)
- separator = backslash;
- colon = strrchr(in, ':');
- if (!separator || separator < colon)
- separator = colon;
- dot = strrchr(in, '.');
- if (dot == NULL || (separator && (dot < separator)))
- return "";
- return dot + 1;
- }
- /*
- ============
- FS_FileWithoutPath
- ============
- */
- const char *FS_FileWithoutPath (const char *in)
- {
- const char *separator, *backslash, *colon;
- separator = strrchr(in, '/');
- backslash = strrchr(in, '\\');
- if (!separator || separator < backslash)
- separator = backslash;
- colon = strrchr(in, ':');
- if (!separator || separator < colon)
- separator = colon;
- return separator ? separator + 1 : in;
- }
- /*
- ================
- FS_ClearSearchPath
- ================
- */
- static void FS_ClearSearchPath (void)
- {
- // unload all packs and directory information, close all pack files
- // (if a qfile is still reading a pack it won't be harmed because it used
- // dup() to get its own handle already)
- while (fs_searchpaths)
- {
- searchpath_t *search = fs_searchpaths;
- fs_searchpaths = search->next;
- if (search->pack && search->pack != fs_selfpack)
- {
- if(!search->pack->vpack)
- {
- // 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);
- }
- }
- static void FS_AddSelfPack(void)
- {
- if(fs_selfpack)
- {
- searchpath_t *search;
- search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
- search->next = fs_searchpaths;
- search->pack = fs_selfpack;
- fs_searchpaths = search;
- }
- }
- /*
- ================
- FS_Rescan
- ================
- */
- void FS_Rescan (void)
- {
- int i;
- qboolean fs_modified = false;
- qboolean reset = false;
- char gamedirbuf[MAX_INPUTLINE];
- char vabuf[1024];
- if (fs_searchpaths)
- reset = true;
- FS_ClearSearchPath();
- // automatically activate gamemode for the gamedirs specified
- if (reset)
- COM_ChangeGameTypeForGameDirs();
- // add the game-specific paths
- // gamedirname1 (typically id1)
- FS_AddGameHierarchy (gamedirname1);
- // update the com_modname (used for server info)
- if (gamedirname2 && gamedirname2[0])
- strlcpy(com_modname, gamedirname2, sizeof(com_modname));
- else
- strlcpy(com_modname, gamedirname1, sizeof(com_modname));
- // add the 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);
- }
- // -game <gamedir>
- // Adds basedir/gamedir as an override game
- // LordHavoc: now supports multiple -game directories
- // set the com_modname (reported in server info)
- *gamedirbuf = 0;
- for (i = 0;i < fs_numgamedirs;i++)
- {
- fs_modified = true;
- FS_AddGameHierarchy (fs_gamedirs[i]);
- // update the com_modname (used server info)
- strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
- if(i)
- strlcat(gamedirbuf, va(vabuf, sizeof(vabuf), " %s", fs_gamedirs[i]), sizeof(gamedirbuf));
- else
- 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 "-condebug" is in the command line, remove the previous log file
- if (COM_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");
- switch(gamemode)
- {
- case GAME_NORMAL:
- case GAME_HIPNOTIC:
- case GAME_ROGUE:
- if (!registered.integer)
- {
- if (fs_modified)
- Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
- else
- Con_Print("Playing shareware version.\n");
- }
- else
- Con_Print("Playing registered version.\n");
- break;
- case GAME_STEELSTORM:
- if (registered.integer)
- Con_Print("Playing registered version.\n");
- else
- Con_Print("Playing shareware version.\n");
- break;
- default:
- break;
- }
- // unload all wads so that future queries will return the new data
- W_UnloadAll();
- }
- static void FS_Rescan_f(void)
- {
- FS_Rescan();
- }
- /*
- ================
- FS_ChangeGameDirs
- ================
- */
- extern qboolean vid_opened;
- qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
- {
- int i;
- const char *p;
- if (fs_numgamedirs == numgamedirs)
- {
- for (i = 0;i < numgamedirs;i++)
- if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
- break;
- if (i == numgamedirs)
- return true; // already using this set of gamedirs, do nothing
- }
- if (numgamedirs > MAX_GAMEDIRS)
- {
- if (complain)
- Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
- return false; // too many gamedirs
- }
- for (i = 0;i < numgamedirs;i++)
- {
- // if string is nasty, reject it
- p = FS_CheckGameDir(gamedirs[i]);
- if(!p)
- {
- if (complain)
- Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
- return false; // nasty gamedirs
- }
- if(p == fs_checkgamedir_missing && failmissing)
- {
- if (complain)
- Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
- return false; // missing gamedirs
- }
- }
- Host_SaveConfig();
- fs_numgamedirs = numgamedirs;
- for (i = 0;i < fs_numgamedirs;i++)
- 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();
- 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;
- // restart the video subsystem after the config is executed
- Cbuf_InsertText("\nloadconfig\nvid_restart\n\n");
- return true;
- }
- /*
- ================
- FS_GameDir_f
- ================
- */
- static void FS_GameDir_f (void)
- {
- int i;
- int numgamedirs;
- char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
- if (Cmd_Argc() < 2)
- {
- Con_Printf("gamedirs active:");
- for (i = 0;i < fs_numgamedirs;i++)
- Con_Printf(" %s", fs_gamedirs[i]);
- Con_Printf("\n");
- return;
- }
- numgamedirs = Cmd_Argc() - 1;
- if (numgamedirs > MAX_GAMEDIRS)
- {
- Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
- return;
- }
- for (i = 0;i < numgamedirs;i++)
- strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
- if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
- {
- // actually, changing during game would work fine, but would be stupid
- Con_Printf("Can not change gamedir while client is connected or server is running!\n");
- return;
- }
- // halt demo playback to close the file
- CL_Disconnect();
- FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
- }
- static const char *FS_SysCheckGameDir(const char *gamedir, char *buf, size_t buflength)
- {
- qboolean success;
- qfile_t *f;
- stringlist_t list;
- fs_offset_t n;
- char vabuf[1024];
- stringlistinit(&list);
- listdirectory(&list, gamedir, "");
- success = list.numstrings > 0;
- stringlistfreecontents(&list);
- if(success)
- {
- f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%smodinfo.txt", gamedir), "r", false);
- if(f)
- {
- n = FS_Read (f, buf, buflength - 1);
- if(n >= 0)
- buf[n] = 0;
- else
- *buf = 0;
- FS_Close(f);
- }
- else
- *buf = 0;
- return buf;
- }
- return NULL;
- }
- /*
- ================
- FS_CheckGameDir
- ================
- */
- const char *FS_CheckGameDir(const char *gamedir)
- {
- const char *ret;
- static char buf[8192];
- char vabuf[1024];
- if (FS_CheckNastyPath(gamedir, true))
- return NULL;
- ret = FS_SysCheckGameDir(va(vabuf, sizeof(vabuf), "%s%s/", fs_userdir, gamedir), buf, sizeof(buf));
- if(ret)
- {
- if(!*ret)
- {
- // get description from basedir
- ret = FS_SysCheckGameDir(va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, gamedir), buf, sizeof(buf));
- if(ret)
- return ret;
- return "";
- }
- return ret;
- }
- ret = FS_SysCheckGameDir(va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, gamedir), buf, sizeof(buf));
- if(ret)
- return ret;
-
- return fs_checkgamedir_missing;
- }
- static void FS_ListGameDirs(void)
- {
- stringlist_t list, list2;
- int i;
- const char *info;
- char vabuf[1024];
- fs_all_gamedirs_count = 0;
- if(fs_all_gamedirs)
- Mem_Free(fs_all_gamedirs);
- stringlistinit(&list);
- listdirectory(&list, va(vabuf, sizeof(vabuf), "%s/", fs_basedir), "");
- listdirectory(&list, va(vabuf, sizeof(vabuf), "%s/", fs_userdir), "");
- stringlistsort(&list, false);
- stringlistinit(&list2);
- for(i = 0; i < list.numstrings; ++i)
- {
- if(i)
- if(!strcmp(list.strings[i-1], list.strings[i]))
- continue;
- info = FS_CheckGameDir(list.strings[i]);
- if(!info)
- continue;
- if(info == fs_checkgamedir_missing)
- continue;
- if(!*info)
- continue;
- stringlistappend(&list2, list.strings[i]);
- }
- stringlistfreecontents(&list);
- fs_all_gamedirs = (gamedir_t *)Mem_Alloc(fs_mempool, list2.numstrings * sizeof(*fs_all_gamedirs));
- for(i = 0; i < list2.numstrings; ++i)
- {
- info = FS_CheckGameDir(list2.strings[i]);
- // all this cannot happen any more, but better be safe than sorry
- if(!info)
- continue;
- if(info == fs_checkgamedir_missing)
- 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));
- ++fs_all_gamedirs_count;
- }
- }
- /*
- #ifdef WIN32
- #pragma comment(lib, "shell32.lib")
- #include <ShlObj.h>
- #endif
- */
- static void COM_InsertFlags(const char *buf) {
- const char *p;
- char *q;
- 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[0] = "dummy"; // Can't really happen.
- else
- new_argv[0] = com_argv[0];
- ++i;
- p = buf;
- while(COM_ParseToken_Console(&p))
- {
- size_t sz = strlen(com_token) + 1; // shut up clang
- if(i > args_left)
- break;
- q = (char *)Mem_Alloc(fs_mempool, sz);
- strlcpy(q, com_token, sz);
- new_argv[i] = q;
- ++i;
- }
- // Now: i <= args_left + 1.
- if (com_argc >= 1)
- {
- memcpy((char *)(&new_argv[i]), &com_argv[1], sizeof(*com_argv) * (com_argc - 1));
- i += com_argc - 1;
- }
- // Now: i <= args_left + (com_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
- }
- static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t userdirsize)
- {
- #if defined(__IPHONEOS__)
- if (userdirmode == USERDIRMODE_HOME)
- {
- // 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);
- return 1;
- }
- return -1;
- #elif defined(WIN32)
- char *homedir;
- #if _MSC_VER >= 1400
- size_t homedirlen;
- #endif
- TCHAR mydocsdir[MAX_PATH + 1];
- wchar_t *savedgamesdirw;
- char savedgamesdir[MAX_OSPATH];
- int fd;
- char vabuf[1024];
- userdir[0] = 0;
- switch(userdirmode)
- {
- default:
- return -1;
- case USERDIRMODE_NOHOME:
- strlcpy(userdir, fs_basedir, userdirsize);
- break;
- case USERDIRMODE_MYGAMES:
- if (!shfolder_dll)
- Sys_LoadLibrary(shfolderdllnames, &shfolder_dll, shfolderfuncs);
- mydocsdir[0] = 0;
- if (qSHGetFolderPath && qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK)
- {
- dpsnprintf(userdir, userdirsize, "%s/My Games/%s/", mydocsdir, gameuserdirname);
- break;
- }
- #if _MSC_VER >= 1400
- _dupenv_s(&homedir, &homedirlen, "USERPROFILE");
- if(homedir)
- {
- dpsnprintf(userdir, userdirsize, "%s/.%s/", homedir, gameuserdirname);
- free(homedir);
- break;
- }
- #else
- homedir = getenv("USERPROFILE");
- if(homedir)
- {
- dpsnprintf(userdir, userdirsize, "%s/.%s/", homedir, gameuserdirname);
- break;
- }
- #endif
- return -1;
- case USERDIRMODE_SAVEDGAMES:
- if (!shell32_dll)
- Sys_LoadLibrary(shell32dllnames, &shell32_dll, shell32funcs);
- if (!ole32_dll)
- Sys_LoadLibrary(ole32dllnames, &ole32_dll, ole32funcs);
- if (qSHGetKnownFolderPath && qCoInitializeEx && qCoTaskMemFree && qCoUninitialize)
- {
- savedgamesdir[0] = 0;
- qCoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
- /*
- #ifdef __cplusplus
- if (SHGetKnownFolderPath(FOLDERID_SavedGames, KF_FLAG_CREATE | KF_FLAG_NO_ALIAS, NULL, &savedgamesdirw) == S_OK)
- #else
- if (SHGetKnownFolderPath(&FOLDERID_SavedGames, KF_FLAG_CREATE | KF_FLAG_NO_ALIAS, NULL, &savedgamesdirw) == S_OK)
- #endif
- */
- 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
- …
Large files files are truncated, but you can click here to view the full file