20 #include "shared/shared.h"
21 #include "shared/list.h"
22 #include "common/common.h"
23 #include "common/cvar.h"
24 #include "common/error.h"
25 #include "common/files.h"
26 #include "common/prompt.h"
27 #include "system/system.h"
29 #include "format/pak.h"
33 #include <sys/types.h>
59 #define MAX_FILE_HANDLES 32
62 #define ZIP_MAXFILES 0x8000 // 32k files
63 #define ZIP_BUFSIZE 0x10000 // inflate in blocks of 64k
65 #define ZIP_BUFREADCOMMENT 1024
66 #define ZIP_SIZELOCALHEADER 30
67 #define ZIP_SIZECENTRALHEADER 20
68 #define ZIP_SIZECENTRALDIRITEM 46
70 #define ZIP_LOCALHEADERMAGIC 0x04034b50
71 #define ZIP_CENTRALHEADERMAGIC 0x02014b50
72 #define ZIP_ENDHEADERMAGIC 0x06054b50
76 #define FS_DPrintf(...) \
77 if (fs_debug && fs_debug->integer) \
78 Com_LPrintf(PRINT_DEVELOPER, __VA_ARGS__)
80 #define FS_DPrintf(...)
83 #define PATH_NOT_CHECKED -1
85 #define FOR_EACH_SYMLINK(link, list) \
86 LIST_FOR_EACH(symlink_t, link, list, entry)
88 #define FOR_EACH_SYMLINK_SAFE(link, next, list) \
89 LIST_FOR_EACH_SAFE(symlink_t, link, next, list, entry)
110 byte buffer[ZIP_BUFSIZE];
183 static int fs_count_read;
184 static int fs_count_open;
185 static int fs_count_strcmp;
186 static int fs_count_strlwr;
187 #define FS_COUNT_READ fs_count_read++
188 #define FS_COUNT_OPEN fs_count_open++
189 #define FS_COUNT_STRCMP fs_count_strcmp++
190 #define FS_COUNT_STRLWR fs_count_strlwr++
192 #define FS_COUNT_READ (void)0
193 #define FS_COUNT_OPEN (void)0
194 #define FS_COUNT_STRCMP (void)0
195 #define FS_COUNT_STRLWR (void)0
199 static cvar_t *fs_debug;
208 static zipstream_t fs_zipstream;
210 static void open_zip_file(
file_t *file);
211 static void close_zip_file(
file_t *file);
212 static ssize_t tell_zip_file(
file_t *file);
213 static ssize_t read_zip_file(
file_t *file,
void *buf,
size_t len);
241 if (*p ==
'/' || *p ==
'\\') {
257 if (strchr(
"<>:\"|?*",
c))
273 int res = PATH_VALID;
280 res = PATH_MIXED_CASE;
297 Com_Printf(
"'%s' contains invalid characters for a filename.\n", var->name);
301 if (strchr(var->string,
'/') || strchr(var->string,
'\\')) {
302 Com_Printf(
"'%s' should be a single filename, not a path.\n", var->name);
309 Com_Printf(
"...falling back to %s\n", var->default_string);
339 if (
c ==
'/' ||
c ==
'\\' ||
c == 0) {
340 if ((pre & 0xffffff) == ((
'/' << 16) | (
'.' << 8) |
'.')) {
348 while (out > start && *out !=
'/')
360 if ((pre & 0xffff) == ((
'/' << 8) |
'.')) {
373 if ((pre & 0xff) ==
'/') {
384 pre = (pre << 8) |
c;
402 size_t len = strlen(in);
442 Com_Error(ERR_FATAL,
"%s: bad file type", __func__);
459 size_t namelen = *len_p;
465 if (!FS_pathcmpn(buffer, link->
name, link->
namelen)) {
468 if (newlen < MAX_OSPATH) {
494 if ((file->
mode & FS_MODE_MASK) == FS_MODE_READ)
513 switch (file->
type) {
515 ret = ftell(file->
fp);
524 return tell_zip_file(file);
526 ret = gztell(file->zfp);
528 return Q_ERR_LIBRARY_ERROR;
545 if (entry->
filepos > LONG_MAX - offset)
548 filepos = entry->
filepos + offset;
549 if (fseek(file->
fp, filepos, SEEK_SET) == -1)
554 return Q_ERR_SUCCESS;
571 if (offset > LONG_MAX)
577 switch (file->
type) {
579 if (fseek(file->
fp, (
long)offset, SEEK_SET) == -1) {
582 return Q_ERR_SUCCESS;
587 if (gzseek(file->zfp, (z_off_t)offset, SEEK_SET) == -1) {
590 return Q_ERR_SUCCESS;
614 if (*path ==
'/' && path[1] ==
'/') {
617 p = strchr(path + 2,
'/');
619 p = strchr(p + 1,
'/');
628 for (; *ofs ==
'/'; ofs++)
631 for (; *ofs; ofs++) {
635 ret = os_mkdir(path);
638 qerror_t
err = Q_Errno();
639 if (
err != Q_ERR_EXIST)
645 return Q_ERR_SUCCESS;
648 #define FS_ERR_READ(fp) \
649 (ferror(fp) ? Q_Errno() : Q_ERR_UNEXPECTED_EOF)
650 #define FS_ERR_WRITE(fp) \
651 (ferror(fp) ? Q_Errno() : Q_ERR_FAILURE)
675 switch (file->
type) {
677 return Q_ERR_SUCCESS;
684 mode = file->
mode & FS_MODE_MASK;
689 return Q_ERR_FILE_TOO_SMALL;
693 if (fseek(file->
fp, 0, SEEK_SET) == -1) {
698 if (fread(&magic, 1, 4, file->
fp) != 4) {
703 if (!CHECK_GZIP_HEADER(magic)) {
704 return Q_ERR_INVALID_FORMAT;
708 if (fseek(file->
fp, file->
length - 4, SEEK_SET) == -1) {
713 if (fread(&magic, 1, 4, file->
fp) != 4) {
717 length = LittleLong(magic);
731 if (fseek(file->
fp, 0, SEEK_SET) == -1) {
735 fd = os_fileno(file->
fp);
739 zfp = gzdopen(fd, modeStr);
741 return Q_ERR_FAILURE;
747 return Q_ERR_SUCCESS;
766 switch (file->
type) {
783 close_zip_file(file);
792 memset(file, 0,
sizeof(*file));
799 if (os_stat(path, &
st) == -1)
802 if (Q_ISDIR(
st.st_mode))
805 if (!Q_ISREG(
st.st_mode))
806 return Q_ERR_FILE_NOT_REGULAR;
809 info->size =
st.st_size;
810 info->ctime =
st.st_ctime;
811 info->mtime =
st.st_mtime;
814 return Q_ERR_SUCCESS;
826 if (os_fstat(fd, &
st) == -1)
829 if (Q_ISDIR(
st.st_mode))
832 if (!Q_ISREG(
st.st_mode))
833 return Q_ERR_FILE_NOT_REGULAR;
836 info->size =
st.st_size;
837 info->ctime =
st.st_ctime;
838 info->mtime =
st.st_mtime;
841 return Q_ERR_SUCCESS;
844 static inline FILE *
fopen_hack(
const char *path,
const char *mode)
847 if (mode[0] ==
'w' && mode[1] ==
'x') {
849 int flags = _O_WRONLY | _O_CREAT | _O_EXCL | _S_IREAD | _S_IWRITE;
856 fd = _open(path, flags);
860 fp = _fdopen(fd, (flags & _O_BINARY) ?
"wb" :
"w");
866 int flags = O_WRONLY | O_CREAT | O_EXCL;
867 int perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
871 fd = open(path, flags, perm);
875 fp = fdopen(fd,
"wb");
882 #endif // _GNU_SOURCE
884 return fopen(path, mode);
889 char normalized[MAX_OSPATH], fullpath[MAX_OSPATH];
899 if (len >=
sizeof(normalized)) {
900 return Q_ERR_NAMETOOLONG;
905 return Q_ERR_NAMETOOSHORT;
910 ret = Q_ERR_INVALID_PATH;
915 if ((file->
mode & FS_PATH_MASK) == FS_PATH_BASE) {
917 len =
Q_concat(fullpath,
sizeof(fullpath),
918 sys_homedir->string,
"/" BASEGAME
"/", normalized, NULL);
920 len =
Q_concat(fullpath,
sizeof(fullpath),
921 sys_basedir->string,
"/" BASEGAME
"/", normalized, NULL);
924 len =
Q_concat(fullpath,
sizeof(fullpath),
927 if (len >=
sizeof(fullpath)) {
928 ret = Q_ERR_NAMETOOLONG;
932 mode = file->
mode & FS_MODE_MASK;
935 strcpy(mode_str,
"a");
938 strcpy(mode_str,
"w");
939 if (file->
mode & FS_FLAG_EXCL)
940 strcat(mode_str,
"x");
946 strcpy(mode_str,
"r+");
954 if (!(file->
mode & FS_FLAG_TEXT))
955 strcat(mode_str,
"b");
976 switch (file->
mode & FS_BUF_MASK) {
979 setvbuf(fp, NULL, _IONBF, BUFSIZ);
983 setvbuf(fp, NULL, _IOLBF, BUFSIZ);
987 setvbuf(fp, NULL, _IOFBF, BUFSIZ);
994 if (mode == FS_MODE_RDWR) {
996 if (fseek(fp, 0, SEEK_END) == -1) {
1009 FS_DPrintf(
"%s: %s: %lu bytes\n", __func__, fullpath, pos);
1014 file->
error = Q_ERR_SUCCESS;
1028 static qerror_t check_header_coherency(FILE *fp,
packfile_t *entry)
1030 unsigned flags, comp_mtd;
1031 size_t comp_len, file_len;
1032 size_t name_size, xtra_size;
1033 byte header[ZIP_SIZELOCALHEADER];
1036 if (fseek(fp, (
long)entry->
filepos, SEEK_SET) == -1)
1038 if (fread(header, 1,
sizeof(header), fp) !=
sizeof(header))
1042 if (LittleLongMem(&header[0]) != ZIP_LOCALHEADERMAGIC)
1043 return Q_ERR_NOT_COHERENT;
1045 flags = LittleShortMem(&header[6]);
1046 comp_mtd = LittleShortMem(&header[8]);
1047 comp_len = LittleLongMem(&header[18]);
1048 file_len = LittleLongMem(&header[22]);
1049 name_size = LittleShortMem(&header[26]);
1050 xtra_size = LittleShortMem(&header[28]);
1052 if (comp_mtd != entry->compmtd)
1053 return Q_ERR_NOT_COHERENT;
1057 if ((flags & 8) == 0) {
1058 if (comp_len != entry->complen)
1059 return Q_ERR_NOT_COHERENT;
1060 if (file_len != entry->
filelen)
1061 return Q_ERR_NOT_COHERENT;
1064 ofs = ZIP_SIZELOCALHEADER + name_size + xtra_size;
1065 if (entry->
filepos > LONG_MAX - ofs) {
1070 entry->coherent = qtrue;
1071 return Q_ERR_SUCCESS;
1074 static voidpf FS_zalloc(voidpf opaque, uInt items, uInt size)
1076 return FS_Malloc(items * size);
1079 static void FS_zfree(voidpf opaque, voidpf address)
1084 static void open_zip_file(
file_t *file)
1090 s = FS_Malloc(
sizeof(*s));
1091 memset(&s->stream, 0,
sizeof(s->stream));
1101 z->zalloc = FS_zalloc;
1102 z->zfree = FS_zfree;
1103 if (inflateInit2(z, -MAX_WBITS) != Z_OK) {
1104 Com_Error(ERR_FATAL,
"%s: inflateInit2() failed", __func__);
1108 z->avail_in = z->avail_out = 0;
1109 z->total_in = z->total_out = 0;
1110 z->next_in = z->next_out = NULL;
1112 s->rest_in = file->
entry->complen;
1117 static void close_zip_file(
file_t *file)
1119 zipstream_t *s = file->zfp;
1121 inflateEnd(&s->stream);
1127 static ssize_t tell_zip_file(
file_t *file)
1129 zipstream_t *s = file->zfp;
1131 return s->stream.total_out;
1134 static ssize_t read_zip_file(
file_t *file,
void *buf,
size_t len)
1136 zipstream_t *s = file->zfp;
1137 z_streamp z = &s->stream;
1138 size_t block, result;
1149 z->avail_out = (uInt)len;
1158 block = ZIP_BUFSIZE;
1159 if (block > s->rest_in) {
1163 result = fread(s->buffer, 1, block, file->
fp);
1164 if (result != block) {
1171 s->rest_in -= result;
1172 z->next_in = s->buffer;
1173 z->avail_in = result;
1176 ret = inflate(z, Z_SYNC_FLUSH);
1177 if (ret == Z_STREAM_END) {
1181 file->
error = Q_ERR_INFLATE_FAILED;
1187 }
while (z->avail_out);
1189 len -= z->avail_out;
1192 if (file->
error && len == 0) {
1208 fp = fopen(
pack->filename,
"rb");
1219 if (
pack->type == FS_ZIP && !entry->coherent) {
1220 ret = check_header_coherency(fp, entry);
1227 if (fseek(fp, (
long)entry->
filepos, SEEK_SET) == -1) {
1234 file->
entry = entry;
1237 file->
error = Q_ERR_SUCCESS;
1242 if (
pack->type == FS_ZIP) {
1243 if (file->
mode & FS_FLAG_DEFLATE) {
1247 file->
length = entry->complen;
1248 }
else if (entry->compmtd) {
1249 open_zip_file(file);
1284 fp = fopen(fullpath,
"rb");
1299 file->
error = Q_ERR_SUCCESS;
1300 file->
length = info.size;
1302 FS_DPrintf(
"%s: %s: %"PRIz
" bytes\n", __func__, fullpath, info.size);
1312 #ifndef NO_TEXTURE_RELOADS
1313 char fullpath[MAX_OSPATH];
1332 if (valid == PATH_INVALID) {
1337 len =
Q_concat(fullpath,
sizeof(fullpath), search->
filename,
"/", file, NULL);
1338 if (len >=
sizeof(fullpath)) {
1339 return Q_ERR_NAMETOOLONG;
1343 if (stat(fullpath, &lstat) == 0) {
1345 *last_modified = lstat.st_mtime;
1346 return Q_ERR_SUCCESS;
1353 return Q_ERR_INVALID_PATH;
1361 char fullpath[MAX_OSPATH];
1372 hash = FS_HashPath(normalized, 0);
1378 if (file->
mode & FS_PATH_MASK) {
1379 if ((file->
mode & search->
mode & FS_PATH_MASK) == 0) {
1386 if ((file->
mode & FS_TYPE_MASK) == FS_TYPE_REAL) {
1390 if (namelen >= MAX_QPATH) {
1395 if ((file->
mode & FS_FLAG_DEFLATE) && pak->
type != FS_ZIP) {
1401 for (; entry; entry = entry->
hash_next) {
1402 if (entry->
namelen != namelen) {
1406 if ((file->
mode & FS_FLAG_DEFLATE) && entry->compmtd != Z_DEFLATED) {
1411 if (!FS_pathcmp(entry->
name, normalized)) {
1417 if ((file->
mode & FS_TYPE_MASK) == FS_TYPE_PAK) {
1421 if (file->
mode & FS_FLAG_DEFLATE) {
1431 if (valid == PATH_INVALID) {
1435 len =
Q_concat(fullpath,
sizeof(fullpath),
1436 search->
filename,
"/", normalized, NULL);
1437 if (len >=
sizeof(fullpath)) {
1438 ret = Q_ERR_NAMETOOLONG;
1443 if (ret != Q_ERR_NOENT)
1447 if (valid == PATH_MIXED_CASE) {
1450 Q_strlwr(fullpath + strlen(search->
filename) + 1);
1452 if (ret != Q_ERR_NOENT)
1460 ret = valid ? Q_ERR_NOENT : Q_ERR_INVALID_PATH;
1470 char normalized[MAX_OSPATH];
1476 if (namelen >= MAX_OSPATH) {
1477 return Q_ERR_NAMETOOLONG;
1482 return Q_ERR_NAMETOOLONG;
1487 return Q_ERR_NAMETOOSHORT;
1491 if (ret == Q_ERR_NOENT) {
1494 if (namelen >= MAX_OSPATH) {
1495 return Q_ERR_NAMETOOLONG;
1515 result = fread(buf, 1, len, file->
fp);
1516 if (result != len) {
1531 result = fread(buf, 1, len, file->
fp);
1532 if (result != len && ferror(file->
fp)) {
1533 file->
error = Q_Errno();
1547 ssize_t
FS_Read(
void *buf,
size_t len, qhandle_t f)
1557 if ((file->
mode & FS_MODE_MASK) != FS_MODE_READ)
1564 if (len > SSIZE_MAX)
1570 switch (file->
type) {
1577 ret = gzread(file->zfp, buf, len);
1579 return Q_ERR_LIBRARY_ERROR;
1583 return read_zip_file(file, buf, len);
1599 if ((file->
mode & FS_MODE_MASK) != FS_MODE_READ)
1606 s = fgets(buffer, size, file->
fp);
1608 return ferror(file->
fp) ? Q_Errno() : 0;
1624 switch (file->
type) {
1630 gzflush(file->zfp, Z_SYNC_FLUSH);
1643 ssize_t
FS_Write(
const void *buf,
size_t len, qhandle_t f)
1651 if ((file->
mode & FS_MODE_MASK) == FS_MODE_READ)
1658 if (len > SSIZE_MAX)
1664 switch (file->
type) {
1666 result = fwrite(buf, 1, len, file->
fp);
1667 if (result != len) {
1674 if (gzwrite(file->zfp, buf, len) == 0) {
1675 file->
error = Q_ERR_LIBRARY_ERROR;
1681 Com_Error(ERR_FATAL,
"%s: bad file type", __func__);
1699 Com_Error(ERR_FATAL,
"%s: NULL", __func__);
1716 if ((mode & FS_MODE_MASK) == FS_MODE_READ) {
1731 const char *dir,
const char *name,
const char *
ext)
1741 len =
Q_concat(buf, size, dir, name, NULL);
1743 Q_PrintError(
"open", Q_ERR_NAMETOOLONG);
1754 if (len != Q_ERR_NOENT) {
1757 if (!COM_CompareExtension(buf,
ext)) {
1766 Q_PrintError(
"open", Q_ERR_NAMETOOLONG);
1776 Com_Printf(
"Couldn't open %s: %s\n", buf,
Q_ErrorString(len));
1782 const char *dir,
const char *name,
const char *
ext)
1784 char normalized[MAX_OSPATH];
1790 if (len >=
sizeof(normalized)) {
1791 Q_PrintError(
"open", Q_ERR_NAMETOOLONG);
1797 Q_PrintError(
"open", Q_ERR_NAMETOOSHORT);
1805 if (!COM_CompareExtension(normalized,
ext)) {
1806 ext = (mode & FS_FLAG_GZIP) ?
"" : NULL;
1810 (mode & FS_FLAG_GZIP) ?
".gz" : NULL, NULL);
1812 Q_PrintError(
"open", Q_ERR_NAMETOOLONG);
1821 if (mode & FS_FLAG_GZIP) {
1833 Com_EPrintf(
"Couldn't open %s: %s\n", buf,
Q_ErrorString(len));
1847 const char *dir,
const char *name,
const char *
ext)
1849 if ((mode & FS_MODE_MASK) == FS_MODE_READ) {
1864 ssize_t
FS_LoadFileEx(
const char *path,
void **buffer,
unsigned flags, memtag_t tag)
1872 Com_Error(ERR_FATAL,
"%s: NULL", __func__);
1889 file->
mode = (flags & ~FS_MODE_MASK) | FS_MODE_READ;
1903 if (len > MAX_LOADFILE) {
1914 len = read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
1945 ret = write == len ? Q_ERR_SUCCESS : write < 0 ? write : Q_ERR_FAILURE;
1961 const char *dir,
const char *name,
const char *
ext,
1962 const void *data,
size_t len)
1975 ret = write == len ? Q_ERR_SUCCESS : write < 0 ? write : Q_ERR_FAILURE;
1980 Com_EPrintf(
"Couldn't write %s: %s\n", buf,
Q_ErrorString(ret));
1994 qerror_t FS_RenameFile(
const char *from,
const char *to)
1996 char normalized[MAX_OSPATH];
1997 char frompath[MAX_OSPATH];
1998 char topath[MAX_OSPATH];
2003 if (len >=
sizeof(normalized))
2004 return Q_ERR_NAMETOOLONG;
2007 return Q_ERR_INVALID_PATH;
2010 if (len >=
sizeof(frompath))
2011 return Q_ERR_NAMETOOLONG;
2015 if (len >=
sizeof(normalized))
2016 return Q_ERR_NAMETOOLONG;
2019 return Q_ERR_INVALID_PATH;
2022 if (len >=
sizeof(topath))
2023 return Q_ERR_NAMETOOLONG;
2026 if (rename(frompath, topath))
2029 return Q_ERR_SUCCESS;
2032 #endif // USE_CLIENT
2042 char string[MAXPRINTMSG];
2045 va_start(argptr, format);
2046 len =
Q_vsnprintf(
string,
sizeof(
string), format, argptr);
2049 if (len >=
sizeof(
string)) {
2050 return Q_ERR_STRING_TRUNCATED;
2069 if (!
pack->refcount) {
2070 Com_Error(ERR_FATAL,
"%s: refcount already zero", __func__);
2072 if (!--
pack->refcount) {
2081 unsigned num_files,
size_t names_len)
2087 hash_size = npot32(num_files / 3);
2089 len = strlen(name) + 1;
2097 pack->num_files = num_files;
2098 pack->hash_size = hash_size;
2101 pack->filename = (
char *)(
pack->file_hash + hash_size);
2102 pack->names =
pack->filename + len;
2103 memcpy(
pack->filename, name, len);
2116 hash = FS_HashPath(file->
name,
pack->hash_size);
2118 pack->file_hash[hash] = file;
2125 dpackheader_t header;
2128 unsigned i, num_files;
2130 size_t len, names_len;
2133 dpackfile_t info[MAX_FILES_IN_PACK];
2135 fp = fopen(packfile,
"rb");
2137 Com_Printf(
"Couldn't open %s: %s\n", packfile, strerror(errno));
2141 if (fread(&header, 1,
sizeof(header), fp) !=
sizeof(header)) {
2142 Com_Printf(
"Reading header failed on %s\n", packfile);
2146 if (LittleLong(header.ident) != IDPAKHEADER) {
2147 Com_Printf(
"%s is not a 'PACK' file\n", packfile);
2151 header.dirlen = LittleLong(header.dirlen);
2152 if (header.dirlen > INT_MAX || header.dirlen %
sizeof(dpackfile_t)) {
2153 Com_Printf(
"%s has bad directory length\n", packfile);
2157 num_files = header.dirlen /
sizeof(dpackfile_t);
2158 if (num_files < 1) {
2159 Com_Printf(
"%s has no files\n", packfile);
2162 if (num_files > MAX_FILES_IN_PACK) {
2163 Com_Printf(
"%s has too many files: %u > %u\n", packfile, num_files, MAX_FILES_IN_PACK);
2167 header.dirofs = LittleLong(header.dirofs);
2168 if (header.dirofs > LONG_MAX - header.dirlen) {
2169 Com_Printf(
"%s has bad directory offset\n", packfile);
2172 if (fseek(fp, (
long)header.dirofs, SEEK_SET)) {
2173 Com_Printf(
"Seeking to directory failed on %s\n", packfile);
2176 if (fread(info, 1, header.dirlen, fp) != header.dirlen) {
2177 Com_Printf(
"Reading directory failed on %s\n", packfile);
2182 for (i = 0, dfile = info; i < num_files; i++, dfile++) {
2183 dfile->filepos = LittleLong(dfile->filepos);
2184 dfile->filelen = LittleLong(dfile->filelen);
2185 if (dfile->filelen > INT_MAX || dfile->filepos > INT_MAX - dfile->filelen) {
2186 Com_Printf(
"%s has bad directory structure\n", packfile);
2189 dfile->name[
sizeof(dfile->name) - 1] = 0;
2190 names_len += strlen(dfile->name) + 1;
2199 for (i = 0, dfile = info; i < num_files; i++, dfile++) {
2200 len = strlen(dfile->name) + 1;
2202 file->
name = memcpy(name, dfile->name, len);
2205 file->
filepos = dfile->filepos;
2206 file->
filelen = dfile->filelen;
2208 file->coherent = qtrue;
2216 packfile,
pack->num_files,
pack->hash_size);
2228 static size_t search_central_header(FILE *fp)
2230 size_t file_size, back_read;
2231 size_t max_back = 0xffff;
2232 byte buf[ZIP_BUFREADCOMMENT + 4];
2235 if (fseek(fp, 0, SEEK_END) == -1)
2241 file_size = (size_t)ret;
2242 if (max_back > file_size)
2243 max_back = file_size;
2246 while (back_read < max_back) {
2247 size_t i, read_size, read_pos;
2249 if (back_read + ZIP_BUFREADCOMMENT > max_back)
2250 back_read = max_back;
2252 back_read += ZIP_BUFREADCOMMENT;
2254 read_pos = file_size - back_read;
2256 read_size = back_read;
2257 if (read_size > ZIP_BUFREADCOMMENT + 4)
2258 read_size = ZIP_BUFREADCOMMENT + 4;
2260 if (fseek(fp, (
long)read_pos, SEEK_SET) == -1)
2262 if (fread(buf, 1, read_size, fp) != read_size)
2268 if (LittleLongMem(buf + i) == ZIP_ENDHEADERMAGIC)
2269 return read_pos + i;
2277 static size_t get_file_info(FILE *fp,
size_t pos,
packfile_t *file,
size_t *len,
size_t remaining)
2279 size_t name_size, xtra_size, comm_size;
2280 size_t comp_len, file_len, file_pos;
2282 byte header[ZIP_SIZECENTRALDIRITEM];
2288 if (fseek(fp, (
long)pos, SEEK_SET) == -1)
2290 if (fread(header, 1,
sizeof(header), fp) !=
sizeof(header))
2294 if (LittleLongMem(&header[0]) != ZIP_CENTRALHEADERMAGIC)
2297 comp_mtd = LittleShortMem(&header[10]);
2298 comp_len = LittleLongMem(&header[20]);
2299 file_len = LittleLongMem(&header[24]);
2300 name_size = LittleShortMem(&header[28]);
2301 xtra_size = LittleShortMem(&header[30]);
2302 comm_size = LittleShortMem(&header[32]);
2303 file_pos = LittleLongMem(&header[42]);
2305 if (file_len > LONG_MAX)
2307 if (comp_len > LONG_MAX || file_pos > LONG_MAX - comp_len)
2310 if (!file_len || !comp_len) {
2314 if (file_len != comp_len) {
2315 FS_DPrintf(
"%s: skipping file stored with file_len != comp_len\n", __func__);
2318 }
else if (comp_mtd != Z_DEFLATED) {
2319 FS_DPrintf(
"%s: skipping file compressed with unknown method\n", __func__);
2323 FS_DPrintf(
"%s: skipping file with empty name\n", __func__);
2326 if (name_size >= MAX_QPATH) {
2327 FS_DPrintf(
"%s: skipping file with oversize name\n", __func__);
2333 if (name_size >= remaining)
2335 file->compmtd = comp_mtd;
2336 file->complen = comp_len;
2339 if (fread(file->
name, 1, name_size, fp) != name_size)
2341 file->
name[name_size] = 0;
2344 *len = name_size + 1;
2347 return ZIP_SIZECENTRALDIRITEM + name_size + xtra_size + comm_size;
2350 static pack_t *load_zip_file(
const char *packfile)
2354 size_t len, names_len;
2355 unsigned i, num_disk, num_disk_cd, num_files, num_files_cd;
2356 size_t header_pos, central_ofs, central_size, central_end;
2357 size_t extra_bytes, ofs;
2360 byte header[ZIP_SIZECENTRALHEADER];
2362 fp = fopen(packfile,
"rb");
2364 Com_Printf(
"Couldn't open %s: %s\n", packfile, strerror(errno));
2368 header_pos = search_central_header(fp);
2370 Com_Printf(
"No central header found in %s\n", packfile);
2373 if (fseek(fp, (
long)header_pos, SEEK_SET) == -1) {
2374 Com_Printf(
"Couldn't seek to central header in %s\n", packfile);
2377 if (fread(header, 1,
sizeof(header), fp) !=
sizeof(header)) {
2378 Com_Printf(
"Reading central header failed on %s\n", packfile);
2382 num_disk = LittleShortMem(&header[4]);
2383 num_disk_cd = LittleShortMem(&header[6]);
2384 num_files = LittleShortMem(&header[8]);
2385 num_files_cd = LittleShortMem(&header[10]);
2386 if (num_files_cd != num_files || num_disk_cd != 0 || num_disk != 0) {
2387 Com_Printf(
"%s is an unsupported multi-part archive\n", packfile);
2390 if (num_files < 1) {
2391 Com_Printf(
"%s has no files\n", packfile);
2394 if (num_files > ZIP_MAXFILES) {
2395 Com_Printf(
"%s has too many files: %u > %u\n", packfile, num_files, ZIP_MAXFILES);
2399 central_size = LittleLongMem(&header[12]);
2400 central_ofs = LittleLongMem(&header[16]);
2401 central_end = central_ofs + central_size;
2402 if (central_end > header_pos || central_end < central_ofs) {
2403 Com_Printf(
"%s has bad central directory offset\n", packfile);
2408 extra_bytes = header_pos - central_end;
2410 Com_Printf(
"%s has %"PRIz
" extra bytes at the beginning, funny sfx archive?\n",
2411 packfile, extra_bytes);
2417 header_pos = central_ofs + extra_bytes;
2418 for (i = 0; i < num_files_cd; i++) {
2419 ofs = get_file_info(fp, header_pos, NULL, &len, 0);
2421 Com_Printf(
"%s has bad central directory structure (pass %d)\n", packfile, 1);
2433 Com_Printf(
"%s has no valid files\n", packfile);
2443 header_pos = central_ofs + extra_bytes;
2444 for (i = 0; i < num_files_cd; i++) {
2448 ofs = get_file_info(fp, header_pos, file, &len, names_len);
2450 Com_Printf(
"%s has bad central directory structure (pass %d)\n", packfile, 2);
2458 file->coherent = qfalse;
2471 FS_DPrintf(
"%s: %u files, %u skipped, %u hash\n",
2472 packfile,
pack->num_files, num_files_cd -
pack->num_files,
pack->hash_size);
2487 static int pakcmp(
const void *p1,
const void *p2)
2489 char *s1 = *(
char **)p1;
2490 char *s2 = *(
char **)p2;
2492 if (!Q_stricmpn(s1,
"pak", 3)) {
2493 if (!Q_stricmpn(s2,
"pak", 3)) {
2494 unsigned long n1 = strtoul(s1 + 3, &s1, 10);
2495 unsigned long n2 = strtoul(s2 + 3, &s2, 10);
2506 if (!Q_stricmpn(s2,
"pak", 3)) {
2511 return Q_stricmp(s1, s2);
2516 static void q_printf(2, 3) add_game_dir(
unsigned mode, const
char *fmt, ...)
2521 void *files[MAX_LISTED_FILES];
2523 char path[MAX_OSPATH];
2526 va_start(argptr, fmt);
2531 Com_EPrintf(
"%s: refusing oversize path\n", __func__);
2540 #define PAK_EXT ".pak;.pkz"
2542 #define PAK_EXT ".pak"
2550 if (!(mode & FS_PATH_GAME) && !count) {
2554 qsort(files, count,
sizeof(files[0]),
pakcmp);
2556 for (i = 0; i < count; i++) {
2558 if (len >=
sizeof(path)) {
2559 Com_EPrintf(
"%s: refusing oversize path\n", __func__);
2564 if (len > 4 && !Q_stricmp(path + len - 4,
".pkz"))
2565 pack = load_zip_file(path);
2572 search->
mode = mode;
2579 for (i = 0; i < count; i++) {
2586 search->
mode = mode;
2587 search->
pack = NULL;
2599 file_info_t *
FS_CopyInfo(
const char *name,
size_t size, time_t ctime, time_t mtime)
2609 out = FS_Mallocz(
sizeof(*out) + len);
2613 memcpy(out->name, name, len + 1);
2627 out = FS_Malloc(
sizeof(
void *) * (count + 1));
2628 for (i = 0; i < count; i++) {
2642 filter = strchr(filter,
';');
2643 if (filter) filter++;
2652 const char *e, *n, *l;
2654 if (!name[0] || !
ext[0]) {
2658 for (l = name; l[1]; l++)
2661 for (e =
ext; e[1]; e++)
2695 static int infocmp(
const void *p1,
const void *p2)
2697 file_info_t *n1 = *(file_info_t **)p1;
2698 file_info_t *n2 = *(file_info_t **)p2;
2700 return FS_pathcmp(n1->name, n2->name);
2705 char *s1 = *(
char **)p1;
2706 char *s2 = *(
char **)p2;
2708 return FS_pathcmp(s1, s2);
2723 void *files[MAX_LISTED_FILES], *info;
2724 int i, j, count, total;
2725 char normalized[MAX_OSPATH], buffer[MAX_OSPATH];
2727 size_t len, pathlen;
2740 if (pathlen >=
sizeof(normalized)) {
2748 if ((flags & FS_SEARCH_DIRSONLY) && (flags & FS_SEARCH_MASK & ~FS_SEARCH_DIRSONLY)) {
2753 if (flags & FS_PATH_MASK) {
2754 if ((flags & search->
mode & FS_PATH_MASK) == 0) {
2759 if ((flags & FS_TYPE_MASK) == FS_TYPE_REAL) {
2769 if (file->
namelen < pathlen) {
2772 if (FS_pathcmpn(s, path, pathlen)) {
2775 if (s[pathlen] !=
'/') {
2778 if (flags & FS_SEARCH_BYFILTER) {
2781 }
else if (path == normalized) {
2782 if (!(flags & FS_SEARCH_DIRSONLY) && strchr(s,
'/')) {
2789 if (flags & FS_SEARCH_BYFILTER) {
2801 if (flags & (FS_SEARCH_DIRSONLY | FS_SEARCH_STRIPEXT)) {
2802 s = strcpy(buffer, s);
2806 if (flags & FS_SEARCH_DIRSONLY) {
2816 for (j = 0; j < count; j++) {
2817 if (!FS_pathcmp(files[j], s)) {
2827 if (!(flags & FS_SEARCH_SAVEPATH)) {
2832 if (flags & FS_SEARCH_STRIPEXT) {
2841 if (flags & FS_SEARCH_EXTRAINFO) {
2844 info = FS_CopyString(s);
2847 files[count++] = info;
2849 if (count >= MAX_LISTED_FILES) {
2854 if ((flags & FS_TYPE_MASK) == FS_TYPE_PAK) {
2861 if (len + pathlen + 1 >= MAX_OSPATH) {
2867 if (valid == PATH_INVALID) {
2870 s = memcpy(buffer, search->
filename, len);
2872 memcpy(s + len, path, pathlen + 1);
2877 if (flags & FS_SEARCH_BYFILTER) {
2884 if (count >= MAX_LISTED_FILES) {
2897 if (flags & FS_SEARCH_EXTRAINFO) {
2899 qsort(files, count,
sizeof(files[0]),
infocmp);
2903 qsort(files, count,
sizeof(files[0]),
alphacmp);
2907 for (i = 1; i < count; i++) {
2908 if (!FS_pathcmp(files[i - 1], files[i])) {
2910 files[i - 1] = NULL;
2917 list = FS_Malloc(
sizeof(
void *) * (total + 1));
2920 for (i = 0; i < count; i++) {
2922 list[total++] = files[i];
2947 for (p = list; *p; p++) {
2954 void FS_File_g(
const char *path,
const char *
ext,
unsigned flags, genctx_t *ctx)
2965 for (i = 0; i < numFiles; i++) {
2967 if (ctx->count < ctx->size && !strncmp(s, ctx->partial, ctx->length)) {
2968 ctx->matches[ctx->count++] = s;
2980 int i, listed, total;
2985 listed = total > 128 ? 128 : total;
2986 for (i = 0; i < listed; i++) {
2987 Com_Printf(
"%s\n", (
char *)list[i]);
2992 if (listed == total) {
2993 Com_Printf(
"%i files listed\n", listed);
2995 Com_Printf(
"%i files listed (%d files more)\n", listed, total - listed);
3010 Com_Printf(
"Usage: %s <filter> [full_path]\n",
Cmd_Argv(0));
3016 flags = FS_SEARCH_BYFILTER;
3018 flags |= FS_SEARCH_SAVEPATH;
3034 Com_Printf(
"Usage: %s <directory> [.extension]\n",
Cmd_Argv(0));
3057 char normalized[MAX_OSPATH], fullpath[MAX_OSPATH];
3066 size_t len, namelen;
3067 qboolean report_all;
3070 Com_Printf(
"Usage: %s <path> [all]\n",
Cmd_Argv(0));
3076 if (namelen >= MAX_OSPATH) {
3077 Com_Printf(
"Refusing to lookup oversize path.\n");
3084 if (namelen >= MAX_OSPATH) {
3085 Com_Printf(
"Oversize symbolic link ('%s --> '%s').\n",
3090 Com_Printf(
"Symbolic link ('%s' --> '%s') in effect.\n",
3100 Com_Printf(
"Refusing to lookup empty path.\n");
3107 if (namelen >= MAX_QPATH) {
3108 Com_Printf(
"Not searching for '%s' in pack files "
3109 "since path length exceedes %d characters.\n",
3110 normalized, MAX_QPATH - 1);
3113 hash = FS_HashPath(normalized, 0);
3122 if (namelen >= MAX_QPATH) {
3128 for (; entry; entry = entry->
hash_next) {
3129 if (entry->
namelen != namelen) {
3132 if (!FS_pathcmp(entry->
name, normalized)) {
3134 Com_Printf(
"%s/%s (%"PRIz
" bytes)\n", pak->
filename,
3145 if (valid == PATH_INVALID) {
3147 Com_Printf(
"Not searching for '%s' in physical file "
3148 "system since path contains invalid characters.\n",
3152 if (valid == PATH_INVALID) {
3157 len =
Q_concat(fullpath, MAX_OSPATH,
3158 search->
filename,
"/", normalized, NULL);
3159 if (len >= MAX_OSPATH) {
3160 Com_WPrintf(
"Full path length '%s/%s' exceeded %d characters.\n",
3161 search->
filename, normalized, MAX_OSPATH - 1);
3171 if (ret == Q_ERR_NOENT && valid == PATH_MIXED_CASE) {
3172 Q_strlwr(fullpath + strlen(search->
filename) + 1);
3174 if (ret == Q_ERR_SUCCESS)
3175 Com_Printf(
"Physical path found after converting to lower case.\n");
3179 if (ret == Q_ERR_SUCCESS) {
3180 Com_Printf(
"%s (%"PRIz
" bytes)\n", fullpath, info.size);
3185 }
else if (ret != Q_ERR_NOENT) {
3186 Com_EPrintf(
"Couldn't get info on '%s': %s\n",
3195 if ((total == 0 || report_all) && link == NULL) {
3199 if (namelen >= MAX_OSPATH) {
3200 Com_Printf(
"Oversize symbolic link ('%s --> '%s').\n",
3205 Com_Printf(
"Symbolic link ('%s' --> '%s') in effect.\n",
3212 Com_Printf(
"%d instances of %s\n", total, normalized);
3214 Com_Printf(
"%s was not found\n", normalized);
3226 int numFilesInPAK = 0;
3228 int numFilesInZIP = 0;
3230 Com_Printf(
"Current search path:\n");
3245 if (numFilesInPAK) {
3246 Com_Printf(
"%i files in PAK files\n", numFilesInPAK);
3250 if (numFilesInZIP) {
3251 Com_Printf(
"%i files in PKZ files\n", numFilesInZIP);
3262 static void FS_Stats_f(
void)
3268 int len, maxLen = 0;
3269 int totalHashSize, totalLen;
3271 totalHashSize = totalLen = 0;
3276 for (i = 0; i <
pack->hash_size; i++) {
3277 if (!(file =
pack->file_hash[i])) {
3285 max =
pack->file_hash[i];
3295 Com_Printf(
"Total calls to open_file_read: %d\n", fs_count_read);
3296 Com_Printf(
"Total path comparsions: %d\n", fs_count_strcmp);
3297 Com_Printf(
"Total calls to open_from_disk: %d\n", fs_count_open);
3298 Com_Printf(
"Total mixed-case reopens: %d\n", fs_count_strlwr);
3300 if (!totalHashSize) {
3301 Com_Printf(
"No stats to display\n");
3305 Com_Printf(
"Maximum hash bucket length is %d, average is %.2f\n", maxLen, (
float)totalLen / totalHashSize);
3307 Com_Printf(
"Dumping longest bucket (%s):\n", maxpack->
filename);
3308 for (file = max; file; file = file->
hash_next) {
3309 Com_Printf(
"%s\n", file->
name);
3320 if (!strncmp(
Cmd_Argv(ctx->argnum - 1),
"soft", 4))
3353 static const cmd_option_t options[] = {
3354 {
"a",
"all",
"delete all links" },
3355 {
"h",
"help",
"display this message" },
3363 if (!strncmp(
Cmd_Argv(0),
"soft", 4))
3372 Com_Printf(
"Deletes a symbolic link with the specified name.");
3377 Com_Printf(
"Deleted all symbolic links.\n");
3386 Com_Printf(
"Missing name argument.\n");
3392 if (!FS_pathcmp(link->
name, name)) {
3393 List_Remove(&link->
entry);
3400 Com_Printf(
"Symbolic link '%s' does not exist.\n", name);
3408 size_t namelen, targlen;
3409 char name[MAX_OSPATH];
3410 char target[MAX_OSPATH];
3412 if (!strncmp(
Cmd_Argv(0),
"soft", 4))
3421 Com_Printf(
"%s --> %s\n", link->
name, link->
target);
3424 Com_Printf(
"------------------\n"
3425 "%d symbolic link%s listed.\n", count, count == 1 ?
"" :
"s");
3430 Com_Printf(
"Usage: %s <name> <target>\n"
3431 "Creates symbolic link to target with the specified name.\n"
3432 "Virtual quake paths are accepted.\n"
3433 "Links are effective only for reading.\n",
3439 if (namelen == 0 || namelen >=
sizeof(name)) {
3440 Com_Printf(
"Invalid symbolic link name.\n");
3445 if (targlen == 0 || targlen >=
sizeof(target)) {
3446 Com_Printf(
"Invalid symbolic link target.\n");
3452 if (!FS_pathcmp(link->
name, name)) {
3459 link = FS_Malloc(
sizeof(*link) + namelen);
3460 memcpy(link->
name, name, namelen + 1);
3462 List_Append(list, &link->
entry);
3465 link->
target = FS_CopyString(target);
3504 add_game_dir(FS_PATH_BASE | FS_PATH_GAME,
"%s/"BASEGAME,
sys_basedir->string);
3519 add_game_dir(FS_PATH_BASE,
"%s/"BASEGAME,
sys_homedir->string);
3525 path->
mode &= ~FS_PATH_GAME;
3533 add_game_dir(FS_PATH_BASE | FS_PATH_GAME,
3539 path->
mode |= FS_PATH_GAME;
3558 Com_Printf(
"----- FS_Restart -----\n");
3577 Com_Printf(
"----------------------\n");
3597 {
"fs_stats", FS_Stats_f },
3626 Com_WPrintf(
"%s: closing handle %d\n", __func__, i + 1);
3639 inflateEnd(&fs_zipstream.stream);
3652 char *s =
self->string;
3656 if (!Q_stricmp(s, BASEGAME)) {
3659 Com_Printf(
"'%s' should contain characters [A-Za-z0-9_-] only.\n", self->name);
3676 if (FS_FileExists(
"maps/base1.bsp"))
3681 if (!FS_FileExists(
"pics/colormap.pcx") || !FS_FileExists(
"pics/conchars.pcx") || !FS_FileExists(
"default.cfg"))
3683 Com_Error(ERR_FATAL,
"No game data files detected. Please make sure that there are .pak files"
3684 " in the game directory: %s.\nReinstalling the game can fix the issue.",
fs_gamedir);
3696 if (!FS_FileExistsEx(COM_AUTOEXEC_CFG, FS_TYPE_REAL | FS_PATH_BASE)) {
3716 Com_Printf(
"------- FS_Init -------\n");
3724 fs_debug =
Cvar_Get(
"fs_debug",
"0", 0);
3734 Com_Printf(
"-----------------------\n");