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>
50 All of Quake's data access is through a hierchal file system, but the contents
51 of the file system can be transparently merged from several sources.
53 The "base directory" is the path to the directory holding the quake.exe and
54 all game directories. The sys_* files pass this to host_init in
55 quakeparms_t->basedir. This can be overridden with the "-basedir" command
56 line parm to allow code debugging in a different directory. The base
57 directory is only used during filesystem initialization.
59 The "game directory" is the first tree on the search path and directory that
60 all generated files (savegames, screenshots, demos, config files) will be
61 saved to. This can be overridden with the "-game" command line parameter.
62 The game directory can never be changed while quake is executing. This is a
63 precacution against having a malicious server instruct clients to write files
64 over areas they shouldn't.
70 =============================================================================
74 =============================================================================
77 // Magic numbers of a ZIP file (big-endian format)
78 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
79 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
80 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
82 // Other constants for ZIP files
83 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
84 #define ZIP_END_CDIR_SIZE 22
85 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
86 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
90 =============================================================================
94 =============================================================================
97 // Our own file structure on top of FILE
101 FS_FLAG_PACKED = (1 << 0) // inside a package (PAK or PK3)
108 size_t length; // file size (PACKED only)
109 size_t offset; // offset into a package (PACKED only)
110 size_t position; // current position in the file (PACKED only)
114 // ------ PK3 files on disk ------ //
116 // You can get the complete ZIP format description from PKWARE website
120 unsigned int signature;
121 unsigned short disknum;
122 unsigned short cdir_disknum; // number of the disk with the start of the central directory
123 unsigned short localentries; // number of entries in the central directory on this disk
124 unsigned short nbentries; // total number of entries in the central directory on this disk
125 unsigned int cdir_size; // size of the central directory
126 unsigned int cdir_offset; // with respect to the starting disk number
127 unsigned short comment_size;
128 } pk3_endOfCentralDir_t;
131 // ------ PAK files on disk ------ //
135 int filepos, filelen;
146 // Packages in memory
150 FILE_FLAG_TRUEOFFS = (1 << 0), // the offset in packfile_t is the true contents offset
151 FILE_FLAG_DEFLATED = (1 << 1) // file compressed using the deflate algorithm
156 char name [MAX_QPATH];
159 size_t packsize; // size in the package
160 size_t realsize; // real file size (uncompressed)
163 typedef struct pack_s
165 char filename [MAX_OSPATH];
174 // Search paths for files (including packages)
175 typedef struct searchpath_s
177 // only one of filename / pack will be used
178 char filename[MAX_OSPATH];
180 struct searchpath_s *next;
185 =============================================================================
189 =============================================================================
192 mempool_t *fs_mempool;
193 mempool_t *pak_mempool;
197 pack_t *packlist = NULL;
199 searchpath_t *fs_searchpaths;
201 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
202 #define MAX_FILES_IN_PACK 65536
204 char fs_gamedir[MAX_OSPATH];
205 char fs_basedir[MAX_OSPATH];
207 qboolean fs_modified; // set true if using non-id files
211 =============================================================================
213 PRIVATE FUNCTIONS - PK3 HANDLING
215 =============================================================================
220 PK3_GetEndOfCentralDir
222 Extract the end of the central directory from a PK3 package
225 qboolean PK3_GetEndOfCentralDir (const char *packfile, FILE *packhandle, pk3_endOfCentralDir_t *eocd)
227 long filesize, maxsize;
231 // Get the package size
232 fseek (packhandle, 0, SEEK_END);
233 filesize = ftell (packhandle);
234 if (filesize < ZIP_END_CDIR_SIZE)
237 // Load the end of the file in memory
238 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
241 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
242 buffer = Mem_Alloc (tempmempool, maxsize);
243 fseek (packhandle, filesize - maxsize, SEEK_SET);
244 if (fread (buffer, 1, maxsize, packhandle) != maxsize)
250 // Look for the end of central dir signature around the end of the file
251 maxsize -= ZIP_END_CDIR_SIZE;
252 ptr = &buffer[maxsize];
254 while (BuffBigLong (ptr) != ZIP_END_HEADER)
266 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
267 eocd->signature = LittleLong (eocd->signature);
268 eocd->disknum = LittleShort (eocd->disknum);
269 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
270 eocd->localentries = LittleShort (eocd->localentries);
271 eocd->nbentries = LittleShort (eocd->nbentries);
272 eocd->cdir_size = LittleLong (eocd->cdir_size);
273 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
274 eocd->comment_size = LittleShort (eocd->comment_size);
286 Extract the file list from a PK3 file
289 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
291 qbyte *central_dir, *ptr;
295 // Load the central directory in memory
296 central_dir = Mem_Alloc (tempmempool, eocd->cdir_size);
297 fseek (pack->handle, eocd->cdir_offset, SEEK_SET);
298 fread (central_dir, 1, eocd->cdir_size, pack->handle);
300 // Extract the files properties
301 // The parsing is done "by hand" because some fields have variable sizes and
302 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
303 remaining = eocd->cdir_size;
306 for (ind = 0; ind < eocd->nbentries; ind++)
308 size_t namesize, count;
311 // Checking the remaining size
312 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
314 Mem_Free (central_dir);
317 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
320 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
322 Mem_Free (central_dir);
326 namesize = BuffLittleShort (&ptr[28]); // filename length
328 // Check encryption, compression, and attributes
329 // 1st uint8 : general purpose bit flag
330 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
331 // 2nd uint8 : external file attributes
332 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
333 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
335 // Still enough bytes for the name?
336 if (remaining < namesize || namesize >= sizeof (*pack->files))
338 Mem_Free (central_dir);
342 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
343 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
346 file = &pack->files[pack->numfiles];
347 memcpy (file->name, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
348 file->name[namesize] = '\0';
350 // Compression, sizes and offset
351 if (BuffLittleShort (&ptr[10]))
352 file->flags = FILE_FLAG_DEFLATED;
353 file->packsize = BuffLittleLong (&ptr[20]);
354 file->realsize = BuffLittleLong (&ptr[24]);
355 file->offset = BuffLittleLong (&ptr[42]);
361 // Skip the name, additionnal field, and comment
362 // 1er uint16 : extra field length
363 // 2eme uint16 : file comment length
364 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
365 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
369 Mem_Free (central_dir);
370 return pack->numfiles;
378 Create a package entry associated with a PK3 file
381 pack_t *FS_LoadPackPK3 (const char *packfile)
384 pk3_endOfCentralDir_t eocd;
388 packhandle = fopen (packfile, "rb");
392 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
393 Sys_Error ("%s is not a PK3 file", packfile);
395 // Multi-volume ZIP archives are NOT allowed
396 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
397 Sys_Error ("%s is a multi-volume ZIP archive", packfile);
399 if (eocd.nbentries > MAX_FILES_IN_PACK)
400 Sys_Error ("%s contains too many files (%hu)", packfile, eocd.nbentries);
402 // Create a package structure in memory
403 pack = Mem_Alloc (pak_mempool, sizeof (pack_t));
404 strcpy (pack->filename, packfile);
405 pack->handle = packhandle;
406 pack->numfiles = eocd.nbentries;
407 pack->mempool = Mem_AllocPool (packfile);
408 pack->files = Mem_Alloc (pack->mempool, eocd.nbentries * sizeof(packfile_t));
409 pack->next = packlist;
412 real_nb_files = PK3_BuildFileList (pack, &eocd);
413 if (real_nb_files <= 0)
414 Sys_Error ("%s is not a valid PK3 file", packfile);
416 Con_Printf ("Added packfile %s (%i files)\n", packfile, real_nb_files);
423 PK3_GetTrueFileOffset
425 Find where the true file data offset is
428 void PK3_GetTrueFileOffset (packfile_t *file, pack_t *pack)
430 qbyte buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
434 if (file->flags & FILE_FLAG_TRUEOFFS)
437 // Load the local file description
438 fseek (pack->handle, file->offset, SEEK_SET);
439 count = fread (buffer, 1, ZIP_LOCAL_CHUNK_BASE_SIZE, pack->handle);
440 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
441 Sys_Error ("Can't retrieve file %s in package %s", file->name, pack->filename);
443 // Skip name and extra field
444 file->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
446 file->flags |= FILE_FLAG_TRUEOFFS;
451 =============================================================================
453 OTHER PRIVATE FUNCTIONS
455 =============================================================================
463 Only used for FS_WriteFile.
466 void FS_CreatePath (char *path)
470 for (ofs = path+1 ; *ofs ; ofs++)
472 if (*ofs == '/' || *ofs == '\\')
474 // create the directory
490 void FS_Path_f (void)
494 Con_Printf ("Current search path:\n");
495 for (s=fs_searchpaths ; s ; s=s->next)
499 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
502 Con_Printf ("%s\n", s->filename);
511 Takes an explicit (not game tree related) path to a pak file.
513 Loads the header and directory, adding the files at the beginning
514 of the list so they override previous pack files.
517 pack_t *FS_LoadPackPAK (const char *packfile)
519 dpackheader_t header;
523 dpackfile_t *info; // temporary alloc, allowing huge pack directories
525 packhandle = fopen (packfile, "rb");
529 fread ((void *)&header, 1, sizeof(header), packhandle);
530 if (memcmp(header.id, "PACK", 4))
531 Sys_Error ("%s is not a packfile", packfile);
532 header.dirofs = LittleLong (header.dirofs);
533 header.dirlen = LittleLong (header.dirlen);
535 if (header.dirlen % sizeof(dpackfile_t))
536 Sys_Error ("%s has an invalid directory size", packfile);
538 numpackfiles = header.dirlen / sizeof(dpackfile_t);
540 if (numpackfiles > MAX_FILES_IN_PACK)
541 Sys_Error ("%s has %i files", packfile, numpackfiles);
543 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
544 strcpy (pack->filename, packfile);
545 pack->handle = packhandle;
546 pack->numfiles = numpackfiles;
547 pack->mempool = Mem_AllocPool(packfile);
548 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
549 pack->next = packlist;
552 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
553 fseek (packhandle, header.dirofs, SEEK_SET);
554 fread ((void *)info, 1, header.dirlen, packhandle);
556 // parse the directory
557 for (i = 0;i < numpackfiles;i++)
560 packfile_t *file = &pack->files[i];
562 strcpy (file->name, info[i].name);
563 file->offset = LittleLong(info[i].filepos);
564 size = LittleLong (info[i].filelen);
565 file->packsize = size;
566 file->realsize = size;
567 file->flags = FILE_FLAG_TRUEOFFS;
572 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
581 Sets fs_gamedir, adds the directory to the head of the path,
582 then loads and adds pak1.pak pak2.pak ...
585 void FS_AddGameDirectory (char *dir)
587 stringlist_t *list, *current;
588 searchpath_t *search;
590 char pakfile[MAX_OSPATH];
592 strcpy (fs_gamedir, dir);
594 // add the directory to the search path
595 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
596 strcpy (search->filename, dir);
597 search->next = fs_searchpaths;
598 fs_searchpaths = search;
600 list = listdirectory(dir);
602 // add any PAK package in the directory
603 for (current = list;current;current = current->next)
605 if (matchpattern(current->text, "*.pak", true))
607 sprintf (pakfile, "%s/%s", dir, current->text);
608 pak = FS_LoadPackPAK (pakfile);
611 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
613 search->next = fs_searchpaths;
614 fs_searchpaths = search;
617 Con_Printf("unable to load pak \"%s\"\n", pakfile);
621 // add any PK3 package in the director
622 for (current = list;current;current = current->next)
624 if (matchpattern(current->text, "*.pk3", true))
626 sprintf (pakfile, "%s/%s", dir, current->text);
627 pak = FS_LoadPackPK3 (pakfile);
630 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
632 search->next = fs_searchpaths;
633 fs_searchpaths = search;
636 Con_Printf("unable to load pak \"%s\"\n", pakfile);
648 char *FS_FileExtension (const char *in)
650 static char exten[8];
653 while (*in && *in != '.')
658 for (i=0 ; i<7 && *in ; i++,in++)
673 searchpath_t *search;
675 fs_mempool = Mem_AllocPool("file management");
676 pak_mempool = Mem_AllocPool("paks");
678 Cmd_AddCommand ("path", FS_Path_f);
680 strcpy(fs_basedir, ".");
683 // Overrides the system supplied base directory (under GAMENAME)
684 i = COM_CheckParm ("-basedir");
685 if (i && i < com_argc-1)
686 strcpy (fs_basedir, com_argv[i+1]);
688 i = strlen (fs_basedir);
689 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
692 // start up with GAMENAME by default (id1)
693 strcpy(com_modname, GAMENAME);
694 FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir));
698 strcpy(com_modname, gamedirname);
699 FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname));
703 // Adds basedir/gamedir as an override game
704 i = COM_CheckParm ("-game");
705 if (i && i < com_argc-1)
708 strcpy(com_modname, com_argv[i+1]);
709 FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i+1]));
712 // -path <dir or packfile> [<dir or packfile>] ...
713 // Fully specifies the exact search path, overriding the generated one
714 i = COM_CheckParm ("-path");
718 fs_searchpaths = NULL;
719 while (++i < com_argc)
721 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
724 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
725 if (!strcasecmp (FS_FileExtension(com_argv[i]), "pak"))
727 search->pack = FS_LoadPackPAK (com_argv[i]);
729 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
731 else if (!strcasecmp (FS_FileExtension (com_argv[i]), "pk3"))
733 search->pack = FS_LoadPackPK3 (com_argv[i]);
735 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
738 strcpy (search->filename, com_argv[i]);
739 search->next = fs_searchpaths;
740 fs_searchpaths = search;
747 =============================================================================
749 MAIN PUBLIC FUNCTIONS
751 =============================================================================
758 Internal function used to create a qfile_t and open the relevant file on disk
761 static qfile_t* FS_SysOpen (const char* filepath, const char* mode)
765 file = Mem_Alloc (fs_mempool, sizeof (*file));
766 memset (file, 0, sizeof (*file));
768 file->stream = fopen (filepath, mode);
784 qfile_t *FS_OpenRead (const char *path, int offs, int len)
788 file = FS_SysOpen (path, "rb");
791 Sys_Error ("Couldn't open %s", path);
796 if (offs < 0 || len < 0)
798 FS_Seek (file, 0, SEEK_END);
799 len = FS_Tell (file);
800 FS_Seek (file, 0, SEEK_SET);
805 FS_Seek (file, offs, SEEK_SET);
807 file->flags |= FS_FLAG_PACKED;
822 If the requested file is inside a packfile, a new qfile_t* will be opened
828 qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
830 searchpath_t *search;
831 char netpath[MAX_OSPATH];
835 filenamelen = strlen (filename);
837 // search through the path, one element at a time
838 search = fs_searchpaths;
840 for ( ; search ; search = search->next)
842 // is the element a pak file?
845 // look through all the pak file elements
847 for (i=0 ; i<pak->numfiles ; i++)
848 if (!strcmp (pak->files[i].name, filename)) // found it?
850 // TODO: compressed files are NOT supported yet
851 if (pak->files[i].flags & FILE_FLAG_DEFLATED)
853 Con_Printf ("WARNING: %s is a compressed file and so cannot be opened\n");
859 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
861 // If we don't have the true offset, get it now
862 if (! (pak->files[i].flags & FILE_FLAG_TRUEOFFS))
863 PK3_GetTrueFileOffset (&pak->files[i], pak);
865 // open a new file in the pakfile
866 return FS_OpenRead (pak->filename, pak->files[i].offset, pak->files[i].packsize);
871 sprintf (netpath, "%s/%s",search->filename, filename);
873 if (!FS_SysFileExists (netpath))
877 Sys_Printf ("FindFile: %s\n",netpath);
878 return FS_OpenRead (netpath, -1, -1);
883 Sys_Printf ("FindFile: can't find %s\n", filename);
894 Open a file. The syntax is the same as fopen
897 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet)
899 // If the file is opened in "write" or "append" mode
900 if (strchr (mode, 'w') || strchr (mode, 'a'))
902 char real_path [MAX_OSPATH];
904 // Open the file on disk directly
905 snprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
907 // Create directories up to the file
908 FS_CreatePath (real_path);
910 return FS_SysOpen (real_path, mode);
913 // Else, we look at the various search paths
914 return FS_FOpenFile (filepath, quiet);
925 int FS_Close (qfile_t* file)
927 if (fclose (file->stream))
939 Write "datasize" bytes into a file
942 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
944 return fwrite (data, 1, datasize, file->stream);
952 Read up to "buffersize" bytes from a file
955 size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
959 // If the file belongs to a package, we must take care
960 // to not read after the end of the file
961 if (file->flags & FS_FLAG_PACKED)
963 size_t remain = file->length - file->position;
964 if (buffersize > remain)
968 nb = fread (buffer, 1, buffersize, file->stream);
970 // Update the position index if the file is packed
971 if ((file->flags & FS_FLAG_PACKED) && nb > 0)
972 file->position += nb;
982 Flush the file output stream
985 int FS_Flush (qfile_t* file)
987 return fflush (file->stream);
995 Print a string into a file
998 int FS_Printf (qfile_t* file, const char* format, ...)
1003 va_start (args, format);
1004 result = vfprintf (file->stream, format, args);
1012 ====================
1015 Get the next character of a file
1016 ====================
1018 int FS_Getc (qfile_t* file)
1022 // If the file belongs to a package, we must take care
1023 // to not read after the end of the file
1024 if (file->flags & FS_FLAG_PACKED)
1026 if (file->position >= file->length)
1030 c = fgetc (file->stream);
1032 // Update the position index if the file is packed
1033 if ((file->flags & FS_FLAG_PACKED) && c != EOF)
1041 ====================
1044 Move the position index in a file
1045 ====================
1047 int FS_Seek (qfile_t* file, long offset, int whence)
1049 // Packed files receive a special treatment
1050 if (file->flags & FS_FLAG_PACKED)
1055 offset += file->position;
1056 // It continues on the next case (no break)
1059 if (offset < 0 || offset > file->length)
1061 if (fseek (file->stream, file->offset + offset, SEEK_SET) == -1)
1063 file->position = offset;
1067 if (offset > 0 || -offset > file->length)
1069 if (fseek (file->stream, file->offset + file->length + offset, SEEK_SET) == -1)
1071 file->position = file->length + offset;
1079 return fseek (file->stream, offset, whence);
1084 ====================
1087 Give the current position in a file
1088 ====================
1090 long FS_Tell (qfile_t* file)
1092 if (file->flags & FS_FLAG_PACKED)
1093 return file->position;
1095 return ftell (file->stream);
1100 ====================
1103 Extract a line from a file
1104 ====================
1106 char* FS_Gets (qfile_t* file, char* buffer, int buffersize)
1108 if (!fgets (buffer, buffersize, file->stream))
1111 // Check that we didn't read after the end of a packed file, and update the position
1112 if (file->flags & FS_FLAG_PACKED)
1114 size_t len = strlen (buffer);
1115 size_t max = file->length - file->position;
1120 file->position = file->length;
1123 file->position += len;
1134 Dynamic length version of fgets. DO NOT free the buffer.
1137 char *FS_Getline (qfile_t *file)
1139 static int size = 256;
1140 static char *buf = 0;
1145 buf = Mem_Alloc (fs_mempool, size);
1147 if (!FS_Gets (file, buf, size))
1151 while (buf[len - 1] != '\n' && buf[len - 1] != '\r')
1153 t = Mem_Alloc (fs_mempool, size + 256);
1154 memcpy(t, buf, size);
1158 if (!FS_Gets (file, buf + len, size - len))
1162 while ((len = strlen(buf)) && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
1169 ====================
1172 Extract a line from a file
1173 ====================
1175 int FS_Eof (qfile_t* file)
1177 if (file->flags & FS_FLAG_PACKED)
1178 return (file->position == file->length);
1180 return feof (file->stream);
1188 Filename are relative to the quake directory.
1189 Always appends a 0 byte.
1192 qbyte *FS_LoadFile (const char *path, qboolean quiet)
1197 // look for it in the filesystem or pack files
1198 h = FS_Open (path, "rb", quiet);
1202 buf = Mem_Alloc(tempmempool, fs_filesize+1);
1204 Sys_Error ("FS_LoadFile: not enough available memory for %s (size %i)", path, fs_filesize);
1206 ((qbyte *)buf)[fs_filesize] = 0;
1208 FS_Read (h, buf, fs_filesize);
1219 The filename will be prefixed by the current game directory
1222 qboolean FS_WriteFile (const char *filename, void *data, int len)
1225 char name[MAX_OSPATH];
1227 sprintf (name, "%s/%s", fs_gamedir, filename);
1229 // Create directories up to the file
1230 FS_CreatePath (name);
1232 handle = fopen (name, "wb");
1235 Con_Printf ("FS_WriteFile: failed on %s\n", name);
1239 Con_DPrintf ("FS_WriteFile: %s\n", name);
1240 fwrite (data, 1, len, handle);
1247 =============================================================================
1249 OTHERS PUBLIC FUNCTIONS
1251 =============================================================================
1259 void FS_StripExtension (const char *in, char *out)
1266 else if (*in == '/' || *in == '\\' || *in == ':')
1282 void FS_DefaultExtension (char *path, const char *extension)
1286 // if path doesn't have a .EXT, append extension
1287 // (extension should include the .)
1288 src = path + strlen(path) - 1;
1290 while (*src != '/' && src != path)
1293 return; // it has an extension
1297 strcat (path, extension);
1301 qboolean FS_FileExists (const char *filename)
1303 searchpath_t *search;
1304 char netpath[MAX_OSPATH];
1308 for (search = fs_searchpaths;search;search = search->next)
1313 for (i = 0;i < pak->numfiles;i++)
1314 if (!strcmp (pak->files[i].name, filename))
1319 sprintf (netpath, "%s/%s",search->filename, filename);
1320 if (FS_SysFileExists (netpath))
1329 qboolean FS_SysFileExists (const char *path)
1334 f = fopen (path, "rb");
1345 if (stat (path,&buf) == -1)
1352 void FS_mkdir (const char *path)