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"
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 file = (VFS_PAKFILE*)safe_malloc( sizeof( VFS_PAKFILE ) );
135 g_pakFiles = g_slist_append( g_pakFiles, file );
137 vfsFixDOSName( filename_inzip );
138 g_strdown( filename_inzip );
140 file->name = strdup( filename_inzip );
141 file->size = file_info.uncompressed_size;
143 memcpy( &file->zipinfo, uf, sizeof( unz_s ) );
145 if ( ( i + 1 ) < gi.number_entry ) {
146 err = unzGoToNextFile( uf );
147 if ( err != UNZ_OK ) {
154 // =============================================================================
157 // reads all pak files from a dir
158 void vfsInitDirectory( const char *path ){
159 char filename[PATH_MAX];
164 for ( j = 0; j < g_numForbiddenDirs; ++j )
166 char* dbuf = g_strdup( path );
167 if ( *dbuf && dbuf[strlen( dbuf ) - 1] == '/' ) {
168 dbuf[strlen( dbuf ) - 1] = 0;
170 const char *p = strrchr( dbuf, '/' );
171 p = ( p ? ( p + 1 ) : dbuf );
172 if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) {
178 if ( j < g_numForbiddenDirs ) {
182 if ( g_numDirs == VFS_MAXDIRS ) {
186 Sys_Printf( "VFS Init: %s\n", path );
188 strncpy( g_strDirs[g_numDirs], path, PATH_MAX );
189 g_strDirs[g_numDirs][PATH_MAX] = 0;
190 vfsFixDOSName( g_strDirs[g_numDirs] );
191 vfsAddSlash( g_strDirs[g_numDirs] );
195 dir = g_dir_open( path, 0, NULL );
200 const char* name = g_dir_read_name( dir );
201 if ( name == NULL ) {
205 for ( j = 0; j < g_numForbiddenDirs; ++j )
207 const char *p = strrchr( name, '/' );
208 p = ( p ? ( p + 1 ) : name );
209 if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) {
213 if ( j < g_numForbiddenDirs ) {
217 dirlist = g_strdup( name );
220 char *ext = strrchr( dirlist, '.' );
222 if ( ext && !Q_stricmp( ext, ".pk3dir" ) ) {
223 if ( g_numDirs == VFS_MAXDIRS ) {
226 snprintf( g_strDirs[g_numDirs], PATH_MAX, "%s/%s", path, name );
227 g_strDirs[g_numDirs][PATH_MAX] = '\0';
228 vfsFixDOSName( g_strDirs[g_numDirs] );
229 vfsAddSlash( g_strDirs[g_numDirs] );
233 if ( ( ext == NULL ) || ( Q_stricmp( ext, ".pk3" ) != 0 ) ) {
238 sprintf( filename, "%s/%s", path, dirlist );
239 vfsInitPakFile( filename );
249 // lists all .shader files
250 void vfsListShaderFiles( char* list, int *num ){
251 //char filename[PATH_MAX];
257 for ( i = 0; i < g_numDirs; i++ ){
258 strncpy( path, g_strDirs[ i ], NAME_MAX );
259 strcat( path, "scripts/" );
261 dir = g_dir_open( path, 0, NULL );
266 const char* name = g_dir_read_name( dir );
267 if ( name == NULL ) {
270 dirlist = g_strdup( name );
271 char *ext = strrchr( dirlist, '.' );
273 if ( ( ext == NULL ) || ( Q_stricmp( ext, ".shader" ) != 0 ) ) {
277 for ( k = 0; k < *num; k++ ){
278 if ( !stricmp( list + k*65, dirlist ) ) goto shISdouplicate;
280 strcpy( list + (*num)*65, dirlist );
288 /* search in packs */
291 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
293 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
295 char *ext = strrchr( file->name, '.' );
297 if ( ( ext == NULL ) || ( Q_stricmp( ext, ".shader" ) != 0 ) ) {
300 //name + ext this time
301 ext = strrchr( file->name, '/' );
304 for ( k = 0; k < *num; k++ ){
305 if ( !stricmp( list + k*65, ext ) ) goto shISdouplicate2;
307 strcpy( list + (*num)*65, ext );
314 // frees all memory that we allocated
318 unzClose( (unzFile)g_unzFiles->data );
319 g_unzFiles = g_slist_remove( g_unzFiles, g_unzFiles->data );
324 VFS_PAKFILE* file = (VFS_PAKFILE*)g_pakFiles->data;
327 g_pakFiles = g_slist_remove( g_pakFiles, file );
331 // return the number of files that match
332 int vfsGetFileCount( const char *filename ){
334 char fixed[NAME_MAX], tmp[NAME_MAX];
337 strcpy( fixed, filename );
338 vfsFixDOSName( fixed );
341 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
343 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
345 if ( strcmp( file->name, fixed ) == 0 ) {
350 for ( i = 0; i < g_numDirs; i++ )
352 strcpy( tmp, g_strDirs[i] );
353 strcat( tmp, fixed );
354 if ( access( tmp, R_OK ) == 0 ) {
362 // NOTE: when loading a file, you have to allocate one extra byte and set it to \0
363 int vfsLoadFile( const char *filename, void **bufferptr, int index ){
365 char tmp[NAME_MAX], fixed[NAME_MAX];
368 // filename is a full path
373 f = fopen( filename, "rb" );
378 fseek( f, 0, SEEK_END );
382 *bufferptr = safe_malloc( len + 1 );
383 if ( *bufferptr == NULL ) {
388 if ( fread( *bufferptr, 1, len, f ) != (size_t) len ) {
394 // we need to end the buffer with a 0
395 ( (char*) ( *bufferptr ) )[len] = 0;
401 strcpy( fixed, filename );
402 vfsFixDOSName( fixed );
405 for ( i = 0; i < g_numDirs; i++ )
407 strcpy( tmp, g_strDirs[i] );
408 strcat( tmp, filename );
409 if ( access( tmp, R_OK ) == 0 ) {
410 if ( count == index ) {
414 f = fopen( tmp, "rb" );
419 fseek( f, 0, SEEK_END );
423 *bufferptr = safe_malloc( len + 1 );
424 if ( *bufferptr == NULL ) {
429 if ( fread( *bufferptr, 1, len, f ) != (size_t) len ) {
435 // we need to end the buffer with a 0
436 ( (char*) ( *bufferptr ) )[len] = 0;
445 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
447 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
449 if ( strcmp( file->name, fixed ) != 0 ) {
453 if ( count == index ) {
454 memcpy( file->zipfile, &file->zipinfo, sizeof( unz_s ) );
456 if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
460 *bufferptr = safe_malloc( file->size + 1 );
461 // we need to end the buffer with a 0
462 ( (char*) ( *bufferptr ) )[file->size] = 0;
464 i = unzReadCurrentFile( file->zipfile, *bufferptr, file->size );
465 unzCloseCurrentFile( file->zipfile );
483 qboolean vfsPackFile( const char *filename, const char *packname ){
485 char tmp[NAME_MAX], fixed[NAME_MAX];
488 byte *bufferptr = NULL;
489 strcpy( fixed, filename );
490 vfsFixDOSName( fixed );
493 for ( i = 0; i < g_numDirs; i++ )
495 strcpy( tmp, g_strDirs[i] );
496 strcat( tmp, filename );
497 if ( access( tmp, R_OK ) == 0 ) {
498 if ( access( packname, R_OK ) == 0 ) {
500 memset( &zip, 0, sizeof(zip) );
501 mz_zip_reader_init_file( &zip, packname, 0 );
502 mz_zip_writer_init_from_reader( &zip, packname );
504 mz_bool success = MZ_TRUE;
505 success &= mz_zip_writer_add_file( &zip, filename, tmp, 0, 0, 10 );
506 if ( !success || !mz_zip_writer_finalize_archive( &zip ) ){
507 Error( "Failed creating zip archive \"%s\"!\n", packname );
509 mz_zip_reader_end( &zip);
510 mz_zip_writer_end( &zip );
514 memset( &zip, 0, sizeof(zip) );
515 if( !mz_zip_writer_init_file( &zip, packname, 0 ) ){
516 Error( "Failed creating zip archive \"%s\"!\n", packname );
518 mz_bool success = MZ_TRUE;
519 success &= mz_zip_writer_add_file( &zip, filename, tmp, 0, 0, 10 );
520 if ( !success || !mz_zip_writer_finalize_archive( &zip ) ){
521 Error( "Failed creating zip archive \"%s\"!\n", packname );
523 mz_zip_writer_end( &zip );
530 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
532 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
534 if ( strcmp( file->name, fixed ) != 0 ) {
538 memcpy( file->zipfile, &file->zipinfo, sizeof( unz_s ) );
540 if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
544 bufferptr = safe_malloc( file->size + 1 );
545 // we need to end the buffer with a 0
546 ( (char*) ( bufferptr ) )[file->size] = 0;
548 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));
549 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);
551 i = unzReadCurrentFile( file->zipfile, bufferptr, file->size );
552 unzCloseCurrentFile( file->zipfile );
557 mz_bool success = MZ_TRUE;
558 success &= mz_zip_add_mem_to_archive_file_in_place_with_time( packname, filename, bufferptr, i, 0, 0, 10, DOS_time, DOS_date );
560 Error( "Failed creating zip archive \"%s\"!\n", packname );