]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/common/vfs.c
handle forbidden dir exclusions properly now
[xonotic/netradiant.git] / tools / quake3 / common / vfs.c
1 /*
2 Copyright (c) 2001, Loki software, inc.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modification, 
6 are permitted provided that the following conditions are met:
7   
8 Redistributions of source code must retain the above copyright notice, this list 
9 of conditions and the following disclaimer.
10     
11 Redistributions in binary form must reproduce the above copyright notice, this
12 list of conditions and the following disclaimer in the documentation and/or
13 other materials provided with the distribution.
14       
15 Neither the name of Loki software nor the names of its contributors may be used 
16 to endorse or promote products derived from this software without specific prior 
17 written permission. 
18         
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
22 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY 
23 DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
26 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
29 */
30
31 //
32 // Rules:
33 //
34 // - Directories should be searched in the following order: ~/.q3a/baseq3,
35 //   install dir (/usr/local/games/quake3/baseq3) and cd_path (/mnt/cdrom/baseq3).
36 //
37 // - Pak files are searched first inside the directories.
38 // - Case insensitive.
39 // - Unix-style slashes (/) (windows is backwards .. everyone knows that)
40 //
41 // Leonardo Zide (leo@lokigames.com)
42 //
43
44 #include <string.h>
45 #include <stdlib.h>
46 #include <sys/stat.h>
47
48 #include "cmdlib.h"
49 #include "filematch.h"
50 #include "mathlib.h"
51 #include "inout.h"
52 #include "vfs.h"
53 #include "unzip.h"
54
55 typedef struct
56 {
57   char*   name;
58   unz_s   zipinfo;
59   unzFile zipfile;
60   guint32   size;
61 } VFS_PAKFILE;
62
63 // =============================================================================
64 // Global variables
65
66 static GSList*  g_unzFiles;
67 static GSList*  g_pakFiles;
68 static char     g_strDirs[VFS_MAXDIRS][PATH_MAX+1];
69 static int      g_numDirs;
70 char     g_strForbiddenDirs[VFS_MAXDIRS][PATH_MAX+1];
71 int      g_numForbiddenDirs = 0;
72 static gboolean g_bUsePak = TRUE;
73
74 // =============================================================================
75 // Static functions
76
77 static void vfsAddSlash (char *str)
78 {
79   int n = strlen (str);
80   if (n > 0)
81   {
82     if (str[n-1] != '\\' && str[n-1] != '/')
83       strcat (str, "/");
84   }
85 }
86
87 static void vfsFixDOSName (char *src)
88 {
89   if (src == NULL)
90     return;
91   
92   while (*src)
93   {
94     if (*src == '\\')
95       *src = '/';
96     src++;
97   }
98 }
99
100 //!\todo Define globally or use heap-allocated string.
101 #define NAME_MAX 255
102
103 static void vfsInitPakFile (const char *filename)
104 {
105   unz_global_info gi;
106   unzFile uf;
107   guint32 i;
108   int err;
109   
110   uf = unzOpen (filename);
111   if (uf == NULL)
112     return;
113   
114   g_unzFiles = g_slist_append (g_unzFiles, uf);
115   
116   err = unzGetGlobalInfo (uf,&gi);
117   if (err != UNZ_OK)
118     return;
119   unzGoToFirstFile(uf);
120   
121   for (i = 0; i < gi.number_entry; i++)
122   {
123     char filename_inzip[NAME_MAX];
124     unz_file_info file_info;
125     VFS_PAKFILE* file;
126     
127     err = unzGetCurrentFileInfo (uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
128     if (err != UNZ_OK)
129       break;
130     
131     file = (VFS_PAKFILE*)safe_malloc (sizeof (VFS_PAKFILE));
132     g_pakFiles = g_slist_append (g_pakFiles, file);
133     
134     vfsFixDOSName (filename_inzip);
135     g_strdown (filename_inzip);
136     
137     file->name = strdup (filename_inzip);
138     file->size = file_info.uncompressed_size;
139     file->zipfile = uf;
140     memcpy (&file->zipinfo, uf, sizeof (unz_s));
141     
142     if ((i+1) < gi.number_entry)
143     {
144       err = unzGoToNextFile(uf);
145       if (err!=UNZ_OK)
146         break;
147     }
148   }
149 }
150
151 // =============================================================================
152 // Global functions
153
154 // reads all pak files from a dir
155 void vfsInitDirectory (const char *path)
156 {
157   char filename[PATH_MAX];
158   char *dirlist;
159   GDir *dir;
160   int j;
161
162   for(j = 0; j < g_numForbiddenDirs; ++j)
163   {
164     char* dbuf = g_strdup(path);
165     if(*dbuf && dbuf[strlen(dbuf)-1] == '/')
166       dbuf[strlen(dbuf)-1] = 0;
167     const char *p = strrchr(dbuf, '/');
168     p = (p ? (p+1) : dbuf);
169     if(matchpattern(p, g_strForbiddenDirs[j], TRUE))
170     {
171       g_free(dbuf);
172       break;
173     }
174     g_free(dbuf);
175   }
176   if(j < g_numForbiddenDirs)
177     return;
178   
179   if (g_numDirs == VFS_MAXDIRS)
180     return;
181   
182   Sys_Printf ("VFS Init: %s\n", path);
183   
184   strncpy (g_strDirs[g_numDirs], path, PATH_MAX);
185   g_strDirs[g_numDirs][PATH_MAX] = 0;
186   vfsFixDOSName (g_strDirs[g_numDirs]);
187   vfsAddSlash (g_strDirs[g_numDirs]);
188   g_numDirs++;
189   
190   if (g_bUsePak)
191   {
192     dir = g_dir_open (path, 0, NULL);
193
194     if (dir != NULL)
195     {
196       while (1)
197       {
198         const char* name = g_dir_read_name(dir);
199         if(name == NULL)
200           break;
201
202         for(j = 0; j < g_numForbiddenDirs; ++j)
203         {
204           const char *p = strrchr(name, '/');
205           p = (p ? (p+1) : name);
206           if(matchpattern(p, g_strForbiddenDirs[j], TRUE))
207             break;
208         }
209         if(j < g_numForbiddenDirs)
210           continue;
211
212         dirlist = g_strdup(name);
213
214         {
215           char *ext = strrchr (dirlist, '.');
216
217           if(ext && !Q_stricmp(ext, ".pk3dir"))
218           {
219             if (g_numDirs == VFS_MAXDIRS)
220               continue;
221             snprintf(g_strDirs[g_numDirs], PATH_MAX, "%s/%s", path, name);
222             g_strDirs[g_numDirs][PATH_MAX] = '\0';
223             vfsFixDOSName (g_strDirs[g_numDirs]);
224             vfsAddSlash (g_strDirs[g_numDirs]);
225             ++g_numDirs;
226           }
227
228           if ((ext == NULL) || (Q_stricmp (ext, ".pk3") != 0))
229             continue;
230         }
231         
232         sprintf (filename, "%s/%s", path, dirlist);
233         vfsInitPakFile (filename);
234
235         g_free(dirlist);
236       }
237       g_dir_close (dir);
238     }
239   }
240 }
241
242 // frees all memory that we allocated
243 void vfsShutdown ()
244 {
245   while (g_unzFiles)
246   {
247     unzClose ((unzFile)g_unzFiles->data);
248     g_unzFiles = g_slist_remove (g_unzFiles, g_unzFiles->data);
249   }
250   
251   while (g_pakFiles)
252   {
253     VFS_PAKFILE* file = (VFS_PAKFILE*)g_pakFiles->data;
254     free (file->name);
255     free (file);
256     g_pakFiles = g_slist_remove (g_pakFiles, file);
257   }
258 }
259
260 // return the number of files that match
261 int vfsGetFileCount (const char *filename)
262 {
263   int i, count = 0;
264   char fixed[NAME_MAX], tmp[NAME_MAX];
265   GSList *lst;
266   
267   strcpy (fixed, filename);
268   vfsFixDOSName (fixed);
269   g_strdown (fixed);
270   
271   for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))
272   {
273     VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
274     
275     if (strcmp (file->name, fixed) == 0)
276       count++;
277   }
278   
279   for (i = 0; i < g_numDirs; i++)
280   {
281     strcpy (tmp, g_strDirs[i]);
282     strcat (tmp, fixed);
283     if (access (tmp, R_OK) == 0)
284       count++;
285   }
286   
287   return count;
288 }
289
290 // NOTE: when loading a file, you have to allocate one extra byte and set it to \0
291 int vfsLoadFile (const char *filename, void **bufferptr, int index)
292 {
293   int i, count = 0;
294   char tmp[NAME_MAX], fixed[NAME_MAX];
295   GSList *lst;
296   
297   // filename is a full path
298   if (index == -1)
299   {
300     long len;
301     FILE *f;
302     
303     f = fopen (filename, "rb");
304     if (f == NULL)
305       return -1;
306     
307     fseek (f, 0, SEEK_END);
308     len = ftell (f);
309     rewind (f);
310     
311     *bufferptr = safe_malloc (len+1);
312     if (*bufferptr == NULL)
313         {
314                 fclose(f);
315                 return -1;
316         }
317     
318     if(fread (*bufferptr, 1, len, f) != (size_t) len)
319         {
320                 fclose(f);
321                 return -1;
322         }
323     fclose (f);
324     
325     // we need to end the buffer with a 0
326     ((char*) (*bufferptr))[len] = 0;
327     
328     return len;
329   }
330   
331   *bufferptr = NULL;
332   strcpy (fixed, filename);
333   vfsFixDOSName (fixed);
334   g_strdown (fixed);
335   
336   for (i = 0; i < g_numDirs; i++)
337   {
338     strcpy (tmp, g_strDirs[i]);
339     strcat (tmp, filename);
340     if (access (tmp, R_OK) == 0)
341     {
342       if (count == index)
343       {
344         long len;
345         FILE *f;
346         
347         f = fopen (tmp, "rb");
348         if (f == NULL)
349           return -1;
350         
351         fseek (f, 0, SEEK_END);
352         len = ftell (f);
353         rewind (f);
354         
355         *bufferptr = safe_malloc (len+1);
356         if (*bufferptr == NULL)
357                 {
358                   fclose(f);
359           return -1;
360                 }
361         
362         if(fread (*bufferptr, 1, len, f) != (size_t) len)
363                 {
364                   fclose(f);
365           return -1;
366                 }
367         fclose (f);
368         
369         // we need to end the buffer with a 0
370         ((char*) (*bufferptr))[len] = 0;
371         
372         return len;
373       }
374       
375       count++;
376     }
377   }
378   
379   for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))
380   {
381     VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
382     
383     if (strcmp (file->name, fixed) != 0)
384       continue;
385     
386     if (count == index)
387     {
388       memcpy (file->zipfile, &file->zipinfo, sizeof (unz_s));
389       
390       if (unzOpenCurrentFile (file->zipfile) != UNZ_OK)
391         return -1;
392       
393       *bufferptr = safe_malloc (file->size+1);
394       // we need to end the buffer with a 0
395       ((char*) (*bufferptr))[file->size] = 0;
396       
397       i = unzReadCurrentFile (file->zipfile , *bufferptr, file->size);
398       unzCloseCurrentFile (file->zipfile);
399       if (i < 0)
400         return -1;
401       else
402         return file->size;
403     }
404     
405     count++;
406   }
407   
408   return -1;
409 }