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