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