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>
65 // =============================================================================
68 static GSList* g_unzFiles;
69 static GSList* g_pakFiles;
70 static char g_strDirs[VFS_MAXDIRS][PATH_MAX + 1];
72 char g_strForbiddenDirs[VFS_MAXDIRS][PATH_MAX + 1];
73 int g_numForbiddenDirs = 0;
74 static gboolean g_bUsePak = TRUE;
76 // =============================================================================
79 static void vfsAddSlash( char *str ){
80 int n = strlen( str );
82 if ( str[n - 1] != '\\' && str[n - 1] != '/' ) {
88 static void vfsFixDOSName( char *src ){
102 //!\todo Define globally or use heap-allocated string.
105 static void vfsInitPakFile( const char *filename ){
111 uf = unzOpen( filename );
116 g_unzFiles = g_slist_append( g_unzFiles, uf );
118 err = unzGetGlobalInfo( uf,&gi );
119 if ( err != UNZ_OK ) {
122 unzGoToFirstFile( uf );
124 for ( i = 0; i < gi.number_entry; i++ )
126 char filename_inzip[NAME_MAX];
127 char *filename_lower;
128 unz_file_info file_info;
131 err = unzGetCurrentFileInfo( uf, &file_info, filename_inzip, sizeof( filename_inzip ), NULL, 0, NULL, 0 );
132 if ( err != UNZ_OK ) {
136 err = unzGetFilePos( uf, &pos );
137 if ( err != UNZ_OK ) {
141 file = (VFS_PAKFILE*)safe_malloc( sizeof( VFS_PAKFILE ) );
142 g_pakFiles = g_slist_append( g_pakFiles, file );
144 vfsFixDOSName( filename_inzip );
145 //-1 null terminated string
146 filename_lower = g_ascii_strdown( filename_inzip, -1 );
148 file->name = strdup( filename_lower );
149 file->size = file_info.uncompressed_size;
153 if ( ( i + 1 ) < gi.number_entry ) {
154 err = unzGoToNextFile( uf );
155 if ( err != UNZ_OK ) {
159 g_free( filename_lower );
163 // =============================================================================
166 // reads all pak files from a dir
167 void vfsInitDirectory( const char *path ){
168 char filename[PATH_MAX];
173 for ( j = 0; j < g_numForbiddenDirs; ++j )
175 char* dbuf = g_strdup( path );
176 if ( *dbuf && dbuf[strlen( dbuf ) - 1] == '/' ) {
177 dbuf[strlen( dbuf ) - 1] = 0;
179 const char *p = strrchr( dbuf, '/' );
180 p = ( p ? ( p + 1 ) : dbuf );
181 if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) {
187 if ( j < g_numForbiddenDirs ) {
191 if ( g_numDirs == VFS_MAXDIRS ) {
195 Sys_Printf( "VFS Init: %s\n", path );
197 strncpy( g_strDirs[g_numDirs], path, PATH_MAX );
198 g_strDirs[g_numDirs][PATH_MAX] = 0;
199 vfsFixDOSName( g_strDirs[g_numDirs] );
200 vfsAddSlash( g_strDirs[g_numDirs] );
204 dir = g_dir_open( path, 0, NULL );
209 const char* name = g_dir_read_name( dir );
210 if ( name == NULL ) {
214 for ( j = 0; j < g_numForbiddenDirs; ++j )
216 const char *p = strrchr( name, '/' );
217 p = ( p ? ( p + 1 ) : name );
218 if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) {
222 if ( j < g_numForbiddenDirs ) {
226 dirlist = g_strdup( name );
229 char *ext = strrchr( dirlist, '.' );
231 if ( ext != NULL && ( !Q_stricmp( ext, ".pk3dir" ) || !Q_stricmp( ext, ".dpkdir" ) ) ) {
232 if ( g_numDirs == VFS_MAXDIRS ) {
235 snprintf( g_strDirs[g_numDirs], PATH_MAX, "%s/%s", path, name );
236 g_strDirs[g_numDirs][PATH_MAX-1] = '\0';
237 vfsFixDOSName( g_strDirs[g_numDirs] );
238 vfsAddSlash( g_strDirs[g_numDirs] );
242 if ( ext == NULL || ( Q_stricmp( ext, ".pk3" ) != 0 && Q_stricmp( ext, ".dpk" ) != 0 ) ) {
247 sprintf( filename, "%s/%s", path, dirlist );
248 vfsInitPakFile( filename );
258 // lists all .shader files
259 void vfsListShaderFiles( char list[512][64], int *num ){
260 //char filename[PATH_MAX];
266 for ( i = 0; i < g_numDirs; i++ ){
267 strncpy( path, g_strDirs[ i ], NAME_MAX );
268 strcat( path, "scripts/" );
270 dir = g_dir_open( path, 0, NULL );
275 const char* name = g_dir_read_name( dir );
276 if ( name == NULL ) {
279 dirlist = g_strdup( name );
280 char *ext = strrchr( dirlist, '.' );
282 if ( ( ext == NULL ) || ( Q_stricmp( ext, ".shader" ) != 0 ) ) {
286 for ( k = 0; k < *num; k++ ){
287 if ( !stricmp( list[k], dirlist ) ) goto shISdouplicate;
289 strcpy( list[*num], dirlist );
297 /* search in packs */
300 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
302 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
304 char *ext = strrchr( file->name, '.' );
306 if ( ( ext == NULL ) || ( Q_stricmp( ext, ".shader" ) != 0 ) ) {
309 //name + ext this time
310 ext = strrchr( file->name, '/' );
313 for ( k = 0; k < *num; k++ ){
314 if ( !stricmp( list[k], ext ) ) goto shISdouplicate2;
316 strcpy( list[*num], ext );
323 // frees all memory that we allocated
327 unzClose( (unzFile)g_unzFiles->data );
328 g_unzFiles = g_slist_remove( g_unzFiles, g_unzFiles->data );
333 VFS_PAKFILE* file = (VFS_PAKFILE*)g_pakFiles->data;
336 g_pakFiles = g_slist_remove( g_pakFiles, file );
340 // return the number of files that match
341 int vfsGetFileCount( const char *filename ){
343 char fixed[NAME_MAX], tmp[NAME_MAX];
347 strcpy( fixed, filename );
348 vfsFixDOSName( fixed );
349 lower = g_ascii_strdown( fixed, -1 );
351 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
353 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
355 if ( strcmp( file->name, lower ) == 0 ) {
360 for ( i = 0; i < g_numDirs; i++ )
362 strcpy( tmp, g_strDirs[i] );
363 strcat( tmp, lower );
364 if ( access( tmp, R_OK ) == 0 ) {
372 // NOTE: when loading a file, you have to allocate one extra byte and set it to \0
373 int vfsLoadFile( const char *filename, void **bufferptr, int index ){
375 char tmp[NAME_MAX], fixed[NAME_MAX];
379 // filename is a full path
384 f = fopen( filename, "rb" );
389 fseek( f, 0, SEEK_END );
393 *bufferptr = safe_malloc( len + 1 );
394 if ( *bufferptr == NULL ) {
399 if ( fread( *bufferptr, 1, len, f ) != (size_t) len ) {
405 // we need to end the buffer with a 0
406 ( (char*) ( *bufferptr ) )[len] = 0;
412 strcpy( fixed, filename );
413 vfsFixDOSName( fixed );
414 lower = g_ascii_strdown( fixed, -1 );
416 for ( i = 0; i < g_numDirs; i++ )
418 strcpy( tmp, g_strDirs[i] );
419 strcat( tmp, filename );
420 if ( access( tmp, R_OK ) == 0 ) {
421 if ( count == index ) {
425 f = fopen( tmp, "rb" );
430 fseek( f, 0, SEEK_END );
434 *bufferptr = safe_malloc( len + 1 );
435 if ( *bufferptr == NULL ) {
440 if ( fread( *bufferptr, 1, len, f ) != (size_t) len ) {
446 // we need to end the buffer with a 0
447 ( (char*) ( *bufferptr ) )[len] = 0;
456 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
458 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
460 if ( strcmp( file->name, lower ) != 0 ) {
464 if ( count == index ) {
466 if ( unzGoToFilePos( file->zipfile, &file->zippos ) != UNZ_OK ) {
469 if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
473 *bufferptr = safe_malloc( file->size + 1 );
474 // we need to end the buffer with a 0
475 ( (char*) ( *bufferptr ) )[file->size] = 0;
477 i = unzReadCurrentFile( file->zipfile, *bufferptr, file->size );
478 unzCloseCurrentFile( file->zipfile );
495 qboolean vfsPackFile( const char *filename, const char *packname ){
497 char tmp[NAME_MAX], fixed[NAME_MAX];
500 byte *bufferptr = NULL;
501 strcpy( fixed, filename );
502 vfsFixDOSName( fixed );
505 for ( i = 0; i < g_numDirs; i++ )
507 strcpy( tmp, g_strDirs[i] );
508 strcat( tmp, filename );
509 if ( access( tmp, R_OK ) == 0 ) {
510 if ( access( packname, R_OK ) == 0 ) {
512 memset( &zip, 0, sizeof(zip) );
513 mz_zip_reader_init_file( &zip, packname, 0 );
514 mz_zip_writer_init_from_reader( &zip, packname );
516 mz_bool success = MZ_TRUE;
517 success &= mz_zip_writer_add_file( &zip, filename, tmp, 0, 0, 10 );
518 if ( !success || !mz_zip_writer_finalize_archive( &zip ) ){
519 Error( "Failed creating zip archive \"%s\"!\n", packname );
521 mz_zip_reader_end( &zip);
522 mz_zip_writer_end( &zip );
526 memset( &zip, 0, sizeof(zip) );
527 if( !mz_zip_writer_init_file( &zip, packname, 0 ) ){
528 Error( "Failed creating zip archive \"%s\"!\n", packname );
530 mz_bool success = MZ_TRUE;
531 success &= mz_zip_writer_add_file( &zip, filename, tmp, 0, 0, 10 );
532 if ( !success || !mz_zip_writer_finalize_archive( &zip ) ){
533 Error( "Failed creating zip archive \"%s\"!\n", packname );
535 mz_zip_writer_end( &zip );
542 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
544 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
546 if ( strcmp( file->name, fixed ) != 0 ) {
550 memcpy( file->zipfile, &file->zipinfo, sizeof( unz_s ) );
552 if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
556 bufferptr = safe_malloc( file->size + 1 );
557 // we need to end the buffer with a 0
558 ( (char*) ( bufferptr ) )[file->size] = 0;
560 i = unzReadCurrentFile( file->zipfile, bufferptr, file->size );
561 unzCloseCurrentFile( file->zipfile );
566 mz_bool success = MZ_TRUE;
567 success &= mz_zip_add_mem_to_archive_file_in_place( packname, filename, bufferptr, i, 0, 0, 10 );
569 Error( "Failed creating zip archive \"%s\"!\n", packname );