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"
53 #include <minizip/unzip.h>
64 // =============================================================================
67 static GSList* g_unzFiles;
68 static GSList* g_pakFiles;
69 static char g_strDirs[VFS_MAXDIRS][PATH_MAX + 1];
71 char g_strForbiddenDirs[VFS_MAXDIRS][PATH_MAX + 1];
72 int g_numForbiddenDirs = 0;
73 static gboolean g_bUsePak = TRUE;
75 // =============================================================================
78 static void vfsAddSlash( char *str ){
79 int n = strlen( str );
81 if ( str[n - 1] != '\\' && str[n - 1] != '/' ) {
87 static void vfsFixDOSName( char *src ){
101 //!\todo Define globally or use heap-allocated string.
104 static void vfsInitPakFile( const char *filename ){
110 uf = unzOpen( filename );
115 g_unzFiles = g_slist_append( g_unzFiles, uf );
117 err = unzGetGlobalInfo( uf,&gi );
118 if ( err != UNZ_OK ) {
121 unzGoToFirstFile( uf );
123 for ( i = 0; i < gi.number_entry; i++ )
125 char filename_inzip[NAME_MAX];
126 unz_file_info file_info;
129 err = unzGetCurrentFileInfo( uf, &file_info, filename_inzip, sizeof( filename_inzip ), NULL, 0, NULL, 0 );
130 if ( err != UNZ_OK ) {
134 err = unzGetFilePos( uf, &pos );
135 if ( err != UNZ_OK ) {
139 file = (VFS_PAKFILE*)safe_malloc( sizeof( VFS_PAKFILE ) );
140 g_pakFiles = g_slist_append( g_pakFiles, file );
142 vfsFixDOSName( filename_inzip );
143 g_strdown( filename_inzip );
145 file->name = strdup( filename_inzip );
146 file->size = file_info.uncompressed_size;
150 if ( ( i + 1 ) < gi.number_entry ) {
151 err = unzGoToNextFile( uf );
152 if ( err != UNZ_OK ) {
159 // =============================================================================
162 // reads all pak files from a dir
163 void vfsInitDirectory( const char *path ){
164 char filename[PATH_MAX];
169 for ( j = 0; j < g_numForbiddenDirs; ++j )
171 char* dbuf = g_strdup( path );
172 if ( *dbuf && dbuf[strlen( dbuf ) - 1] == '/' ) {
173 dbuf[strlen( dbuf ) - 1] = 0;
175 const char *p = strrchr( dbuf, '/' );
176 p = ( p ? ( p + 1 ) : dbuf );
177 if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) {
183 if ( j < g_numForbiddenDirs ) {
187 if ( g_numDirs == VFS_MAXDIRS ) {
191 Sys_Printf( "VFS Init: %s\n", path );
193 strncpy( g_strDirs[g_numDirs], path, PATH_MAX );
194 g_strDirs[g_numDirs][PATH_MAX] = 0;
195 vfsFixDOSName( g_strDirs[g_numDirs] );
196 vfsAddSlash( g_strDirs[g_numDirs] );
200 dir = g_dir_open( path, 0, NULL );
205 const char* name = g_dir_read_name( dir );
206 if ( name == NULL ) {
210 for ( j = 0; j < g_numForbiddenDirs; ++j )
212 const char *p = strrchr( name, '/' );
213 p = ( p ? ( p + 1 ) : name );
214 if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) {
218 if ( j < g_numForbiddenDirs ) {
222 dirlist = g_strdup( name );
225 char *ext = strrchr( dirlist, '.' );
227 if ( ext && ( !Q_stricmp( ext, ".pk3dir" ) || !Q_stricmp( ext, ".dpkdir" ) ) ) {
228 if ( g_numDirs == VFS_MAXDIRS ) {
231 snprintf( g_strDirs[g_numDirs], PATH_MAX, "%s/%s", path, name );
232 g_strDirs[g_numDirs][PATH_MAX] = '\0';
233 vfsFixDOSName( g_strDirs[g_numDirs] );
234 vfsAddSlash( g_strDirs[g_numDirs] );
238 if ( ( ext == NULL ) || ( Q_stricmp( ext, ".pk3" ) != 0 || !Q_stricmp( ext, ".dpk" ) != 0 ) ) {
243 sprintf( filename, "%s/%s", path, dirlist );
244 vfsInitPakFile( filename );
253 // frees all memory that we allocated
257 unzClose( (unzFile)g_unzFiles->data );
258 g_unzFiles = g_slist_remove( g_unzFiles, g_unzFiles->data );
263 VFS_PAKFILE* file = (VFS_PAKFILE*)g_pakFiles->data;
266 g_pakFiles = g_slist_remove( g_pakFiles, file );
270 // return the number of files that match
271 int vfsGetFileCount( const char *filename ){
273 char fixed[NAME_MAX], tmp[NAME_MAX];
276 strcpy( fixed, filename );
277 vfsFixDOSName( fixed );
280 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
282 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
284 if ( strcmp( file->name, fixed ) == 0 ) {
289 for ( i = 0; i < g_numDirs; i++ )
291 strcpy( tmp, g_strDirs[i] );
292 strcat( tmp, fixed );
293 if ( access( tmp, R_OK ) == 0 ) {
301 // NOTE: when loading a file, you have to allocate one extra byte and set it to \0
302 int vfsLoadFile( const char *filename, void **bufferptr, int index ){
304 char tmp[NAME_MAX], fixed[NAME_MAX];
307 // filename is a full path
312 f = fopen( filename, "rb" );
317 fseek( f, 0, SEEK_END );
321 *bufferptr = safe_malloc( len + 1 );
322 if ( *bufferptr == NULL ) {
327 if ( fread( *bufferptr, 1, len, f ) != (size_t) len ) {
333 // we need to end the buffer with a 0
334 ( (char*) ( *bufferptr ) )[len] = 0;
340 strcpy( fixed, filename );
341 vfsFixDOSName( fixed );
344 for ( i = 0; i < g_numDirs; i++ )
346 strcpy( tmp, g_strDirs[i] );
347 strcat( tmp, filename );
348 if ( access( tmp, R_OK ) == 0 ) {
349 if ( count == index ) {
353 f = fopen( tmp, "rb" );
358 fseek( f, 0, SEEK_END );
362 *bufferptr = safe_malloc( len + 1 );
363 if ( *bufferptr == NULL ) {
368 if ( fread( *bufferptr, 1, len, f ) != (size_t) len ) {
374 // we need to end the buffer with a 0
375 ( (char*) ( *bufferptr ) )[len] = 0;
384 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
386 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
388 if ( strcmp( file->name, fixed ) != 0 ) {
392 if ( count == index ) {
394 if ( unzGoToFilePos( file->zipfile, &file->zippos ) != UNZ_OK ) {
397 if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
401 *bufferptr = safe_malloc( file->size + 1 );
402 // we need to end the buffer with a 0
403 ( (char*) ( *bufferptr ) )[file->size] = 0;
405 i = unzReadCurrentFile( file->zipfile, *bufferptr, file->size );
406 unzCloseCurrentFile( file->zipfile );