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