# define lseek _lseeki64
#endif
+#if _MSC_VER >= 1400
+// suppress deprecated warnings
+# include <sys/stat.h>
+# include <share.h>
+# define read _read
+# define write _write
+# define close _close
+# define unlink _unlink
+# define dup _dup
+#endif
+
/*
All of Quake's data access is through a hierchal file system, but the contents
#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
#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)
#define FILE_BUFF_SIZE 2048
typedef struct
// For zipped files
ztoolkit_t* ztk;
+
+ // for data files
+ const unsigned char *data;
};
#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
{
typedef struct pack_s
{
char filename [MAX_OSPATH];
+ char shortname [MAX_QPATH];
int handle;
int ignorecase; // PK3 ignores case
int numfiles;
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,
int fs_numgamedirs = 0;
char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
-cvar_t scr_screenshot_name = {0, "scr_screenshot_name","dp", "prefix name for saved screenshots (changes based on -game commandline, as well as which game mode is running)"};
+cvar_t scr_screenshot_name = {0, "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"};
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);
#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))
+
+// qz_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
static dllfunction_t zlibfuncs[] =
{
{"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;
+#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 dllhandle_t shfolder_dll = NULL;
+#endif
/*
====================
return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
}
+/*
+====================
+FS_HasZlib
+
+See if zlib is available
+====================
+*/
+qboolean FS_HasZlib(void)
+{
+ PK3_OpenLibrary(); // to be safe
+ return (zlib_dll != 0);
+}
/*
====================
// Load the central directory in memory
central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
- read (pack->handle, central_dir, eocd->cdir_size);
+ if(read (pack->handle, central_dir, eocd->cdir_size) != (ssize_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
// 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] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
+ if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
{
// Still enough bytes for the name?
if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
offset = BuffLittleLong (&ptr[42]);
packsize = BuffLittleLong (&ptr[20]);
realsize = 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);
}
}
pack_t *pack;
int real_nb_files;
+#if _MSC_VER >= 1400
+ _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
+#else
packhandle = open (packfile, O_RDONLY | O_BINARY);
+#endif
if (packhandle < 0)
return NULL;
============
FS_CreatePath
-Only used for FS_Open.
+Only used for FS_OpenRealFile.
============
*/
void FS_CreatePath (char *path)
pack_t *pack;
dpackfile_t *info;
+#if _MSC_VER >= 1400
+ _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
+#else
packhandle = open (packfile, O_RDONLY | O_BINARY);
+#endif
if (packhandle < 0)
return NULL;
- read (packhandle, (void *)&header, sizeof(header));
+ if(read (packhandle, (void *)&header, sizeof(header)) != sizeof(header))
+ {
+ Con_Printf ("%s is not a packfile\n", packfile);
+ close(packhandle);
+ return NULL;
+ }
if (memcmp(header.id, "PACK", 4))
{
Con_Printf ("%s is not a packfile\n", packfile);
plain directories.
================
*/
-static qboolean FS_AddPack_Fullpath(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
+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;
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
dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
- return FS_AddPack_Fullpath(fullpath, already_loaded, keep_plain_dirs);
+ return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs);
}
int i;
stringlist_t list;
searchpath_t *search;
- char pakfile[MAX_OSPATH];
strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
stringlistinit(&list);
- listdirectory(&list, dir);
+ listdirectory(&list, "", dir);
stringlistsort(&list);
// add any PAK package in the directory
{
if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
{
- dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
- FS_AddPack_Fullpath(pakfile, NULL, false);
+ FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
}
}
{
if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
{
- dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
- FS_AddPack_Fullpath(pakfile, NULL, false);
+ FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
}
}
char userdir[MAX_QPATH];
#ifdef WIN32
TCHAR mydocsdir[MAX_PATH + 1];
-#else
- const char *homedir;
+#if _MSC_VER >= 1400
+ size_t homedirlen;
#endif
+#endif
+ char *homedir;
// Add the common game directory
FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
// Add the personal game directory
#ifdef WIN32
- if(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK)
+ if(qSHGetFolderPath && (qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK))
+ {
dpsnprintf(userdir, sizeof(userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
- fprintf(stderr, "userdir = %s\n", userdir);
+ Con_DPrintf("Obtained personal directory %s from SHGetFolderPath\n", userdir);
+ }
+ else
+ {
+ // use the environment
+#if _MSC_VER >= 1400
+ _dupenv_s (&homedir, &homedirlen, "USERPROFILE");
+#else
+ homedir = getenv("USERPROFILE");
+#endif
+
+ if(homedir)
+ {
+ dpsnprintf(userdir, sizeof(userdir), "%s/My Documents/My Games/%s/", homedir, gameuserdirname);
+#if _MSC_VER >= 1400
+ free(homedir);
+#endif
+ Con_DPrintf("Obtained personal directory %s from environment\n", userdir);
+ }
+ else
+ *userdir = 0; // just to make sure it hasn't been written to by SHGetFolderPath returning failure
+ }
+
+ if(!*userdir)
+ Con_DPrintf("Could not obtain home directory; not supporting -mygames\n");
#else
homedir = getenv ("HOME");
if(homedir)
dpsnprintf(userdir, sizeof(userdir), "%s/.%s/", homedir, gameuserdirname);
+
+ if(!*userdir)
+ Con_DPrintf("Could not obtain home directory; assuming -nohome\n");
#endif
+
#ifdef WIN32
if(!COM_CheckParm("-mygames"))
{
+#if _MSC_VER >= 1400
+ int fd;
+ _sopen_s(&fd, va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE); // note: no O_TRUNC here!
+#else
int fd = open (va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
+#endif
if(fd >= 0)
{
close(fd);
if(COM_CheckParm("-nohome"))
*userdir = 0;
-
+
if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
dpsnprintf(userdir, sizeof(userdir), "%s/", com_argv[i+1]);
}
}
- // halt demo playback to close the file
- CL_Disconnect();
-
Host_SaveConfig();
fs_numgamedirs = numgamedirs;
return;
}
+ // halt demo playback to close the file
+ CL_Disconnect();
+
FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
}
qboolean success;
stringlist_t list;
stringlistinit(&list);
- listdirectory(&list, va("%s%s/", fs_basedir, gamedir));
+ listdirectory(&list, va("%s%s/", fs_basedir, gamedir), "");
success = list.numstrings > 0;
stringlistfreecontents(&list);
return success;
{
int i;
+#ifdef WIN32
+ const char* dllnames [] =
+ {
+ "shfolder.dll", // IE 4, or Win NT and higher
+ NULL
+ };
+ Sys_LoadLibrary(dllnames, &shfolder_dll, shfolderfuncs);
+ // don't care for the result; if it fails, %USERPROFILE% will be used instead
+#endif
+
fs_mempool = Mem_AllocPool("file management", 0, NULL);
strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
strlcat(fs_basedir, "/", sizeof(fs_basedir));
if (!FS_CheckGameDir(gamedirname1))
- Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
+ Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
- Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
+ Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
// -game <gamedir>
// Adds basedir/gamedir as an override game
if (FS_CheckNastyPath(com_argv[i], true))
Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
if (!FS_CheckGameDir(com_argv[i]))
- Sys_Error("-game %s%s/ not found!\n", fs_basedir, com_argv[i]);
+ Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]);
// add the gamedir to the list of active gamedirs
strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
fs_numgamedirs++;
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");
}
/*
// by the OS anyway)
FS_ClearSearchPath();
Mem_FreePool (&fs_mempool);
+
+#ifdef WIN32
+ Sys_UnloadLibrary (&shfolder_dll);
+#endif
}
/*
memset (file, 0, sizeof (*file));
file->ungetc = EOF;
+#if _MSC_VER >= 1400
+ _sopen_s(&file->handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
+#else
file->handle = open (filepath, mod | opt, 0666);
+#endif
if (file->handle < 0)
{
Mem_Free (file);
Look for a file in the search paths and open it in read-only mode
===========
*/
-qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
+qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
{
searchpath_t *search;
int pack_ind;
}
// So, we found it in a package...
+
+ // Is it a PK3 symlink?
+ // TODO also handle directory symlinks by parsing the whole structure...
+ // but heck, file symlinks are good enough for now
+ if(search->pack->files[pack_ind].flags & PACKFILE_FLAG_SYMLINK)
+ {
+ if(symlinkLevels <= 0)
+ {
+ Con_Printf("symlink: %s: too many levels of symbolic links\n", filename);
+ return NULL;
+ }
+ else
+ {
+ char linkbuf[MAX_QPATH];
+ fs_offset_t count;
+ qfile_t *linkfile = FS_OpenPackedFile (search->pack, pack_ind);
+ const char *mergeslash;
+ char *mergestart;
+
+ if(!linkfile)
+ return NULL;
+ count = FS_Read(linkfile, linkbuf, sizeof(linkbuf) - 1);
+ FS_Close(linkfile);
+ if(count < 0)
+ return NULL;
+ linkbuf[count] = 0;
+
+ // Now combine the paths...
+ mergeslash = strrchr(filename, '/');
+ mergestart = linkbuf;
+ if(!mergeslash)
+ mergeslash = filename;
+ while(!strncmp(mergestart, "../", 3))
+ {
+ mergestart += 3;
+ while(mergeslash > filename)
+ {
+ --mergeslash;
+ if(*mergeslash == '/')
+ break;
+ }
+ }
+ // Now, mergestart will point to the path to be appended, and mergeslash points to where it should be appended
+ if(mergeslash == filename)
+ {
+ // Either mergeslash == filename, then we just replace the name (done below)
+ }
+ else
+ {
+ // Or, we append the name after mergeslash;
+ // or rather, we can also shift the linkbuf so we can put everything up to and including mergeslash first
+ int spaceNeeded = mergeslash - filename + 1;
+ int spaceRemoved = mergestart - linkbuf;
+ if(count - spaceRemoved + spaceNeeded >= MAX_QPATH)
+ {
+ Con_DPrintf("symlink: too long path rejected\n");
+ return NULL;
+ }
+ memmove(linkbuf + spaceNeeded, linkbuf + spaceRemoved, count - spaceRemoved);
+ memcpy(linkbuf, filename, spaceNeeded);
+ linkbuf[count - spaceRemoved + spaceNeeded] = 0;
+ mergestart = linkbuf;
+ }
+ if (!quiet && developer_loading.integer)
+ Con_DPrintf("symlink: %s -> %s\n", filename, mergestart);
+ if(FS_CheckNastyPath (mergestart, false))
+ {
+ Con_DPrintf("symlink: nasty path %s rejected\n", mergestart);
+ return NULL;
+ }
+ return FS_OpenReadFile(mergestart, quiet, nonblocking, symlinkLevels - 1);
+ }
+ }
+
return FS_OpenPackedFile (search->pack, pack_ind);
}
/*
====================
-FS_Open
+FS_OpenRealFile
-Open a file. The syntax is the same as fopen
+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_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
-{
-#ifdef FS_FIX_PATHS
- char fixedFileName[MAX_QPATH];
- char *d;
- strlcpy( fixedFileName, filepath, MAX_QPATH );
- // try to fix common mistakes (\ instead of /)
- for( d = fixedFileName ; *d ; d++ )
- if( *d == '\\' )
- *d = '/';
- filepath = fixedFileName;
-#endif
+qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qboolean quiet)
+{
+ char real_path [MAX_OSPATH];
if (FS_CheckNastyPath(filepath, false))
{
- Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
+ Con_Printf("FS_OpenRealFile(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
return NULL;
}
- // If the file is opened in "write", "append", or "read/write" mode
+ dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
+
+ // If the file is opened in "write", "append", or "read/write" mode,
+ // create directories up to the file.
if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
- {
- char real_path [MAX_OSPATH];
+ FS_CreatePath (real_path);
+ return FS_SysOpen (real_path, mode, false);
+}
- // Open the file on disk directly
- dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
- // Create directories up to the file
- FS_CreatePath (real_path);
+/*
+====================
+FS_OpenVirtualFile
- return FS_SysOpen (real_path, mode, nonblocking);
+Open a file. The syntax is the same as fopen
+====================
+*/
+qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet)
+{
+ if (FS_CheckNastyPath(filepath, false))
+ {
+ Con_Printf("FS_OpenVirtualFile(\"%s\", %s): nasty filename rejected\n", filepath, quiet ? "true" : "false");
+ return NULL;
}
- // Else, we look at the various search paths and open the file in read-only mode
- else
- return FS_OpenReadFile (filepath, quiet, nonblocking);
+
+ return FS_OpenReadFile (filepath, quiet, false, 16);
}
+/*
+====================
+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* file;
+ file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
+ memset (file, 0, sizeof (*file));
+ file->flags = QFILE_FLAG_DATA;
+ file->ungetc = EOF;
+ file->real_length = size;
+ file->data = data;
+ return file;
+}
+
/*
====================
FS_Close
*/
int FS_Close (qfile_t* file)
{
+ if(file->flags & QFILE_FLAG_DATA)
+ {
+ Mem_Free(file);
+ return 0;
+ }
+
if (close (file->handle))
return EOF;
else
done = 0;
+ if(file->flags & QFILE_FLAG_DATA)
+ {
+ size_t left = file->real_length - file->position;
+ if(buffersize > left)
+ buffersize = left;
+ memcpy(buffer, file->data + file->position, buffersize);
+ file->position += buffersize;
+ return buffersize;
+ }
+
// First, we copy as many bytes as we can from "buff"
if (file->buff_ind < file->buff_len)
{
if (offset < 0 || offset > file->real_length)
return -1;
+ if(file->flags & QFILE_FLAG_DATA)
+ {
+ file->position = offset;
+ return 0;
+ }
+
// If we have the data in our read buffer, we don't need to actually seek
if (file->position - file->buff_len <= offset && offset <= file->position)
{
unsigned char *buf = NULL;
fs_offset_t filesize = 0;
- file = FS_Open (path, "rb", quiet, false);
+ file = FS_OpenVirtualFile(path, quiet);
if (file)
{
filesize = file->real_length;
+ if(filesize < 0)
+ {
+ Con_Printf("FS_LoadFile(\"%s\", pool, %s, filesizepointer): trying to open a non-regular file\n", path, quiet ? "true" : "false");
+ FS_Close(file);
+ return NULL;
+ }
+
buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
buf[filesize] = '\0';
FS_Read (file, buf, filesize);
{
qfile_t *file;
- file = FS_Open (filename, "wb", false, false);
+ file = FS_OpenRealFile(filename, "wb", false);
if (!file)
{
Con_Printf("FS_WriteFile: failed on %s\n", filename);
int FS_SysFileType (const char *path)
{
#if WIN32
+// Sajt - some older sdks are missing this define
+# ifndef INVALID_FILE_ATTRIBUTES
+# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+# endif
+
DWORD result = GetFileAttributes(path);
if(result == INVALID_FILE_ATTRIBUTES)
stringlist_t dirlist;
const char *slash, *backslash, *colon, *separator;
char *basepath;
- char netpath[MAX_OSPATH];
char temp[MAX_OSPATH];
for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
}
else
{
- // get a directory listing and look at each name
- dpsnprintf(netpath, sizeof (netpath), "%s%s", searchpath->filename, basepath);
- stringlistinit(&dirlist);
- listdirectory(&dirlist, netpath);
- for (dirlistindex = 0;dirlistindex < dirlist.numstrings;dirlistindex++)
+ stringlist_t matchedSet, foundSet;
+ const char *start = pattern;
+
+ stringlistinit(&matchedSet);
+ stringlistinit(&foundSet);
+ // add a first entry to the set
+ stringlistappend(&matchedSet, "");
+ // iterate through pattern's path
+ while (*start)
{
- dpsnprintf(temp, sizeof(temp), "%s%s", basepath, dirlist.strings[dirlistindex]);
+ const char *asterisk, *wildcard, *nextseparator, *prevseparator;
+ char subpath[MAX_OSPATH];
+ char subpattern[MAX_OSPATH];
+
+ // find the next wildcard
+ wildcard = strchr(start, '?');
+ asterisk = strchr(start, '*');
+ if (asterisk && (!wildcard || asterisk < wildcard))
+ {
+ wildcard = asterisk;
+ }
+
+ if (wildcard)
+ {
+ nextseparator = strchr( wildcard, '/' );
+ }
+ else
+ {
+ nextseparator = NULL;
+ }
+
+ if( !nextseparator ) {
+ nextseparator = start + strlen( start );
+ }
+
+ // 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)));
+ // find the last '/' before the wildcard
+ prevseparator = strrchr( subpattern, '/' );
+ if (!prevseparator)
+ prevseparator = subpattern;
+ else
+ 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)));
+
+ // for each entry in matchedSet try to open the subdirectories specified in subpath
+ for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
+ strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
+ strlcat( temp, subpath, sizeof(temp) );
+ listdirectory( &foundSet, searchpath->filename, temp );
+ }
+ if( dirlistindex == 0 ) {
+ break;
+ }
+ // reset the current result set
+ stringlistfreecontents( &matchedSet );
+ // match against the pattern
+ for( dirlistindex = 0 ; dirlistindex < foundSet.numstrings ; dirlistindex++ ) {
+ const char *direntry = foundSet.strings[ dirlistindex ];
+ if (matchpattern(direntry, subpattern, true)) {
+ stringlistappend( &matchedSet, direntry );
+ }
+ }
+ stringlistfreecontents( &foundSet );
+
+ start = nextseparator;
+ }
+
+ for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
+ {
+ const char *temp = matchedSet.strings[dirlistindex];
if (matchpattern(temp, (char *)pattern, true))
{
for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
}
}
}
- stringlistfreecontents(&dirlist);
+ stringlistfreecontents( &matchedSet );
}
}
FS_ListDirectoryCmd("ls", false);
}
+void FS_Which_f(void)
+{
+ const char *filename;
+ int index;
+ searchpath_t *sp;
+ if (Cmd_Argc() != 2)
+ {
+ Con_Printf("usage:\n%s <file>\n", Cmd_Argv(0));
+ return;
+ }
+ filename = Cmd_Argv(1);
+ sp = FS_FindFile(filename, &index, true);
+ if (!sp) {
+ Con_Printf("%s isn't anywhere\n", filename);
+ return;
+ }
+ if (sp->pack)
+ Con_Printf("%s is in package %s\n", filename, sp->pack->shortname);
+ else
+ Con_Printf("%s is file %s%s\n", filename, sp->filename, filename);
+}
+
+
const char *FS_WhichPack(const char *filename)
{
int index;
searchpath_t *sp = FS_FindFile(filename, &index, true);
if(sp && sp->pack)
- return sp->pack->filename;
+ return sp->pack->shortname;
else
return 0;
}
return crc;
}
+unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool)
+{
+ z_stream strm;
+ unsigned char *out = NULL;
+ unsigned char *tmp;
+
+ memset(&strm, 0, sizeof(strm));
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+
+ if(level < 0)
+ level = Z_DEFAULT_COMPRESSION;
+
+ if(qz_deflateInit2(&strm, level, Z_DEFLATED, -MAX_WBITS, Z_MEMLEVEL_DEFAULT, Z_BINARY) != Z_OK)
+ {
+ Con_Printf("FS_Deflate: deflate init error!\n");
+ return NULL;
+ }
+
+ strm.next_in = (unsigned char*)data;
+ strm.avail_in = size;
+
+ tmp = (unsigned char *) Mem_Alloc(tempmempool, size);
+ if(!tmp)
+ {
+ Con_Printf("FS_Deflate: not enough memory in tempmempool!\n");
+ qz_deflateEnd(&strm);
+ return NULL;
+ }
+
+ strm.next_out = tmp;
+ strm.avail_out = size;
+
+ if(qz_deflate(&strm, Z_FINISH) != Z_STREAM_END)
+ {
+ Con_Printf("FS_Deflate: deflate failed!\n");
+ qz_deflateEnd(&strm);
+ Mem_Free(tmp);
+ return NULL;
+ }
+
+ if(qz_deflateEnd(&strm) != Z_OK)
+ {
+ Con_Printf("FS_Deflate: deflateEnd failed\n");
+ Mem_Free(tmp);
+ return NULL;
+ }
+
+ if(strm.total_out >= size)
+ {
+ Con_Printf("FS_Deflate: deflate is useless on this data!\n");
+ Mem_Free(tmp);
+ return NULL;
+ }
+
+ out = (unsigned char *) Mem_Alloc(mempool, strm.total_out);
+ if(!out)
+ {
+ Con_Printf("FS_Deflate: not enough memory in target mempool!\n");
+ Mem_Free(tmp);
+ return NULL;
+ }
+
+ if(deflated_size)
+ *deflated_size = (size_t)strm.total_out;
+
+ memcpy(out, tmp, strm.total_out);
+ Mem_Free(tmp);
+
+ return out;
+}
+
+static void AssertBufsize(sizebuf_t *buf, int length)
+{
+ if(buf->cursize + length > buf->maxsize)
+ {
+ int oldsize = buf->maxsize;
+ unsigned char *olddata;
+ olddata = buf->data;
+ buf->maxsize += length;
+ buf->data = (unsigned char *) Mem_Alloc(tempmempool, buf->maxsize);
+ if(olddata)
+ {
+ memcpy(buf->data, olddata, oldsize);
+ Mem_Free(olddata);
+ }
+ }
+}
+
+unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflated_size, mempool_t *mempool)
+{
+ int ret;
+ z_stream strm;
+ unsigned char *out = NULL;
+ unsigned char tmp[2048];
+ unsigned int have;
+ sizebuf_t outbuf;
+
+ memset(&outbuf, 0, sizeof(outbuf));
+ outbuf.data = (unsigned char *) Mem_Alloc(tempmempool, sizeof(tmp));
+ outbuf.maxsize = sizeof(tmp);
+
+ memset(&strm, 0, sizeof(strm));
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+
+ if(qz_inflateInit2(&strm, -MAX_WBITS) != Z_OK)
+ {
+ Con_Printf("FS_Inflate: inflate init error!\n");
+ Mem_Free(outbuf.data);
+ return NULL;
+ }
+
+ strm.next_in = (unsigned char*)data;
+ strm.avail_in = size;
+
+ do
+ {
+ strm.next_out = tmp;
+ strm.avail_out = sizeof(tmp);
+ ret = qz_inflate(&strm, Z_NO_FLUSH);
+ // it either returns Z_OK on progress, Z_STREAM_END on end
+ // or an error code
+ switch(ret)
+ {
+ case Z_STREAM_END:
+ case Z_OK:
+ break;
+
+ case Z_STREAM_ERROR:
+ Con_Print("FS_Inflate: stream error!\n");
+ break;
+ case Z_DATA_ERROR:
+ Con_Print("FS_Inflate: data error!\n");
+ break;
+ case Z_MEM_ERROR:
+ Con_Print("FS_Inflate: mem error!\n");
+ break;
+ case Z_BUF_ERROR:
+ Con_Print("FS_Inflate: buf error!\n");
+ break;
+ default:
+ Con_Print("FS_Inflate: unknown error!\n");
+ break;
+
+ }
+ if(ret != Z_OK && ret != Z_STREAM_END)
+ {
+ Con_Printf("Error after inflating %u bytes\n", (unsigned)strm.total_in);
+ Mem_Free(outbuf.data);
+ qz_inflateEnd(&strm);
+ return NULL;
+ }
+ have = sizeof(tmp) - strm.avail_out;
+ AssertBufsize(&outbuf, max(have, sizeof(tmp)));
+ SZ_Write(&outbuf, tmp, have);
+ } while(ret != Z_STREAM_END);
+
+ qz_inflateEnd(&strm);
+
+ out = (unsigned char *) Mem_Alloc(mempool, outbuf.cursize);
+ if(!out)
+ {
+ Con_Printf("FS_Inflate: not enough memory in target mempool!\n");
+ Mem_Free(outbuf.data);
+ return NULL;
+ }
+
+ memcpy(out, outbuf.data, outbuf.cursize);
+ Mem_Free(outbuf.data);
+
+ if(inflated_size)
+ *inflated_size = (size_t)outbuf.cursize;
+
+ return out;
+}