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>
55 #define GARUX_DISABLE_BAD_MINIZ
56 #ifndef GARUX_DISABLE_BAD_MINIZ
69 // =============================================================================
72 static GSList* g_unzFiles;
73 static GSList* g_pakFiles;
74 static char g_strDirs[VFS_MAXDIRS][PATH_MAX + 1];
76 char g_strForbiddenDirs[VFS_MAXDIRS][PATH_MAX + 1];
77 int g_numForbiddenDirs = 0;
78 static gboolean g_bUsePak = TRUE;
79 char g_strLoadedFileLocation[1024];
81 // =============================================================================
84 static void vfsAddSlash( char *str ){
85 int n = strlen( str );
87 if ( str[n - 1] != '\\' && str[n - 1] != '/' ) {
93 static void vfsFixDOSName( char *src ){
100 if ( *src == '\\' ) {
107 //!\todo Define globally or use heap-allocated string.
110 static void vfsInitPakFile( const char *filename ){
116 uf = unzOpen( filename );
121 g_unzFiles = g_slist_append( g_unzFiles, uf );
123 err = unzGetGlobalInfo( uf,&gi );
124 if ( err != UNZ_OK ) {
127 unzGoToFirstFile( uf );
129 char* unzFilePath = strdup( filename );
131 for ( i = 0; i < gi.number_entry; i++ )
133 char filename_inzip[NAME_MAX];
134 char *filename_lower;
135 unz_file_info file_info;
138 err = unzGetCurrentFileInfo( uf, &file_info, filename_inzip, sizeof( filename_inzip ), NULL, 0, NULL, 0 );
139 if ( err != UNZ_OK ) {
143 err = unzGetFilePos( uf, &pos );
144 if ( err != UNZ_OK ) {
148 file = (VFS_PAKFILE*)safe_malloc( sizeof( VFS_PAKFILE ) );
149 g_pakFiles = g_slist_append( g_pakFiles, file );
151 vfsFixDOSName( filename_inzip );
152 //-1 null terminated string
153 filename_lower = g_ascii_strdown( filename_inzip, -1 );
155 file->name = strdup( filename_lower );
156 file->size = file_info.uncompressed_size;
158 file->unzFilePath = unzFilePath;
161 if ( ( i + 1 ) < gi.number_entry ) {
162 err = unzGoToNextFile( uf );
163 if ( err != UNZ_OK ) {
167 g_free( filename_lower );
171 // =============================================================================
174 // reads all pak files from a dir
175 void vfsInitDirectory( const char *path ){
176 char filename[PATH_MAX];
181 for ( j = 0; j < g_numForbiddenDirs; ++j )
183 char* dbuf = g_strdup( path );
184 if ( *dbuf && dbuf[strlen( dbuf ) - 1] == '/' ) {
185 dbuf[strlen( dbuf ) - 1] = 0;
187 const char *p = strrchr( dbuf, '/' );
188 p = ( p ? ( p + 1 ) : dbuf );
189 if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) {
195 if ( j < g_numForbiddenDirs ) {
199 if ( g_numDirs == VFS_MAXDIRS ) {
203 Sys_Printf( "VFS Init: %s\n", path );
205 strncpy( g_strDirs[g_numDirs], path, PATH_MAX );
206 g_strDirs[g_numDirs][PATH_MAX] = 0;
207 vfsFixDOSName( g_strDirs[g_numDirs] );
208 vfsAddSlash( g_strDirs[g_numDirs] );
212 dir = g_dir_open( path, 0, NULL );
217 const char* name = g_dir_read_name( dir );
218 if ( name == NULL ) {
222 for ( j = 0; j < g_numForbiddenDirs; ++j )
224 const char *p = strrchr( name, '/' );
225 p = ( p ? ( p + 1 ) : name );
226 if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) {
230 if ( j < g_numForbiddenDirs ) {
234 dirlist = g_strdup( name );
237 char *ext = strrchr( dirlist, '.' );
239 if ( ext != NULL && ( !Q_stricmp( ext, ".pk3dir" ) || !Q_stricmp( ext, ".dpkdir" ) ) ) {
240 if ( g_numDirs == VFS_MAXDIRS ) {
243 snprintf( g_strDirs[g_numDirs], PATH_MAX, "%s/%s", path, name );
244 g_strDirs[g_numDirs][PATH_MAX-1] = '\0';
245 vfsFixDOSName( g_strDirs[g_numDirs] );
246 vfsAddSlash( g_strDirs[g_numDirs] );
250 if ( ext == NULL || ( Q_stricmp( ext, ".pk3" ) != 0 && Q_stricmp( ext, ".dpk" ) != 0 ) ) {
255 sprintf( filename, "%s/%s", path, dirlist );
256 vfsInitPakFile( filename );
266 // lists all .shader files
267 void vfsListShaderFiles( char* list, int *num ){
268 //char filename[PATH_MAX];
274 for ( i = 0; i < g_numDirs; i++ ){
275 strncpy( path, g_strDirs[ i ], NAME_MAX );
276 strcat( path, "scripts/" );
278 dir = g_dir_open( path, 0, NULL );
283 const char* name = g_dir_read_name( dir );
284 if ( name == NULL ) {
287 dirlist = g_strdup( name );
288 char *ext = strrchr( dirlist, '.' );
290 if ( ( ext == NULL ) || ( Q_stricmp( ext, ".shader" ) != 0 ) ) {
294 for ( k = 0; k < *num; k++ ){
295 if ( !Q_stricmp( list + k*65, dirlist ) ) goto shISdouplicate;
297 strcpy( list + (*num)*65, dirlist );
305 /* search in packs */
308 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
310 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
312 char *ext = strrchr( file->name, '.' );
314 if ( ( ext == NULL ) || ( Q_stricmp( ext, ".shader" ) != 0 ) ) {
317 //name + ext this time
318 ext = strrchr( file->name, '/' );
321 for ( k = 0; k < *num; k++ ){
322 if ( !Q_stricmp( list + k*65, ext ) ) goto shISdouplicate2;
324 strcpy( list + (*num)*65, ext );
331 // frees all memory that we allocated
335 unzClose( (unzFile)g_unzFiles->data );
336 g_unzFiles = g_slist_remove( g_unzFiles, g_unzFiles->data );
341 VFS_PAKFILE* file = (VFS_PAKFILE*)g_pakFiles->data;
342 free( file->unzFilePath );
345 g_pakFiles = g_slist_remove( g_pakFiles, file );
349 // return the number of files that match
350 int vfsGetFileCount( const char *filename ){
352 char fixed[NAME_MAX], tmp[NAME_MAX];
356 strcpy( fixed, filename );
357 vfsFixDOSName( fixed );
358 lower = g_ascii_strdown( fixed, -1 );
360 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
362 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
364 if ( strcmp( file->name, lower ) == 0 ) {
369 for ( i = 0; i < g_numDirs; i++ )
371 strcpy( tmp, g_strDirs[i] );
372 strcat( tmp, lower );
373 if ( access( tmp, R_OK ) == 0 ) {
381 // NOTE: when loading a file, you have to allocate one extra byte and set it to \0
382 int vfsLoadFile( const char *filename, void **bufferptr, int index ){
384 char tmp[NAME_MAX], fixed[NAME_MAX];
388 // filename is a full path
390 strcpy( g_strLoadedFileLocation, filename );
394 f = fopen( filename, "rb" );
399 fseek( f, 0, SEEK_END );
403 *bufferptr = safe_malloc( len + 1 );
404 if ( *bufferptr == NULL ) {
409 if ( fread( *bufferptr, 1, len, f ) != (size_t) len ) {
415 // we need to end the buffer with a 0
416 ( (char*) ( *bufferptr ) )[len] = 0;
422 strcpy( fixed, filename );
423 vfsFixDOSName( fixed );
424 lower = g_ascii_strdown( fixed, -1 );
426 for ( i = 0; i < g_numDirs; i++ )
428 strcpy( tmp, g_strDirs[i] );
429 strcat( tmp, filename );
430 if ( access( tmp, R_OK ) == 0 ) {
431 if ( count == index ) {
432 strcpy( g_strLoadedFileLocation, tmp );
437 f = fopen( tmp, "rb" );
442 fseek( f, 0, SEEK_END );
446 *bufferptr = safe_malloc( len + 1 );
447 if ( *bufferptr == NULL ) {
452 if ( fread( *bufferptr, 1, len, f ) != (size_t) len ) {
458 // we need to end the buffer with a 0
459 ( (char*) ( *bufferptr ) )[len] = 0;
468 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
470 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
472 if ( strcmp( file->name, lower ) != 0 ) {
476 if ( count == index ) {
477 strcpy( g_strLoadedFileLocation, file->unzFilePath );
478 strcat( g_strLoadedFileLocation, " :: " );
479 strcat( g_strLoadedFileLocation, filename );
482 if ( unzGoToFilePos( file->zipfile, &file->zippos ) != UNZ_OK ) {
485 if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
489 *bufferptr = safe_malloc( file->size + 1 );
490 // we need to end the buffer with a 0
491 ( (char*) ( *bufferptr ) )[file->size] = 0;
493 i = unzReadCurrentFile( file->zipfile, *bufferptr, file->size );
494 unzCloseCurrentFile( file->zipfile );
511 qboolean vfsPackFile( const char *filename, const char *packname, const int compLevel ){
512 #ifndef GARUX_DISABLE_BAD_MINIZ
514 char tmp[NAME_MAX], fixed[NAME_MAX];
517 byte *bufferptr = NULL;
518 strcpy( fixed, filename );
519 vfsFixDOSName( fixed );
522 for ( i = 0; i < g_numDirs; i++ )
524 strcpy( tmp, g_strDirs[i] );
525 strcat( tmp, filename );
526 if ( access( tmp, R_OK ) == 0 ) {
527 if ( access( packname, R_OK ) == 0 ) {
529 memset( &zip, 0, sizeof(zip) );
530 mz_zip_reader_init_file( &zip, packname, 0 );
531 mz_zip_writer_init_from_reader( &zip, packname );
533 mz_bool success = MZ_TRUE;
534 success &= mz_zip_writer_add_file( &zip, filename, tmp, 0, 0, compLevel );
535 if ( !success || !mz_zip_writer_finalize_archive( &zip ) ){
536 Error( "Failed creating zip archive \"%s\"!\n", packname );
538 mz_zip_reader_end( &zip);
539 mz_zip_writer_end( &zip );
543 memset( &zip, 0, sizeof(zip) );
544 if( !mz_zip_writer_init_file( &zip, packname, 0 ) ){
545 Error( "Failed creating zip archive \"%s\"!\n", packname );
547 mz_bool success = MZ_TRUE;
548 success &= mz_zip_writer_add_file( &zip, filename, tmp, 0, 0, compLevel );
549 if ( !success || !mz_zip_writer_finalize_archive( &zip ) ){
550 Error( "Failed creating zip archive \"%s\"!\n", packname );
552 mz_zip_writer_end( &zip );
559 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
561 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
563 if ( strcmp( file->name, fixed ) != 0 ) {
567 memcpy( file->zipfile, &file->zipinfo, sizeof( unz_s ) );
569 if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
573 bufferptr = safe_malloc( file->size + 1 );
574 // we need to end the buffer with a 0
575 ( (char*) ( bufferptr ) )[file->size] = 0;
577 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));
578 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);
580 i = unzReadCurrentFile( file->zipfile, bufferptr, file->size );
581 unzCloseCurrentFile( file->zipfile );
586 mz_bool success = MZ_TRUE;
587 success &= mz_zip_add_mem_to_archive_file_in_place_with_time( packname, filename, bufferptr, i, 0, 0, compLevel, DOS_time, DOS_date );
589 Error( "Failed creating zip archive \"%s\"!\n", packname );
598 Error( "Disabled because of miniz issue" );
602 qboolean vfsPackFile_Absolute_Path( const char *filepath, const char *filename, const char *packname, const int compLevel ){
603 #ifndef GARUX_DISABLE_BAD_MINIZ
605 strcpy( tmp, filepath );
606 if ( access( tmp, R_OK ) == 0 ) {
607 if ( access( packname, R_OK ) == 0 ) {
609 memset( &zip, 0, sizeof(zip) );
610 mz_zip_reader_init_file( &zip, packname, 0 );
611 mz_zip_writer_init_from_reader( &zip, packname );
613 mz_bool success = MZ_TRUE;
614 success &= mz_zip_writer_add_file( &zip, filename, tmp, 0, 0, compLevel );
615 if ( !success || !mz_zip_writer_finalize_archive( &zip ) ){
616 Error( "Failed creating zip archive \"%s\"!\n", packname );
618 mz_zip_reader_end( &zip);
619 mz_zip_writer_end( &zip );
623 memset( &zip, 0, sizeof(zip) );
624 if( !mz_zip_writer_init_file( &zip, packname, 0 ) ){
625 Error( "Failed creating zip archive \"%s\"!\n", packname );
627 mz_bool success = MZ_TRUE;
628 success &= mz_zip_writer_add_file( &zip, filename, tmp, 0, 0, compLevel );
629 if ( !success || !mz_zip_writer_finalize_archive( &zip ) ){
630 Error( "Failed creating zip archive \"%s\"!\n", packname );
632 mz_zip_writer_end( &zip );
640 Error( "Disabled because of miniz issue" );