2 Copyright (c) 2001, Loki software, inc.
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
8 Redistributions of source code must retain the above copyright notice, this list
9 of conditions and the following disclaimer.
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.
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
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.
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).
37 // - Pak files are searched first inside the directories.
38 // - Case insensitive.
39 // - Unix-style slashes (/) (windows is backwards .. everyone knows that)
41 // Leonardo Zide (leo@lokigames.com)
49 #include "filematch.h"
63 // =============================================================================
66 static GSList* g_unzFiles;
67 static GSList* g_pakFiles;
68 static char g_strDirs[VFS_MAXDIRS][PATH_MAX + 1];
70 char g_strForbiddenDirs[VFS_MAXDIRS][PATH_MAX + 1];
71 int g_numForbiddenDirs = 0;
72 static gboolean g_bUsePak = TRUE;
74 // =============================================================================
77 static void vfsAddSlash( char *str ){
78 int n = strlen( str );
80 if ( str[n - 1] != '\\' && str[n - 1] != '/' ) {
86 static void vfsFixDOSName( char *src ){
100 //!\todo Define globally or use heap-allocated string.
103 static void vfsInitPakFile( const char *filename ){
109 uf = unzOpen( filename );
114 g_unzFiles = g_slist_append( g_unzFiles, uf );
116 err = unzGetGlobalInfo( uf,&gi );
117 if ( err != UNZ_OK ) {
120 unzGoToFirstFile( uf );
122 for ( i = 0; i < gi.number_entry; i++ )
124 char filename_inzip[NAME_MAX];
125 unz_file_info file_info;
128 err = unzGetCurrentFileInfo( uf, &file_info, filename_inzip, sizeof( filename_inzip ), NULL, 0, NULL, 0 );
129 if ( err != UNZ_OK ) {
133 file = (VFS_PAKFILE*)safe_malloc( sizeof( VFS_PAKFILE ) );
134 g_pakFiles = g_slist_append( g_pakFiles, file );
136 vfsFixDOSName( filename_inzip );
137 g_strdown( filename_inzip );
139 file->name = strdup( filename_inzip );
140 file->size = file_info.uncompressed_size;
142 memcpy( &file->zipinfo, uf, sizeof( unz_s ) );
144 if ( ( i + 1 ) < gi.number_entry ) {
145 err = unzGoToNextFile( uf );
146 if ( err != UNZ_OK ) {
153 // =============================================================================
156 // reads all pak files from a dir
157 void vfsInitDirectory( const char *path ){
158 char filename[PATH_MAX];
163 for ( j = 0; j < g_numForbiddenDirs; ++j )
165 char* dbuf = g_strdup( path );
166 if ( *dbuf && dbuf[strlen( dbuf ) - 1] == '/' ) {
167 dbuf[strlen( dbuf ) - 1] = 0;
169 const char *p = strrchr( dbuf, '/' );
170 p = ( p ? ( p + 1 ) : dbuf );
171 if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) {
177 if ( j < g_numForbiddenDirs ) {
181 if ( g_numDirs == VFS_MAXDIRS ) {
185 Sys_Printf( "VFS Init: %s\n", path );
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] );
194 dir = g_dir_open( path, 0, NULL );
199 const char* name = g_dir_read_name( dir );
200 if ( name == NULL ) {
204 for ( j = 0; j < g_numForbiddenDirs; ++j )
206 const char *p = strrchr( name, '/' );
207 p = ( p ? ( p + 1 ) : name );
208 if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) {
212 if ( j < g_numForbiddenDirs ) {
216 dirlist = g_strdup( name );
219 char *ext = strrchr( dirlist, '.' );
221 if ( ext && ( !Q_stricmp( ext, ".pk3dir" ) || !Q_stricmp( ext, ".dpkdir" ) ) ) {
222 if ( g_numDirs == VFS_MAXDIRS ) {
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] );
232 if ( ( ext == NULL ) || ( Q_stricmp( ext, ".pk3" ) != 0 || !Q_stricmp( ext, ".dpk" ) != 0 ) ) {
237 sprintf( filename, "%s/%s", path, dirlist );
238 vfsInitPakFile( filename );
247 // frees all memory that we allocated
251 unzClose( (unzFile)g_unzFiles->data );
252 g_unzFiles = g_slist_remove( g_unzFiles, g_unzFiles->data );
257 VFS_PAKFILE* file = (VFS_PAKFILE*)g_pakFiles->data;
260 g_pakFiles = g_slist_remove( g_pakFiles, file );
264 // return the number of files that match
265 int vfsGetFileCount( const char *filename ){
267 char fixed[NAME_MAX], tmp[NAME_MAX];
270 strcpy( fixed, filename );
271 vfsFixDOSName( fixed );
274 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
276 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
278 if ( strcmp( file->name, fixed ) == 0 ) {
283 for ( i = 0; i < g_numDirs; i++ )
285 strcpy( tmp, g_strDirs[i] );
286 strcat( tmp, fixed );
287 if ( access( tmp, R_OK ) == 0 ) {
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 ){
298 char tmp[NAME_MAX], fixed[NAME_MAX];
301 // filename is a full path
306 f = fopen( filename, "rb" );
311 fseek( f, 0, SEEK_END );
315 *bufferptr = safe_malloc( len + 1 );
316 if ( *bufferptr == NULL ) {
321 if ( fread( *bufferptr, 1, len, f ) != (size_t) len ) {
327 // we need to end the buffer with a 0
328 ( (char*) ( *bufferptr ) )[len] = 0;
334 strcpy( fixed, filename );
335 vfsFixDOSName( fixed );
338 for ( i = 0; i < g_numDirs; i++ )
340 strcpy( tmp, g_strDirs[i] );
341 strcat( tmp, filename );
342 if ( access( tmp, R_OK ) == 0 ) {
343 if ( count == index ) {
347 f = fopen( tmp, "rb" );
352 fseek( f, 0, SEEK_END );
356 *bufferptr = safe_malloc( len + 1 );
357 if ( *bufferptr == NULL ) {
362 if ( fread( *bufferptr, 1, len, f ) != (size_t) len ) {
368 // we need to end the buffer with a 0
369 ( (char*) ( *bufferptr ) )[len] = 0;
378 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
380 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
382 if ( strcmp( file->name, fixed ) != 0 ) {
386 if ( count == index ) {
387 memcpy( file->zipfile, &file->zipinfo, sizeof( unz_s ) );
389 if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
393 *bufferptr = safe_malloc( file->size + 1 );
394 // we need to end the buffer with a 0
395 ( (char*) ( *bufferptr ) )[file->size] = 0;
397 i = unzReadCurrentFile( file->zipfile, *bufferptr, file->size );
398 unzCloseCurrentFile( file->zipfile );