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