]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/common/vfs.c
fix some more warnings
[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 "mathlib.h"
50 #include "inout.h"
51 #include "vfs.h"
52 #include "unzip.h"
53
54 typedef struct
55 {
56   char*   name;
57   unz_s   zipinfo;
58   unzFile zipfile;
59   guint32   size;
60 } VFS_PAKFILE;
61
62 // =============================================================================
63 // Global variables
64
65 static GSList*  g_unzFiles;
66 static GSList*  g_pakFiles;
67 static char     g_strDirs[VFS_MAXDIRS][PATH_MAX+1];
68 static int      g_numDirs;
69 char     g_strForbiddenDirs[VFS_MAXDIRS][PATH_MAX+1];
70 int      g_numForbiddenDirs = 0;
71 static gboolean g_bUsePak = TRUE;
72
73 // =============================================================================
74 // Static functions
75
76 static void vfsAddSlash (char *str)
77 {
78   int n = strlen (str);
79   if (n > 0)
80   {
81     if (str[n-1] != '\\' && str[n-1] != '/')
82       strcat (str, "/");
83   }
84 }
85
86 static void vfsFixDOSName (char *src)
87 {
88   if (src == NULL)
89     return;
90   
91   while (*src)
92   {
93     if (*src == '\\')
94       *src = '/';
95     src++;
96   }
97 }
98
99 //!\todo Define globally or use heap-allocated string.
100 #define NAME_MAX 255
101
102 static void vfsInitPakFile (const char *filename)
103 {
104   unz_global_info gi;
105   unzFile uf;
106   guint32 i;
107   int err;
108   
109   uf = unzOpen (filename);
110   if (uf == NULL)
111     return;
112   
113   g_unzFiles = g_slist_append (g_unzFiles, uf);
114   
115   err = unzGetGlobalInfo (uf,&gi);
116   if (err != UNZ_OK)
117     return;
118   unzGoToFirstFile(uf);
119   
120   for (i = 0; i < gi.number_entry; i++)
121   {
122     char filename_inzip[NAME_MAX];
123     unz_file_info file_info;
124     VFS_PAKFILE* file;
125     
126     err = unzGetCurrentFileInfo (uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
127     if (err != UNZ_OK)
128       break;
129     
130     file = (VFS_PAKFILE*)safe_malloc (sizeof (VFS_PAKFILE));
131     g_pakFiles = g_slist_append (g_pakFiles, file);
132     
133     vfsFixDOSName (filename_inzip);
134     g_strdown (filename_inzip);
135     
136     file->name = strdup (filename_inzip);
137     file->size = file_info.uncompressed_size;
138     file->zipfile = uf;
139     memcpy (&file->zipinfo, uf, sizeof (unz_s));
140     
141     if ((i+1) < gi.number_entry)
142     {
143       err = unzGoToNextFile(uf);
144       if (err!=UNZ_OK)
145         break;
146     }
147   }
148 }
149
150 // =============================================================================
151 // Global functions
152
153 // reads all pak files from a dir
154 void vfsInitDirectory (const char *path)
155 {
156   char filename[PATH_MAX];
157   char *dirlist;
158   GDir *dir;
159   int j;
160
161   for(j = 0; j < g_numForbiddenDirs; ++j)
162   {
163     if(!Q_stricmp(path, g_strForbiddenDirs[j])
164     || (strlen(path) > strlen(g_strForbiddenDirs[j]) && path[strlen(path) - strlen(g_strForbiddenDirs[j]) - 1] == '/' && !Q_stricmp(path + strlen(path) - strlen(g_strForbiddenDirs[j]), g_strForbiddenDirs[j])))
165       break;
166   }
167   if(j < g_numForbiddenDirs)
168     return;
169   
170   if (g_numDirs == VFS_MAXDIRS)
171     return;
172   
173   Sys_Printf ("VFS Init: %s\n", path);
174   
175   strncpy (g_strDirs[g_numDirs], path, PATH_MAX);
176   g_strDirs[g_numDirs][PATH_MAX] = 0;
177   vfsFixDOSName (g_strDirs[g_numDirs]);
178   vfsAddSlash (g_strDirs[g_numDirs]);
179   g_numDirs++;
180   
181   if (g_bUsePak)
182   {
183     dir = g_dir_open (path, 0, NULL);
184
185     if (dir != NULL)
186     {
187       while (1)
188       {
189         const char* name = g_dir_read_name(dir);
190         if(name == NULL)
191           break;
192
193         for(j = 0; j < g_numForbiddenDirs; ++j)
194           if(!Q_stricmp(name, g_strForbiddenDirs[j]))
195             break;
196         if(j < g_numForbiddenDirs)
197           continue;
198
199         dirlist = g_strdup(name);
200
201         {
202           char *ext = strrchr (dirlist, '.');
203
204           if(ext && !Q_stricmp(ext, ".pk3dir"))
205           {
206             if (g_numDirs == VFS_MAXDIRS)
207               continue;
208             snprintf(g_strDirs[g_numDirs], PATH_MAX, "%s/%s", path, name);
209             g_strDirs[g_numDirs][PATH_MAX] = '\0';
210             vfsFixDOSName (g_strDirs[g_numDirs]);
211             vfsAddSlash (g_strDirs[g_numDirs]);
212             ++g_numDirs;
213           }
214
215           if ((ext == NULL) || (Q_stricmp (ext, ".pk3") != 0))
216             continue;
217         }
218         
219         sprintf (filename, "%s/%s", path, dirlist);
220         vfsInitPakFile (filename);
221
222         g_free(dirlist);
223       }
224       g_dir_close (dir);
225     }
226   }
227 }
228
229 // frees all memory that we allocated
230 void vfsShutdown ()
231 {
232   while (g_unzFiles)
233   {
234     unzClose ((unzFile)g_unzFiles->data);
235     g_unzFiles = g_slist_remove (g_unzFiles, g_unzFiles->data);
236   }
237   
238   while (g_pakFiles)
239   {
240     VFS_PAKFILE* file = (VFS_PAKFILE*)g_pakFiles->data;
241     free (file->name);
242     free (file);
243     g_pakFiles = g_slist_remove (g_pakFiles, file);
244   }
245 }
246
247 // return the number of files that match
248 int vfsGetFileCount (const char *filename)
249 {
250   int i, count = 0;
251   char fixed[NAME_MAX], tmp[NAME_MAX];
252   GSList *lst;
253   
254   strcpy (fixed, filename);
255   vfsFixDOSName (fixed);
256   g_strdown (fixed);
257   
258   for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))
259   {
260     VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
261     
262     if (strcmp (file->name, fixed) == 0)
263       count++;
264   }
265   
266   for (i = 0; i < g_numDirs; i++)
267   {
268     strcpy (tmp, g_strDirs[i]);
269     strcat (tmp, fixed);
270     if (access (tmp, R_OK) == 0)
271       count++;
272   }
273   
274   return count;
275 }
276
277 // NOTE: when loading a file, you have to allocate one extra byte and set it to \0
278 int vfsLoadFile (const char *filename, void **bufferptr, int index)
279 {
280   int i, count = 0;
281   char tmp[NAME_MAX], fixed[NAME_MAX];
282   GSList *lst;
283   
284   // filename is a full path
285   if (index == -1)
286   {
287     long len;
288     FILE *f;
289     
290     f = fopen (filename, "rb");
291     if (f == NULL)
292       return -1;
293     
294     fseek (f, 0, SEEK_END);
295     len = ftell (f);
296     rewind (f);
297     
298     *bufferptr = safe_malloc (len+1);
299     if (*bufferptr == NULL)
300         {
301                 fclose(f);
302                 return -1;
303         }
304     
305     if(fread (*bufferptr, 1, len, f) != len)
306         {
307                 fclose(f);
308                 return -1;
309         }
310     fclose (f);
311     
312     // we need to end the buffer with a 0
313     ((char*) (*bufferptr))[len] = 0;
314     
315     return len;
316   }
317   
318   *bufferptr = NULL;
319   strcpy (fixed, filename);
320   vfsFixDOSName (fixed);
321   g_strdown (fixed);
322   
323   for (i = 0; i < g_numDirs; i++)
324   {
325     strcpy (tmp, g_strDirs[i]);
326     strcat (tmp, filename);
327     if (access (tmp, R_OK) == 0)
328     {
329       if (count == index)
330       {
331         long len;
332         FILE *f;
333         
334         f = fopen (tmp, "rb");
335         if (f == NULL)
336           return -1;
337         
338         fseek (f, 0, SEEK_END);
339         len = ftell (f);
340         rewind (f);
341         
342         *bufferptr = safe_malloc (len+1);
343         if (*bufferptr == NULL)
344           return -1;
345         
346         fread (*bufferptr, 1, len, f);
347         fclose (f);
348         
349         // we need to end the buffer with a 0
350         ((char*) (*bufferptr))[len] = 0;
351         
352         return len;
353       }
354       
355       count++;
356     }
357   }
358   
359   for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))
360   {
361     VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
362     
363     if (strcmp (file->name, fixed) != 0)
364       continue;
365     
366     if (count == index)
367     {
368       memcpy (file->zipfile, &file->zipinfo, sizeof (unz_s));
369       
370       if (unzOpenCurrentFile (file->zipfile) != UNZ_OK)
371         return -1;
372       
373       *bufferptr = safe_malloc (file->size+1);
374       // we need to end the buffer with a 0
375       ((char*) (*bufferptr))[file->size] = 0;
376       
377       i = unzReadCurrentFile (file->zipfile , *bufferptr, file->size);
378       unzCloseCurrentFile (file->zipfile);
379       if (i < 0)
380         return -1;
381       else
382         return file->size;
383     }
384     
385     count++;
386   }
387   
388   return -1;
389 }