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