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