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