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>
68 // =============================================================================
71 static GSList* g_unzFiles;
72 static GSList* g_pakFiles;
73 static char g_strDirs[VFS_MAXDIRS][PATH_MAX + 1];
75 char g_strForbiddenDirs[VFS_MAXDIRS][PATH_MAX + 1];
76 int g_numForbiddenDirs = 0;
77 static gboolean g_bUsePak = TRUE;
79 // =============================================================================
82 static void vfsAddSlash( char *str ){
83 int n = strlen( str );
85 if ( str[n - 1] != '\\' && str[n - 1] != '/' ) {
91 static void vfsFixDOSName( char *src ){
105 //!\todo Define globally or use heap-allocated string.
108 static void vfsInitPakFile( const char *filename ){
114 uf = unzOpen( filename );
119 g_unzFiles = g_slist_append( g_unzFiles, uf );
121 err = unzGetGlobalInfo( uf,&gi );
122 if ( err != UNZ_OK ) {
125 unzGoToFirstFile( uf );
127 for ( i = 0; i < gi.number_entry; i++ )
129 char filename_inzip[NAME_MAX];
130 char *filename_lower;
131 unz_file_info file_info;
134 err = unzGetCurrentFileInfo( uf, &file_info, filename_inzip, sizeof( filename_inzip ), NULL, 0, NULL, 0 );
135 if ( err != UNZ_OK ) {
139 err = unzGetFilePos( uf, &pos );
140 if ( err != UNZ_OK ) {
144 file = (VFS_PAKFILE*)safe_malloc( sizeof( VFS_PAKFILE ) );
145 g_pakFiles = g_slist_append( g_pakFiles, file );
147 vfsFixDOSName( filename_inzip );
148 //-1 null terminated string
149 filename_lower = g_ascii_strdown( filename_inzip, -1 );
151 file->name = strdup( filename_lower );
152 file->size = file_info.uncompressed_size;
156 if ( ( i + 1 ) < gi.number_entry ) {
157 err = unzGoToNextFile( uf );
158 if ( err != UNZ_OK ) {
162 g_free( filename_lower );
166 // =============================================================================
169 // reads all pak files from a dir
170 void vfsInitDirectory( const char *path ){
171 char filename[PATH_MAX];
176 for ( j = 0; j < g_numForbiddenDirs; ++j )
178 char* dbuf = g_strdup( path );
179 if ( *dbuf && dbuf[strlen( dbuf ) - 1] == '/' ) {
180 dbuf[strlen( dbuf ) - 1] = 0;
182 const char *p = strrchr( dbuf, '/' );
183 p = ( p ? ( p + 1 ) : dbuf );
184 if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) {
190 if ( j < g_numForbiddenDirs ) {
194 if ( g_numDirs == VFS_MAXDIRS ) {
198 Sys_Printf( "VFS Init: %s\n", path );
200 strncpy( g_strDirs[g_numDirs], path, PATH_MAX );
201 g_strDirs[g_numDirs][PATH_MAX] = 0;
202 vfsFixDOSName( g_strDirs[g_numDirs] );
203 vfsAddSlash( g_strDirs[g_numDirs] );
207 dir = g_dir_open( path, 0, NULL );
212 const char* name = g_dir_read_name( dir );
213 if ( name == NULL ) {
217 for ( j = 0; j < g_numForbiddenDirs; ++j )
219 const char *p = strrchr( name, '/' );
220 p = ( p ? ( p + 1 ) : name );
221 if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) {
225 if ( j < g_numForbiddenDirs ) {
229 dirlist = g_strdup( name );
232 char *ext = strrchr( dirlist, '.' );
234 if ( ext != NULL && ( !Q_stricmp( ext, ".pk3dir" ) || !Q_stricmp( ext, ".dpkdir" ) ) ) {
235 if ( g_numDirs == VFS_MAXDIRS ) {
238 snprintf( g_strDirs[g_numDirs], PATH_MAX, "%s/%s", path, name );
239 g_strDirs[g_numDirs][PATH_MAX-1] = '\0';
240 vfsFixDOSName( g_strDirs[g_numDirs] );
241 vfsAddSlash( g_strDirs[g_numDirs] );
245 if ( ext == NULL || ( Q_stricmp( ext, ".pk3" ) != 0 && Q_stricmp( ext, ".dpk" ) != 0 ) ) {
250 sprintf( filename, "%s/%s", path, dirlist );
251 vfsInitPakFile( filename );
261 // lists all .shader files
262 void vfsListShaderFiles( char* list, int *num ){
263 //char filename[PATH_MAX];
269 for ( i = 0; i < g_numDirs; i++ ){
270 strncpy( path, g_strDirs[ i ], NAME_MAX );
271 strcat( path, "scripts/" );
273 dir = g_dir_open( path, 0, NULL );
278 const char* name = g_dir_read_name( dir );
279 if ( name == NULL ) {
282 dirlist = g_strdup( name );
283 char *ext = strrchr( dirlist, '.' );
285 if ( ( ext == NULL ) || ( Q_stricmp( ext, ".shader" ) != 0 ) ) {
289 for ( k = 0; k < *num; k++ ){
290 if ( !Q_stricmp( list + k*65, dirlist ) ) goto shISdouplicate;
292 strcpy( list + (*num)*65, dirlist );
300 /* search in packs */
303 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
305 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
307 char *ext = strrchr( file->name, '.' );
309 if ( ( ext == NULL ) || ( Q_stricmp( ext, ".shader" ) != 0 ) ) {
312 //name + ext this time
313 ext = strrchr( file->name, '/' );
316 for ( k = 0; k < *num; k++ ){
317 if ( !Q_stricmp( list + k*65, ext ) ) goto shISdouplicate2;
319 strcpy( list + (*num)*65, ext );
326 // frees all memory that we allocated
330 unzClose( (unzFile)g_unzFiles->data );
331 g_unzFiles = g_slist_remove( g_unzFiles, g_unzFiles->data );
336 VFS_PAKFILE* file = (VFS_PAKFILE*)g_pakFiles->data;
339 g_pakFiles = g_slist_remove( g_pakFiles, file );
343 // return the number of files that match
344 int vfsGetFileCount( const char *filename ){
346 char fixed[NAME_MAX], tmp[NAME_MAX];
350 strcpy( fixed, filename );
351 vfsFixDOSName( fixed );
352 lower = g_ascii_strdown( fixed, -1 );
354 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
356 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
358 if ( strcmp( file->name, lower ) == 0 ) {
363 for ( i = 0; i < g_numDirs; i++ )
365 strcpy( tmp, g_strDirs[i] );
366 strcat( tmp, lower );
367 if ( access( tmp, R_OK ) == 0 ) {
375 // NOTE: when loading a file, you have to allocate one extra byte and set it to \0
376 int vfsLoadFile( const char *filename, void **bufferptr, int index ){
378 char tmp[NAME_MAX], fixed[NAME_MAX];
382 // filename is a full path
387 f = fopen( filename, "rb" );
392 fseek( f, 0, SEEK_END );
396 *bufferptr = safe_malloc( len + 1 );
397 if ( *bufferptr == NULL ) {
402 if ( fread( *bufferptr, 1, len, f ) != (size_t) len ) {
408 // we need to end the buffer with a 0
409 ( (char*) ( *bufferptr ) )[len] = 0;
415 strcpy( fixed, filename );
416 vfsFixDOSName( fixed );
417 lower = g_ascii_strdown( fixed, -1 );
419 for ( i = 0; i < g_numDirs; i++ )
421 strcpy( tmp, g_strDirs[i] );
422 strcat( tmp, filename );
423 if ( access( tmp, R_OK ) == 0 ) {
424 if ( count == index ) {
428 f = fopen( tmp, "rb" );
433 fseek( f, 0, SEEK_END );
437 *bufferptr = safe_malloc( len + 1 );
438 if ( *bufferptr == NULL ) {
443 if ( fread( *bufferptr, 1, len, f ) != (size_t) len ) {
449 // we need to end the buffer with a 0
450 ( (char*) ( *bufferptr ) )[len] = 0;
459 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
461 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
463 if ( strcmp( file->name, lower ) != 0 ) {
467 if ( count == index ) {
469 if ( unzGoToFilePos( file->zipfile, &file->zippos ) != UNZ_OK ) {
472 if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
476 *bufferptr = safe_malloc( file->size + 1 );
477 // we need to end the buffer with a 0
478 ( (char*) ( *bufferptr ) )[file->size] = 0;
480 i = unzReadCurrentFile( file->zipfile, *bufferptr, file->size );
481 unzCloseCurrentFile( file->zipfile );
498 qboolean vfsPackFile( const char *filename, const char *packname ){
501 char tmp[NAME_MAX], fixed[NAME_MAX];
504 byte *bufferptr = NULL;
505 strcpy( fixed, filename );
506 vfsFixDOSName( fixed );
509 for ( i = 0; i < g_numDirs; i++ )
511 strcpy( tmp, g_strDirs[i] );
512 strcat( tmp, filename );
513 if ( access( tmp, R_OK ) == 0 ) {
514 if ( access( packname, R_OK ) == 0 ) {
516 memset( &zip, 0, sizeof(zip) );
517 mz_zip_reader_init_file( &zip, packname, 0 );
518 mz_zip_writer_init_from_reader( &zip, packname );
520 mz_bool success = MZ_TRUE;
521 success &= mz_zip_writer_add_file( &zip, filename, tmp, 0, 0, 10 );
522 if ( !success || !mz_zip_writer_finalize_archive( &zip ) ){
523 Error( "Failed creating zip archive \"%s\"!\n", packname );
525 mz_zip_reader_end( &zip);
526 mz_zip_writer_end( &zip );
530 memset( &zip, 0, sizeof(zip) );
531 if( !mz_zip_writer_init_file( &zip, packname, 0 ) ){
532 Error( "Failed creating zip archive \"%s\"!\n", packname );
534 mz_bool success = MZ_TRUE;
535 success &= mz_zip_writer_add_file( &zip, filename, tmp, 0, 0, 10 );
536 if ( !success || !mz_zip_writer_finalize_archive( &zip ) ){
537 Error( "Failed creating zip archive \"%s\"!\n", packname );
539 mz_zip_writer_end( &zip );
546 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
548 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
550 if ( strcmp( file->name, fixed ) != 0 ) {
554 memcpy( file->zipfile, &file->zipinfo, sizeof( unz_s ) );
556 if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
560 bufferptr = safe_malloc( file->size + 1 );
561 // we need to end the buffer with a 0
562 ( (char*) ( bufferptr ) )[file->size] = 0;
564 mz_uint16 DOS_time = (mz_uint16)(((file->zipinfo.cur_file_info.tmu_date.tm_hour) << 11) + ((file->zipinfo.cur_file_info.tmu_date.tm_min) << 5) + ((file->zipinfo.cur_file_info.tmu_date.tm_sec) >> 1));
565 mz_uint16 DOS_date = (mz_uint16)(((file->zipinfo.cur_file_info.tmu_date.tm_year - 1980) << 9) + ((file->zipinfo.cur_file_info.tmu_date.tm_mon + 1) << 5) + file->zipinfo.cur_file_info.tmu_date.tm_mday);
567 i = unzReadCurrentFile( file->zipfile, bufferptr, file->size );
568 unzCloseCurrentFile( file->zipfile );
573 mz_bool success = MZ_TRUE;
574 success &= mz_zip_add_mem_to_archive_file_in_place_with_time( packname, filename, bufferptr, i, 0, 0, 10, DOS_time, DOS_date );
576 Error( "Failed creating zip archive \"%s\"!\n", packname );
585 Error( "Disabled because of miniz issue" );