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)
46 #if defined ( __linux__ ) || defined ( __APPLE__ )
53 #define S_ISDIR( mode ) ( mode & _S_IFDIR )
76 // =============================================================================
79 static GSList* g_unzFiles;
80 static GSList* g_pakFiles;
81 static char g_strDirs[VFS_MAXDIRS][PATH_MAX];
83 static gboolean g_bUsePak = TRUE;
85 // =============================================================================
88 static void vfsAddSlash( char *str ){
89 int n = strlen( str );
91 if ( str[n - 1] != '\\' && str[n - 1] != '/' ) {
97 static void vfsFixDOSName( char *src ){
104 if ( *src == '\\' ) {
111 //!\todo Define globally or use heap-allocated string.
114 static void vfsInitPakFile( const char *filename ){
120 uf = unzOpen( filename );
125 g_unzFiles = g_slist_append( g_unzFiles, uf );
127 err = unzGetGlobalInfo( uf,&gi );
128 if ( err != UNZ_OK ) {
131 unzGoToFirstFile( uf );
133 for ( i = 0; i < gi.number_entry; i++ )
135 char filename_inzip[NAME_MAX];
136 unz_file_info file_info;
139 err = unzGetCurrentFileInfo( uf, &file_info, filename_inzip, sizeof( filename_inzip ), NULL, 0, NULL, 0 );
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 g_strdown( filename_inzip );
150 file->name = strdup( filename_inzip );
151 file->size = file_info.uncompressed_size;
153 memcpy( &file->zipinfo, uf, sizeof( unz_s ) );
155 if ( ( i + 1 ) < gi.number_entry ) {
156 err = unzGoToNextFile( uf );
157 if ( err != UNZ_OK ) {
164 // =============================================================================
167 // reads all pak files from a dir
168 void vfsInitDirectory( const char *path ){
169 char filename[PATH_MAX];
173 if ( g_numDirs == ( VFS_MAXDIRS - 1 ) ) {
177 Sys_Printf( "VFS Init: %s\n", path );
179 strcpy( g_strDirs[g_numDirs], path );
180 vfsFixDOSName( g_strDirs[g_numDirs] );
181 vfsAddSlash( g_strDirs[g_numDirs] );
185 dir = g_dir_open( path, 0, NULL );
190 const char* name = g_dir_read_name( dir );
191 if ( name == NULL ) {
195 dirlist = g_strdup( name );
198 char *ext = strrchr( dirlist, '.' );
199 if ( ( ext == NULL ) || ( Q_stricmp( ext, ".pk3" ) != 0 ) ) {
204 sprintf( filename, "%s/%s", path, dirlist );
205 vfsInitPakFile( filename );
214 // frees all memory that we allocated
218 unzClose( (unzFile)g_unzFiles->data );
219 g_unzFiles = g_slist_remove( g_unzFiles, g_unzFiles->data );
224 VFS_PAKFILE* file = (VFS_PAKFILE*)g_pakFiles->data;
227 g_pakFiles = g_slist_remove( g_pakFiles, file );
231 // return the number of files that match
232 int vfsGetFileCount( const char *filename ){
234 char fixed[NAME_MAX], tmp[NAME_MAX];
237 strcpy( fixed, filename );
238 vfsFixDOSName( fixed );
241 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
243 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
245 if ( strcmp( file->name, fixed ) == 0 ) {
250 for ( i = 0; i < g_numDirs; i++ )
252 strcpy( tmp, g_strDirs[i] );
253 strcat( tmp, fixed );
254 if ( access( tmp, R_OK ) == 0 ) {
262 // NOTE: when loading a file, you have to allocate one extra byte and set it to \0
263 int vfsLoadFile( const char *filename, void **bufferptr, int index ){
265 char tmp[NAME_MAX], fixed[NAME_MAX];
268 // filename is a full path
273 f = fopen( filename, "rb" );
278 fseek( f, 0, SEEK_END );
282 *bufferptr = safe_malloc( len + 1 );
283 if ( *bufferptr == NULL ) {
287 fread( *bufferptr, 1, len, f );
290 // we need to end the buffer with a 0
291 ( (char*) ( *bufferptr ) )[len] = 0;
297 strcpy( fixed, filename );
298 vfsFixDOSName( fixed );
301 for ( i = 0; i < g_numDirs; i++ )
303 strcpy( tmp, g_strDirs[i] );
304 strcat( tmp, filename );
305 if ( access( tmp, R_OK ) == 0 ) {
306 if ( count == index ) {
310 f = fopen( tmp, "rb" );
315 fseek( f, 0, SEEK_END );
319 *bufferptr = safe_malloc( len + 1 );
320 if ( *bufferptr == NULL ) {
324 fread( *bufferptr, 1, len, f );
327 // we need to end the buffer with a 0
328 ( (char*) ( *bufferptr ) )[len] = 0;
337 for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
339 VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
341 if ( strcmp( file->name, fixed ) != 0 ) {
345 if ( count == index ) {
346 memcpy( file->zipfile, &file->zipinfo, sizeof( unz_s ) );
348 if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
352 *bufferptr = safe_malloc( file->size + 1 );
353 // we need to end the buffer with a 0
354 ( (char*) ( *bufferptr ) )[file->size] = 0;
356 i = unzReadCurrentFile( file->zipfile, *bufferptr, file->size );
357 unzCloseCurrentFile( file->zipfile );