fix bugs in skin load code
[divverent/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       return -1;
301     
302     fread (*bufferptr, 1, len, f);
303     fclose (f);
304     
305     // we need to end the buffer with a 0
306     ((char*) (*bufferptr))[len] = 0;
307     
308     return len;
309   }
310   
311   *bufferptr = NULL;
312   strcpy (fixed, filename);
313   vfsFixDOSName (fixed);
314   g_strdown (fixed);
315   
316   for (i = 0; i < g_numDirs; i++)
317   {
318     strcpy (tmp, g_strDirs[i]);
319     strcat (tmp, filename);
320     if (access (tmp, R_OK) == 0)
321     {
322       if (count == index)
323       {
324         long len;
325         FILE *f;
326         
327         f = fopen (tmp, "rb");
328         if (f == NULL)
329           return -1;
330         
331         fseek (f, 0, SEEK_END);
332         len = ftell (f);
333         rewind (f);
334         
335         *bufferptr = safe_malloc (len+1);
336         if (*bufferptr == NULL)
337           return -1;
338         
339         fread (*bufferptr, 1, len, f);
340         fclose (f);
341         
342         // we need to end the buffer with a 0
343         ((char*) (*bufferptr))[len] = 0;
344         
345         return len;
346       }
347       
348       count++;
349     }
350   }
351   
352   for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))
353   {
354     VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
355     
356     if (strcmp (file->name, fixed) != 0)
357       continue;
358     
359     if (count == index)
360     {
361       memcpy (file->zipfile, &file->zipinfo, sizeof (unz_s));
362       
363       if (unzOpenCurrentFile (file->zipfile) != UNZ_OK)
364         return -1;
365       
366       *bufferptr = safe_malloc (file->size+1);
367       // we need to end the buffer with a 0
368       ((char*) (*bufferptr))[file->size] = 0;
369       
370       i = unzReadCurrentFile (file->zipfile , *bufferptr, file->size);
371       unzCloseCurrentFile (file->zipfile);
372       if (i < 0)
373         return -1;
374       else
375         return file->size;
376     }
377     
378     count++;
379   }
380   
381   return -1;
382 }