4 Copyright (C) 2003 Mathieu Olivier
5 Copyright (C) 1999,2000 contributors of the QuakeForge project
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 See the GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to:
21 Free Software Foundation, Inc.
22 59 Temple Place - Suite 330
23 Boston, MA 02111-1307, USA
38 # include <sys/stat.h>
51 All of Quake's data access is through a hierchal file system, but the contents
52 of the file system can be transparently merged from several sources.
54 The "base directory" is the path to the directory holding the quake.exe and
55 all game directories. The sys_* files pass this to host_init in
56 quakeparms_t->basedir. This can be overridden with the "-basedir" command
57 line parm to allow code debugging in a different directory. The base
58 directory is only used during filesystem initialization.
60 The "game directory" is the first tree on the search path and directory that
61 all generated files (savegames, screenshots, demos, config files) will be
62 saved to. This can be overridden with the "-game" command line parameter.
63 The game directory can never be changed while quake is executing. This is a
64 precacution against having a malicious server instruct clients to write files
65 over areas they shouldn't.
71 =============================================================================
75 =============================================================================
78 // Magic numbers of a ZIP file (big-endian format)
79 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
80 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
81 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
83 // Other constants for ZIP files
84 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
85 #define ZIP_END_CDIR_SIZE 22
86 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
87 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
89 // Zlib constants (from zlib.h)
90 #define Z_SYNC_FLUSH 2
93 #define Z_STREAM_END 1
94 #define ZLIB_VERSION "1.1.4"
98 =============================================================================
102 =============================================================================
105 // Zlib stream (from zlib.h)
106 // Warning: some pointers we don't use directly have
107 // been cast to "void*" for a matter of simplicity
110 qbyte *next_in; // next input byte
111 unsigned int avail_in; // number of bytes available at next_in
112 unsigned long total_in; // total nb of input bytes read so far
114 qbyte *next_out; // next output byte should be put there
115 unsigned int avail_out; // remaining free space at next_out
116 unsigned long total_out; // total nb of bytes output so far
118 char *msg; // last error message, NULL if no error
119 void *state; // not visible by applications
121 void *zalloc; // used to allocate the internal state
122 void *zfree; // used to free the internal state
123 void *opaque; // private data object passed to zalloc and zfree
125 int data_type; // best guess about the data type: ascii or binary
126 unsigned long adler; // adler32 value of the uncompressed data
127 unsigned long reserved; // reserved for future use
131 // Our own file structure on top of FILE
135 FS_FLAG_PACKED = (1 << 0), // inside a package (PAK or PK3)
136 FS_FLAG_DEFLATED = (1 << 1) // file is compressed using the deflate algorithm (PK3 only)
139 #define ZBUFF_SIZE 1024
143 size_t real_length; // length of the uncompressed file
144 size_t in_ind, in_max;
145 // size_t in_position; // we use "file->position" directly instead
146 size_t out_ind, out_max;
147 size_t out_position; // virtual position in the uncompressed file
148 qbyte input [ZBUFF_SIZE];
149 qbyte output [ZBUFF_SIZE];
156 size_t length; // file size (PACKED only)
157 size_t offset; // offset into a package (PACKED only)
158 size_t position; // current position in the file (PACKED only)
159 ztoolkit_t* z; // used for inflating (DEFLATED only)
163 // ------ PK3 files on disk ------ //
165 // You can get the complete ZIP format description from PKWARE website
169 unsigned int signature;
170 unsigned short disknum;
171 unsigned short cdir_disknum; // number of the disk with the start of the central directory
172 unsigned short localentries; // number of entries in the central directory on this disk
173 unsigned short nbentries; // total number of entries in the central directory on this disk
174 unsigned int cdir_size; // size of the central directory
175 unsigned int cdir_offset; // with respect to the starting disk number
176 unsigned short comment_size;
177 } pk3_endOfCentralDir_t;
180 // ------ PAK files on disk ------ //
184 int filepos, filelen;
195 // Packages in memory
199 FILE_FLAG_TRUEOFFS = (1 << 0), // the offset in packfile_t is the true contents offset
200 FILE_FLAG_DEFLATED = (1 << 1) // file compressed using the deflate algorithm
205 char name [MAX_QPATH];
208 size_t packsize; // size in the package
209 size_t realsize; // real file size (uncompressed)
212 typedef struct pack_s
214 char filename [MAX_OSPATH];
216 int ignorecase; // LordHavoc: pk3 ignores case
224 // Search paths for files (including packages)
225 typedef struct searchpath_s
227 // only one of filename / pack will be used
228 char filename[MAX_OSPATH];
230 struct searchpath_s *next;
235 =============================================================================
239 =============================================================================
242 mempool_t *fs_mempool;
243 mempool_t *pak_mempool;
247 pack_t *packlist = NULL;
249 searchpath_t *fs_searchpaths;
251 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
252 #define MAX_FILES_IN_PACK 65536
254 char fs_gamedir[MAX_OSPATH];
255 char fs_basedir[MAX_OSPATH];
257 qboolean fs_modified; // set true if using non-id files
261 =============================================================================
263 PRIVATE FUNCTIONS - PK3 HANDLING
265 =============================================================================
268 // Functions exported from zlib
270 # define ZEXPORT WINAPI
275 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
276 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
277 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
278 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
280 #define qz_inflateInit2(strm, windowBits) \
281 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
283 static dllfunction_t zlibfuncs[] =
285 {"inflate", (void **) &qz_inflate},
286 {"inflateEnd", (void **) &qz_inflateEnd},
287 {"inflateInit2_", (void **) &qz_inflateInit2_},
288 {"inflateReset", (void **) &qz_inflateReset},
292 // Handle for Zlib DLL
293 static dllhandle_t zlib_dll = NULL;
303 void PK3_CloseLibrary (void)
308 Sys_UnloadLibrary (zlib_dll);
317 Try to load the Zlib DLL
320 qboolean PK3_OpenLibrary (void)
323 const dllfunction_t *func;
330 dllname = "zlib.dll";
332 dllname = "libz.so.1";
336 for (func = zlibfuncs; func && func->name != NULL; func++)
337 *func->funcvariable = NULL;
340 if (! (zlib_dll = Sys_LoadLibrary (dllname)))
342 Con_Printf("Can't find %s. Compressed files support disabled\n", dllname);
346 // Get the function adresses
347 for (func = zlibfuncs; func && func->name != NULL; func++)
348 if (!(*func->funcvariable = (void *) Sys_GetProcAddress (zlib_dll, func->name)))
350 Con_Printf("missing function \"%s\" - broken Zlib library!\n", func->name);
355 Con_Printf("%s loaded. Compressed files support enabled\n", dllname);
362 PK3_GetEndOfCentralDir
364 Extract the end of the central directory from a PK3 package
367 qboolean PK3_GetEndOfCentralDir (const char *packfile, FILE *packhandle, pk3_endOfCentralDir_t *eocd)
369 long filesize, maxsize;
373 // Get the package size
374 fseek (packhandle, 0, SEEK_END);
375 filesize = ftell (packhandle);
376 if (filesize < ZIP_END_CDIR_SIZE)
379 // Load the end of the file in memory
380 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
383 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
384 buffer = Mem_Alloc (tempmempool, maxsize);
385 fseek (packhandle, filesize - maxsize, SEEK_SET);
386 if (fread (buffer, 1, maxsize, packhandle) != (unsigned long) maxsize)
392 // Look for the end of central dir signature around the end of the file
393 maxsize -= ZIP_END_CDIR_SIZE;
394 ptr = &buffer[maxsize];
396 while (BuffBigLong (ptr) != ZIP_END_HEADER)
408 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
409 eocd->signature = LittleLong (eocd->signature);
410 eocd->disknum = LittleShort (eocd->disknum);
411 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
412 eocd->localentries = LittleShort (eocd->localentries);
413 eocd->nbentries = LittleShort (eocd->nbentries);
414 eocd->cdir_size = LittleLong (eocd->cdir_size);
415 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
416 eocd->comment_size = LittleShort (eocd->comment_size);
428 Extract the file list from a PK3 file
431 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
433 qbyte *central_dir, *ptr;
437 // Load the central directory in memory
438 central_dir = Mem_Alloc (tempmempool, eocd->cdir_size);
439 fseek (pack->handle, eocd->cdir_offset, SEEK_SET);
440 fread (central_dir, 1, eocd->cdir_size, pack->handle);
442 // Extract the files properties
443 // The parsing is done "by hand" because some fields have variable sizes and
444 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
445 remaining = eocd->cdir_size;
448 for (ind = 0; ind < eocd->nbentries; ind++)
450 size_t namesize, count;
453 // Checking the remaining size
454 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
456 Mem_Free (central_dir);
459 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
462 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
464 Mem_Free (central_dir);
468 namesize = BuffLittleShort (&ptr[28]); // filename length
470 // Check encryption, compression, and attributes
471 // 1st uint8 : general purpose bit flag
472 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
473 // 2nd uint8 : external file attributes
474 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
475 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
477 // Still enough bytes for the name?
478 if ((size_t) remaining < namesize || namesize >= sizeof (*pack->files))
480 Mem_Free (central_dir);
484 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
485 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
488 file = &pack->files[pack->numfiles];
489 memcpy (file->name, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
490 file->name[namesize] = '\0';
492 // Compression, sizes and offset
493 if (BuffLittleShort (&ptr[10]))
494 file->flags = FILE_FLAG_DEFLATED;
495 file->packsize = BuffLittleLong (&ptr[20]);
496 file->realsize = BuffLittleLong (&ptr[24]);
497 file->offset = BuffLittleLong (&ptr[42]);
503 // Skip the name, additionnal field, and comment
504 // 1er uint16 : extra field length
505 // 2eme uint16 : file comment length
506 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
507 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
511 Mem_Free (central_dir);
512 return pack->numfiles;
520 Create a package entry associated with a PK3 file
523 pack_t *FS_LoadPackPK3 (const char *packfile)
526 pk3_endOfCentralDir_t eocd;
530 packhandle = fopen (packfile, "rb");
534 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
535 Sys_Error ("%s is not a PK3 file", packfile);
537 // Multi-volume ZIP archives are NOT allowed
538 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
539 Sys_Error ("%s is a multi-volume ZIP archive", packfile);
541 // LordHavoc: was always false because nbentries is an unsigned short and MAX_FILES_IN_PACK is 65536
542 //if (eocd.nbentries > (unsigned int) MAX_FILES_IN_PACK)
543 // Sys_Error ("%s contains too many files (%hu)", packfile, eocd.nbentries);
545 // Create a package structure in memory
546 pack = Mem_Alloc (pak_mempool, sizeof (pack_t));
547 pack->ignorecase = true; // LordHavoc: pk3 ignores case
548 strcpy (pack->filename, packfile);
549 pack->handle = packhandle;
550 pack->numfiles = eocd.nbentries;
551 pack->mempool = Mem_AllocPool (packfile);
552 pack->files = Mem_Alloc (pack->mempool, eocd.nbentries * sizeof(packfile_t));
553 pack->next = packlist;
556 real_nb_files = PK3_BuildFileList (pack, &eocd);
557 if (real_nb_files <= 0)
558 Sys_Error ("%s is not a valid PK3 file", packfile);
560 Con_Printf ("Added packfile %s (%i files)\n", packfile, real_nb_files);
567 PK3_GetTrueFileOffset
569 Find where the true file data offset is
572 void PK3_GetTrueFileOffset (packfile_t *file, pack_t *pack)
574 qbyte buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
578 if (file->flags & FILE_FLAG_TRUEOFFS)
581 // Load the local file description
582 fseek (pack->handle, file->offset, SEEK_SET);
583 count = fread (buffer, 1, ZIP_LOCAL_CHUNK_BASE_SIZE, pack->handle);
584 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
585 Sys_Error ("Can't retrieve file %s in package %s", file->name, pack->filename);
587 // Skip name and extra field
588 file->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
590 file->flags |= FILE_FLAG_TRUEOFFS;
595 =============================================================================
597 OTHER PRIVATE FUNCTIONS
599 =============================================================================
607 Only used for FS_WriteFile.
610 void FS_CreatePath (char *path)
614 for (ofs = path+1 ; *ofs ; ofs++)
616 if (*ofs == '/' || *ofs == '\\')
618 // create the directory
634 void FS_Path_f (void)
638 Con_Printf ("Current search path:\n");
639 for (s=fs_searchpaths ; s ; s=s->next)
643 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
646 Con_Printf ("%s\n", s->filename);
655 Takes an explicit (not game tree related) path to a pak file.
657 Loads the header and directory, adding the files at the beginning
658 of the list so they override previous pack files.
661 pack_t *FS_LoadPackPAK (const char *packfile)
663 dpackheader_t header;
667 dpackfile_t *info; // temporary alloc, allowing huge pack directories
669 packhandle = fopen (packfile, "rb");
673 fread ((void *)&header, 1, sizeof(header), packhandle);
674 if (memcmp(header.id, "PACK", 4))
675 Sys_Error ("%s is not a packfile", packfile);
676 header.dirofs = LittleLong (header.dirofs);
677 header.dirlen = LittleLong (header.dirlen);
679 if (header.dirlen % sizeof(dpackfile_t))
680 Sys_Error ("%s has an invalid directory size", packfile);
682 numpackfiles = header.dirlen / sizeof(dpackfile_t);
684 if (numpackfiles > MAX_FILES_IN_PACK)
685 Sys_Error ("%s has %i files", packfile, numpackfiles);
687 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
688 pack->ignorecase = false; // LordHavoc: pak is case sensitive
689 strcpy (pack->filename, packfile);
690 pack->handle = packhandle;
691 pack->numfiles = numpackfiles;
692 pack->mempool = Mem_AllocPool(packfile);
693 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
694 pack->next = packlist;
697 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
698 fseek (packhandle, header.dirofs, SEEK_SET);
699 fread ((void *)info, 1, header.dirlen, packhandle);
701 // parse the directory
702 for (i = 0;i < numpackfiles;i++)
705 packfile_t *file = &pack->files[i];
707 strcpy (file->name, info[i].name);
708 file->offset = LittleLong(info[i].filepos);
709 size = LittleLong (info[i].filelen);
710 file->packsize = size;
711 file->realsize = size;
712 file->flags = FILE_FLAG_TRUEOFFS;
717 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
726 Sets fs_gamedir, adds the directory to the head of the path,
727 then loads and adds pak1.pak pak2.pak ...
730 void FS_AddGameDirectory (char *dir)
732 stringlist_t *list, *current;
733 searchpath_t *search;
735 char pakfile[MAX_OSPATH];
737 strcpy (fs_gamedir, dir);
739 // add the directory to the search path
740 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
741 strcpy (search->filename, dir);
742 search->next = fs_searchpaths;
743 fs_searchpaths = search;
745 list = listdirectory(dir);
747 // add any PAK package in the directory
748 for (current = list;current;current = current->next)
750 if (matchpattern(current->text, "*.pak", true))
752 sprintf (pakfile, "%s/%s", dir, current->text);
753 pak = FS_LoadPackPAK (pakfile);
756 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
758 search->next = fs_searchpaths;
759 fs_searchpaths = search;
762 Con_Printf("unable to load pak \"%s\"\n", pakfile);
766 // add any PK3 package in the director
767 for (current = list;current;current = current->next)
769 if (matchpattern(current->text, "*.pk3", true))
771 sprintf (pakfile, "%s/%s", dir, current->text);
772 pak = FS_LoadPackPK3 (pakfile);
775 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
777 search->next = fs_searchpaths;
778 fs_searchpaths = search;
781 Con_Printf("unable to load pak \"%s\"\n", pakfile);
793 char *FS_FileExtension (const char *in)
795 static char exten[8];
796 const char *slash, *backslash, *colon, *dot, *separator;
799 slash = strrchr(in, '/');
800 backslash = strrchr(in, '\\');
801 colon = strrchr(in, ':');
802 dot = strrchr(in, '.');
804 if (separator < backslash)
805 separator = backslash;
806 if (separator < colon)
811 for (i = 0;i < 7 && dot[i];i++)
826 searchpath_t *search;
828 fs_mempool = Mem_AllocPool("file management");
829 pak_mempool = Mem_AllocPool("paks");
831 Cmd_AddCommand ("path", FS_Path_f);
833 strcpy(fs_basedir, ".");
838 // Overrides the system supplied base directory (under GAMENAME)
839 i = COM_CheckParm ("-basedir");
840 if (i && i < com_argc-1)
841 strcpy (fs_basedir, com_argv[i+1]);
843 i = strlen (fs_basedir);
844 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
847 // start up with GAMENAME by default (id1)
848 strcpy(com_modname, GAMENAME);
849 FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir));
853 strcpy(com_modname, gamedirname);
854 FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname));
858 // Adds basedir/gamedir as an override game
859 i = COM_CheckParm ("-game");
860 if (i && i < com_argc-1)
863 strcpy(com_modname, com_argv[i+1]);
864 FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i+1]));
867 // -path <dir or packfile> [<dir or packfile>] ...
868 // Fully specifies the exact search path, overriding the generated one
869 i = COM_CheckParm ("-path");
873 fs_searchpaths = NULL;
874 while (++i < com_argc)
876 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
879 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
880 if (!strcasecmp (FS_FileExtension(com_argv[i]), "pak"))
882 search->pack = FS_LoadPackPAK (com_argv[i]);
884 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
886 else if (!strcasecmp (FS_FileExtension (com_argv[i]), "pk3"))
888 search->pack = FS_LoadPackPK3 (com_argv[i]);
890 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
893 strcpy (search->filename, com_argv[i]);
894 search->next = fs_searchpaths;
895 fs_searchpaths = search;
905 Internal function used to create a qfile_t and open the relevant file on disk
908 static qfile_t* FS_SysOpen (const char* filepath, const char* mode)
912 file = Mem_Alloc (fs_mempool, sizeof (*file));
913 memset (file, 0, sizeof (*file));
915 file->stream = fopen (filepath, mode);
931 qfile_t *FS_OpenRead (const char *path, int offs, int len)
935 file = FS_SysOpen (path, "rb");
938 Sys_Error ("Couldn't open %s", path);
943 if (offs < 0 || len < 0)
945 // We set fs_filesize here for normal files
946 fseek (file->stream, 0, SEEK_END);
947 fs_filesize = ftell (file->stream);
948 fseek (file->stream, 0, SEEK_SET);
953 fseek (file->stream, offs, SEEK_SET);
955 file->flags |= FS_FLAG_PACKED;
968 If the requested file is inside a packfile, a new qfile_t* will be opened
974 qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
976 searchpath_t *search;
977 char netpath[MAX_OSPATH];
979 int i, filenamelen, matched;
981 filenamelen = strlen (filename);
983 // search through the path, one element at a time
984 search = fs_searchpaths;
986 for ( ; search ; search = search->next)
988 // is the element a pak file?
991 // look through all the pak file elements
993 for (i=0 ; i<pak->numfiles ; i++)
996 matched = !strcasecmp (pak->files[i].name, filename);
998 matched = !strcmp (pak->files[i].name, filename);
999 if (matched) // found it?
1004 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1006 // If we don't have the true offset, get it now
1007 if (! (pak->files[i].flags & FILE_FLAG_TRUEOFFS))
1008 PK3_GetTrueFileOffset (&pak->files[i], pak);
1010 // No Zlib DLL = no compressed files
1011 if (!zlib_dll && (pak->files[i].flags & FILE_FLAG_DEFLATED))
1013 Con_Printf ("WARNING: can't open the compressed file %s\n"
1014 "You need the Zlib DLL to use compressed files\n", filename);
1019 // open a new file in the pakfile
1020 file = FS_OpenRead (pak->filename, pak->files[i].offset, pak->files[i].packsize);
1021 fs_filesize = pak->files[i].realsize;
1023 if (pak->files[i].flags & FILE_FLAG_DEFLATED)
1027 file->flags |= FS_FLAG_DEFLATED;
1029 // We need some more variables
1030 ztk = Mem_Alloc (fs_mempool, sizeof (*file->z));
1032 ztk->real_length = pak->files[i].realsize;
1034 // Initialize zlib stream
1035 ztk->zstream.next_in = ztk->input;
1036 ztk->zstream.avail_in = 0;
1038 /* From Zlib's "unzip.c":
1040 * windowBits is passed < 0 to tell that there is no zlib header.
1041 * Note that in this case inflate *requires* an extra "dummy" byte
1042 * after the compressed stream in order to complete decompression and
1043 * return Z_STREAM_END.
1044 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1045 * size of both compressed and uncompressed data
1047 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1048 Sys_Error ("inflate init error (file: %s)", filename);
1050 ztk->zstream.next_out = ztk->output;
1051 ztk->zstream.avail_out = sizeof (ztk->output);
1062 sprintf (netpath, "%s/%s",search->filename, filename);
1064 if (!FS_SysFileExists (netpath))
1068 Sys_Printf ("FindFile: %s\n",netpath);
1069 return FS_OpenRead (netpath, -1, -1);
1074 Sys_Printf ("FindFile: can't find %s\n", filename);
1082 =============================================================================
1084 MAIN PUBLIC FUNCTIONS
1086 =============================================================================
1090 ====================
1093 Open a file. The syntax is the same as fopen
1094 ====================
1096 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet)
1098 // If the file is opened in "write" or "append" mode
1099 if (strchr (mode, 'w') || strchr (mode, 'a'))
1101 char real_path [MAX_OSPATH];
1103 // Open the file on disk directly
1104 snprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1106 // Create directories up to the file
1107 FS_CreatePath (real_path);
1109 return FS_SysOpen (real_path, mode);
1112 // Else, we look at the various search paths
1113 return FS_FOpenFile (filepath, quiet);
1118 ====================
1122 ====================
1124 int FS_Close (qfile_t* file)
1126 if (fclose (file->stream))
1131 qz_inflateEnd (&file->z->zstream);
1141 ====================
1144 Write "datasize" bytes into a file
1145 ====================
1147 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1149 return fwrite (data, 1, datasize, file->stream);
1154 ====================
1157 Read up to "buffersize" bytes from a file
1158 ====================
1160 size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1165 // Quick path for unpacked files
1166 if (! (file->flags & FS_FLAG_PACKED))
1167 return fread (buffer, 1, buffersize, file->stream);
1169 // If the file isn't compressed
1170 if (! (file->flags & FS_FLAG_DEFLATED))
1172 // We must take care to not read after the end of the file
1173 count = file->length - file->position;
1174 if (buffersize > count)
1177 nb = fread (buffer, 1, buffersize, file->stream);
1179 // Update the position index if the file is packed
1180 file->position += nb;
1185 // If the file is compressed, it's more complicated...
1188 // First, we copy as many bytes as we can from "output"
1189 if (ztk->out_ind < ztk->out_max)
1191 count = ztk->out_max - ztk->out_ind;
1193 nb = (buffersize > count) ? count : buffersize;
1194 memcpy (buffer, &ztk->output[ztk->out_ind], nb);
1200 // We cycle through a few operations until we have inflated enough data
1201 while (nb < buffersize)
1203 // NOTE: at this point, "output" should always be empty
1205 // If "input" is also empty, we need to fill it
1206 if (ztk->in_ind == ztk->in_max)
1208 size_t remain = file->length - file->position;
1210 // If we are at the end of the file
1214 count = (remain > sizeof (ztk->input)) ? sizeof (ztk->input) : remain;
1215 fread (ztk->input, 1, count, file->stream);
1217 // Update indexes and counters
1219 ztk->in_max = count;
1220 file->position += count;
1223 // Now that we are sure we have compressed data available, we need to determine
1224 // if it's better to inflate it in "output" or directly in "buffer" (we are in this
1225 // case if we still need more bytes than "output" can contain)
1227 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1228 ztk->zstream.avail_in = ztk->in_max - ztk->in_ind;
1230 // If output will be able to contain at least 1 more byte than the data we need
1231 if (buffersize - nb < sizeof (ztk->output))
1235 // Inflate the data in "output"
1236 ztk->zstream.next_out = ztk->output;
1237 ztk->zstream.avail_out = sizeof (ztk->output);
1238 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1239 if (error != Z_OK && error != Z_STREAM_END)
1240 Sys_Error ("Can't inflate file");
1241 ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1242 ztk->out_max = sizeof (ztk->output) - ztk->zstream.avail_out;
1244 ztk->out_position += ztk->out_max;
1246 // Copy the requested data in "buffer" (as much as we can)
1247 count = (buffersize - nb > ztk->out_max) ? ztk->out_max : buffersize - nb;
1248 memcpy (&((qbyte*)buffer)[nb], ztk->output, count);
1249 ztk->out_ind = count;
1252 // Else, we inflate directly in "buffer"
1257 // Inflate the data in "buffer"
1258 ztk->zstream.next_out = &((qbyte*)buffer)[nb];
1259 ztk->zstream.avail_out = buffersize - nb;
1260 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1261 if (error != Z_OK && error != Z_STREAM_END)
1262 Sys_Error ("Can't inflate file");
1263 ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1265 // Invalidate the output data (for FS_Seek)
1269 // How much data did it inflate?
1270 count = buffersize - nb - ztk->zstream.avail_out;
1271 ztk->out_position += count;
1282 ====================
1285 Flush the file output stream
1286 ====================
1288 int FS_Flush (qfile_t* file)
1290 return fflush (file->stream);
1295 ====================
1298 Print a string into a file
1299 ====================
1301 int FS_Printf (qfile_t* file, const char* format, ...)
1306 va_start (args, format);
1307 result = vfprintf (file->stream, format, args);
1315 ====================
1318 Get the next character of a file
1319 ====================
1321 int FS_Getc (qfile_t* file)
1325 if (FS_Read (file, &c, 1) != 1)
1333 ====================
1336 Move the position index in a file
1337 ====================
1339 int FS_Seek (qfile_t* file, long offset, int whence)
1341 // Quick path for unpacked files
1342 if (! (file->flags & FS_FLAG_PACKED))
1343 return fseek (file->stream, offset, whence);
1345 // Seeking in compressed files is more a hack than anything else,
1346 // but we need to support it, so here it is.
1347 if (file->flags & FS_FLAG_DEFLATED)
1349 ztoolkit_t *ztk = file->z;
1351 qbyte buffer [sizeof (ztk->output)]; // it's big to force inflating into buffer directly
1353 crt_offset = ztk->out_position - ztk->out_max + ztk->out_ind;
1358 offset += crt_offset;
1365 offset += ztk->real_length;
1372 // If we need to go back in the file
1373 if (offset <= (long) crt_offset)
1375 // If we still have the data we need in the output buffer
1376 if (crt_offset - offset <= ztk->out_ind)
1378 ztk->out_ind -= crt_offset - offset;
1382 // Else, we restart from the beginning of the file
1388 ztk->out_position = 0;
1390 fseek (file->stream, file->offset, SEEK_SET);
1392 // Reset the Zlib stream
1393 ztk->zstream.next_in = ztk->input;
1394 ztk->zstream.avail_in = 0;
1395 qz_inflateReset (&ztk->zstream);
1398 // Skip all data until we reach the requested offset
1399 while ((long) crt_offset < offset)
1401 size_t diff = offset - crt_offset;
1404 count = (diff > sizeof (buffer)) ? sizeof (buffer) : diff;
1405 len = FS_Read (file, buffer, count);
1414 // Packed files receive a special treatment too, because
1415 // we need to make sure it doesn't go outside of the file
1419 offset += file->position;
1426 offset += file->length;
1432 if (offset < 0 || offset > (long) file->length)
1435 if (fseek (file->stream, file->offset + offset, SEEK_SET) == -1)
1437 file->position = offset;
1443 ====================
1446 Give the current position in a file
1447 ====================
1449 long FS_Tell (qfile_t* file)
1451 if (file->flags & FS_FLAG_PACKED)
1453 if (file->flags & FS_FLAG_DEFLATED)
1455 ztoolkit_t *ztk = file->z;
1456 return ztk->out_position - ztk->out_max + ztk->out_ind;
1459 return file->position;
1462 return ftell (file->stream);
1467 ====================
1470 Extract a line from a file
1471 ====================
1473 char* FS_Gets (qfile_t* file, char* buffer, int buffersize)
1477 // Quick path for unpacked files
1478 if (! (file->flags & FS_FLAG_PACKED))
1479 return fgets (buffer, buffersize, file->stream);
1481 for (ind = 0; ind < (size_t) buffersize - 1; ind++)
1483 int c = FS_Getc (file);
1498 buffer[ind + 1] = '\0';
1507 buffer[buffersize - 1] = '\0';
1516 Dynamic length version of fgets. DO NOT free the buffer.
1519 char *FS_Getline (qfile_t *file)
1521 static int size = 256;
1522 static char *buf = 0;
1527 buf = Mem_Alloc (fs_mempool, size);
1529 if (!FS_Gets (file, buf, size))
1533 while (buf[len - 1] != '\n' && buf[len - 1] != '\r')
1535 t = Mem_Alloc (fs_mempool, size + 256);
1536 memcpy(t, buf, size);
1540 if (!FS_Gets (file, buf + len, size - len))
1544 while ((len = strlen(buf)) && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
1551 ====================
1554 Extract a line from a file
1555 ====================
1557 int FS_Eof (qfile_t* file)
1559 if (file->flags & FS_FLAG_PACKED)
1561 if (file->flags & FS_FLAG_DEFLATED)
1563 ztoolkit_t *ztk = file->z;
1564 return (ztk->out_position - ztk->out_max + ztk->out_ind == ztk->real_length);
1567 return (file->position == file->length);
1570 return feof (file->stream);
1578 Filename are relative to the quake directory.
1579 Always appends a 0 byte.
1582 qbyte *FS_LoadFile (const char *path, qboolean quiet)
1587 // look for it in the filesystem or pack files
1588 h = FS_Open (path, "rb", quiet);
1592 buf = Mem_Alloc(tempmempool, fs_filesize+1);
1594 Sys_Error ("FS_LoadFile: not enough available memory for %s (size %i)", path, fs_filesize);
1596 ((qbyte *)buf)[fs_filesize] = 0;
1598 FS_Read (h, buf, fs_filesize);
1609 The filename will be prefixed by the current game directory
1612 qboolean FS_WriteFile (const char *filename, void *data, int len)
1615 char name[MAX_OSPATH];
1617 sprintf (name, "%s/%s", fs_gamedir, filename);
1619 // Create directories up to the file
1620 FS_CreatePath (name);
1622 handle = fopen (name, "wb");
1625 Con_Printf ("FS_WriteFile: failed on %s\n", name);
1629 Con_DPrintf ("FS_WriteFile: %s\n", name);
1630 fwrite (data, 1, len, handle);
1637 =============================================================================
1639 OTHERS PUBLIC FUNCTIONS
1641 =============================================================================
1649 void FS_StripExtension (const char *in, char *out)
1656 else if (*in == '/' || *in == '\\' || *in == ':')
1672 void FS_DefaultExtension (char *path, const char *extension)
1676 // if path doesn't have a .EXT, append extension
1677 // (extension should include the .)
1678 src = path + strlen(path) - 1;
1680 while (*src != '/' && src != path)
1683 return; // it has an extension
1687 strcat (path, extension);
1691 qboolean FS_FileExists (const char *filename)
1693 searchpath_t *search;
1694 char netpath[MAX_OSPATH];
1698 for (search = fs_searchpaths;search;search = search->next)
1703 for (i = 0;i < pak->numfiles;i++)
1704 if (!strcmp (pak->files[i].name, filename))
1709 sprintf (netpath, "%s/%s",search->filename, filename);
1710 if (FS_SysFileExists (netpath))
1719 qboolean FS_SysFileExists (const char *path)
1724 f = fopen (path, "rb");
1735 if (stat (path,&buf) == -1)
1742 void FS_mkdir (const char *path)