/* =========================================================================== Return to Castle Wolfenstein multiplayer GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”). RTCW MP Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. RTCW MP Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RTCW MP Source Code. If not, see . In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ //=========================================================================== // // Name: l_qfiles.h // Function: - // Programmer: Mr Elusive // Last update: 1999-11-29 // Tab Size: 3 //=========================================================================== #if defined( WIN32 ) | defined( _WIN32 ) #include #include #include #else #include #include #include #endif #include "qbsp.h" //file extensions with their type typedef struct qfile_exttype_s { char *extension; int type; } qfile_exttyp_t; qfile_exttyp_t quakefiletypes[] = { {QFILEEXT_UNKNOWN, QFILETYPE_UNKNOWN}, {QFILEEXT_PAK, QFILETYPE_PAK}, {QFILEEXT_PK3, QFILETYPE_PK3}, {QFILEEXT_SIN, QFILETYPE_PAK}, {QFILEEXT_BSP, QFILETYPE_BSP}, {QFILEEXT_MAP, QFILETYPE_MAP}, {QFILEEXT_MDL, QFILETYPE_MDL}, {QFILEEXT_MD2, QFILETYPE_MD2}, {QFILEEXT_MD3, QFILETYPE_MD3}, {QFILEEXT_WAL, QFILETYPE_WAL}, {QFILEEXT_WAV, QFILETYPE_WAV}, {QFILEEXT_AAS, QFILETYPE_AAS}, {NULL, 0} }; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int QuakeFileExtensionType( char *extension ) { int i; for ( i = 0; quakefiletypes[i].extension; i++ ) { if ( !stricmp( extension, quakefiletypes[i].extension ) ) { return quakefiletypes[i].type; } //end if } //end for return QFILETYPE_UNKNOWN; } //end of the function QuakeFileExtensionType //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *QuakeFileTypeExtension( int type ) { int i; for ( i = 0; quakefiletypes[i].extension; i++ ) { if ( quakefiletypes[i].type == type ) { return quakefiletypes[i].extension; } //end if } //end for return QFILEEXT_UNKNOWN; } //end of the function QuakeFileExtension //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int QuakeFileType( char *filename ) { char ext[_MAX_PATH] = "."; ExtractFileExtension( filename, ext + 1 ); return QuakeFileExtensionType( ext ); } //end of the function QuakeFileTypeFromFileName //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *StringContains( char *str1, char *str2, int casesensitive ) { int len, i, j; len = strlen( str1 ) - strlen( str2 ); for ( i = 0; i <= len; i++, str1++ ) { for ( j = 0; str2[j]; j++ ) { if ( casesensitive ) { if ( str1[j] != str2[j] ) { break; } } //end if else { if ( toupper( str1[j] ) != toupper( str2[j] ) ) { break; } } //end else } //end for if ( !str2[j] ) { return str1; } } //end for return NULL; } //end of the function StringContains //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int FileFilter( char *filter, char *filename, int casesensitive ) { char buf[1024]; char *ptr; int i, found; while ( *filter ) { if ( *filter == '*' ) { filter++; for ( i = 0; *filter; i++ ) { if ( *filter == '*' || *filter == '?' ) { break; } buf[i] = *filter; filter++; } //end for buf[i] = '\0'; if ( strlen( buf ) ) { ptr = StringContains( filename, buf, casesensitive ); if ( !ptr ) { return false; } filename = ptr + strlen( buf ); } //end if } //end if else if ( *filter == '?' ) { filter++; filename++; } //end else if else if ( *filter == '[' && *( filter + 1 ) == '[' ) { filter++; } //end if else if ( *filter == '[' ) { filter++; found = false; while ( *filter && !found ) { if ( *filter == ']' && *( filter + 1 ) != ']' ) { break; } if ( *( filter + 1 ) == '-' && *( filter + 2 ) && ( *( filter + 2 ) != ']' || *( filter + 3 ) == ']' ) ) { if ( casesensitive ) { if ( *filename >= *filter && *filename <= *( filter + 2 ) ) { found = true; } } //end if else { if ( toupper( *filename ) >= toupper( *filter ) && toupper( *filename ) <= toupper( *( filter + 2 ) ) ) { found = true; } } //end else filter += 3; } //end if else { if ( casesensitive ) { if ( *filter == *filename ) { found = true; } } //end if else { if ( toupper( *filter ) == toupper( *filename ) ) { found = true; } } //end else filter++; } //end else } //end while if ( !found ) { return false; } while ( *filter ) { if ( *filter == ']' && *( filter + 1 ) != ']' ) { break; } filter++; } //end while filter++; filename++; } //end else if else { if ( casesensitive ) { if ( *filter != *filename ) { return false; } } //end if else { if ( toupper( *filter ) != toupper( *filename ) ) { return false; } } //end else filter++; filename++; } //end else } //end while return true; } //end of the function FileFilter //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== quakefile_t *FindQuakeFilesInZip( char *zipfile, char *filter ) { unzFile uf; int err; unz_global_info gi; char filename_inzip[MAX_PATH]; unz_file_info file_info; int i; quakefile_t *qfiles, *lastqf, *qf; uf = unzOpen( zipfile ); err = unzGetGlobalInfo( uf, &gi ); if ( err != UNZ_OK ) { return NULL; } unzGoToFirstFile( uf ); qfiles = NULL; lastqf = NULL; for ( i = 0; i < gi.number_entry; i++ ) { err = unzGetCurrentFileInfo( uf, &file_info, filename_inzip, sizeof( filename_inzip ), NULL,0,NULL,0 ); if ( err != UNZ_OK ) { break; } ConvertPath( filename_inzip ); if ( FileFilter( filter, filename_inzip, false ) ) { qf = malloc( sizeof( quakefile_t ) ); if ( !qf ) { Error( "out of memory" ); } memset( qf, 0, sizeof( quakefile_t ) ); strcpy( qf->pakfile, zipfile ); strcpy( qf->filename, zipfile ); strcpy( qf->origname, filename_inzip ); qf->zipfile = true; //memcpy( &buildBuffer[i].zipfileinfo, (unz_s*)uf, sizeof(unz_s)); memcpy( &qf->zipinfo, (unz_s*)uf, sizeof( unz_s ) ); qf->offset = 0; qf->length = file_info.uncompressed_size; qf->type = QuakeFileType( filename_inzip ); //add the file ot the list qf->next = NULL; if ( lastqf ) { lastqf->next = qf; } else { qfiles = qf;} lastqf = qf; } //end if unzGoToNextFile( uf ); } //end for unzClose( uf ); return qfiles; } //end of the function FindQuakeFilesInZip //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== quakefile_t *FindQuakeFilesInPak( char *pakfile, char *filter ) { FILE *fp; dpackheader_t packheader; dsinpackfile_t *packfiles; dpackfile_t *idpackfiles; quakefile_t *qfiles, *lastqf, *qf; int numpackdirs, i; qfiles = NULL; lastqf = NULL; //open the pak file fp = fopen( pakfile, "rb" ); if ( !fp ) { Warning( "can't open pak file %s", pakfile ); return NULL; } //end if //read pak header, check for valid pak id and seek to the dir entries if ( ( fread( &packheader, 1, sizeof( dpackheader_t ), fp ) != sizeof( dpackheader_t ) ) || ( packheader.ident != IDPAKHEADER && packheader.ident != SINPAKHEADER ) || ( fseek( fp, LittleLong( packheader.dirofs ), SEEK_SET ) ) ) { fclose( fp ); Warning( "invalid pak file %s", pakfile ); return NULL; } //end if //if it is a pak file from id software if ( packheader.ident == IDPAKHEADER ) { //number of dir entries in the pak file numpackdirs = LittleLong( packheader.dirlen ) / sizeof( dpackfile_t ); idpackfiles = (dpackfile_t *) malloc( numpackdirs * sizeof( dpackfile_t ) ); if ( !idpackfiles ) { Error( "out of memory" ); } //read the dir entry if ( fread( idpackfiles, sizeof( dpackfile_t ), numpackdirs, fp ) != numpackdirs ) { fclose( fp ); free( idpackfiles ); Warning( "can't read the Quake pak file dir entries from %s", pakfile ); return NULL; } //end if fclose( fp ); //convert to sin pack files packfiles = (dsinpackfile_t *) malloc( numpackdirs * sizeof( dsinpackfile_t ) ); if ( !packfiles ) { Error( "out of memory" ); } for ( i = 0; i < numpackdirs; i++ ) { strcpy( packfiles[i].name, idpackfiles[i].name ); packfiles[i].filepos = LittleLong( idpackfiles[i].filepos ); packfiles[i].filelen = LittleLong( idpackfiles[i].filelen ); } //end for free( idpackfiles ); } //end if else //its a Sin pack file { //number of dir entries in the pak file numpackdirs = LittleLong( packheader.dirlen ) / sizeof( dsinpackfile_t ); packfiles = (dsinpackfile_t *) malloc( numpackdirs * sizeof( dsinpackfile_t ) ); if ( !packfiles ) { Error( "out of memory" ); } //read the dir entry if ( fread( packfiles, sizeof( dsinpackfile_t ), numpackdirs, fp ) != numpackdirs ) { fclose( fp ); free( packfiles ); Warning( "can't read the Sin pak file dir entries from %s", pakfile ); return NULL; } //end if fclose( fp ); for ( i = 0; i < numpackdirs; i++ ) { packfiles[i].filepos = LittleLong( packfiles[i].filepos ); packfiles[i].filelen = LittleLong( packfiles[i].filelen ); } //end for } //end else // for ( i = 0; i < numpackdirs; i++ ) { ConvertPath( packfiles[i].name ); if ( FileFilter( filter, packfiles[i].name, false ) ) { qf = malloc( sizeof( quakefile_t ) ); if ( !qf ) { Error( "out of memory" ); } memset( qf, 0, sizeof( quakefile_t ) ); strcpy( qf->pakfile, pakfile ); strcpy( qf->filename, pakfile ); strcpy( qf->origname, packfiles[i].name ); qf->zipfile = false; qf->offset = packfiles[i].filepos; qf->length = packfiles[i].filelen; qf->type = QuakeFileType( packfiles[i].name ); //add the file ot the list qf->next = NULL; if ( lastqf ) { lastqf->next = qf; } else { qfiles = qf;} lastqf = qf; } //end if } //end for free( packfiles ); return qfiles; } //end of the function FindQuakeFilesInPak //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== quakefile_t *FindQuakeFilesWithPakFilter( char *pakfilter, char *filter ) { #if defined( WIN32 ) | defined( _WIN32 ) WIN32_FIND_DATA filedata; HWND handle; struct _stat statbuf; #else glob_t globbuf; struct stat statbuf; int j; #endif quakefile_t *qfiles, *lastqf, *qf; char pakfile[_MAX_PATH], filename[_MAX_PATH], *str; int done; qfiles = NULL; lastqf = NULL; if ( pakfilter && strlen( pakfilter ) ) { #if defined( WIN32 ) | defined( _WIN32 ) handle = FindFirstFile( pakfilter, &filedata ); done = ( handle == INVALID_HANDLE_VALUE ); while ( !done ) { _splitpath( pakfilter, pakfile, NULL, NULL, NULL ); _splitpath( pakfilter, NULL, &pakfile[strlen( pakfile )], NULL, NULL ); AppendPathSeperator( pakfile, _MAX_PATH ); strcat( pakfile, filedata.cFileName ); _stat( pakfile, &statbuf ); #else glob( pakfilter, 0, NULL, &globbuf ); for ( j = 0; j < globbuf.gl_pathc; j++ ) { strcpy( pakfile, globbuf.gl_pathv[j] ); stat( pakfile, &statbuf ); #endif //if the file with .pak or .pk3 is a folder if ( statbuf.st_mode & S_IFDIR ) { strcpy( filename, pakfilter ); AppendPathSeperator( filename, _MAX_PATH ); strcat( filename, filter ); qf = FindQuakeFilesWithPakFilter( NULL, filename ); if ( lastqf ) { lastqf->next = qf; } else { qfiles = qf;} lastqf = qf; while ( lastqf->next ) lastqf = lastqf->next; } //end if else { #if defined( WIN32 ) | defined( _WIN32 ) str = StringContains( pakfile, ".pk3", false ); #else str = StringContains( pakfile, ".pk3", true ); #endif if ( str && str == pakfile + strlen( pakfile ) - strlen( ".pk3" ) ) { qf = FindQuakeFilesInZip( pakfile, filter ); } //end if else { qf = FindQuakeFilesInPak( pakfile, filter ); } //end else // if ( qf ) { if ( lastqf ) { lastqf->next = qf; } else { qfiles = qf;} lastqf = qf; while ( lastqf->next ) lastqf = lastqf->next; } //end if } //end else // #if defined( WIN32 ) | defined( _WIN32 ) //find the next file done = !FindNextFile( handle, &filedata ); } //end while #else } //end for globfree( &globbuf ); #endif } //end if else { #if defined( WIN32 ) | defined( _WIN32 ) handle = FindFirstFile( filter, &filedata ); done = ( handle == INVALID_HANDLE_VALUE ); while ( !done ) { _splitpath( filter, filename, NULL, NULL, NULL ); _splitpath( filter, NULL, &filename[strlen( filename )], NULL, NULL ); AppendPathSeperator( filename, _MAX_PATH ); strcat( filename, filedata.cFileName ); #else glob( filter, 0, NULL, &globbuf ); for ( j = 0; j < globbuf.gl_pathc; j++ ) { strcpy( filename, globbuf.gl_pathv[j] ); #endif // qf = malloc( sizeof( quakefile_t ) ); if ( !qf ) { Error( "out of memory" ); } memset( qf, 0, sizeof( quakefile_t ) ); strcpy( qf->pakfile, "" ); strcpy( qf->filename, filename ); strcpy( qf->origname, filename ); qf->offset = 0; qf->length = 0; qf->type = QuakeFileType( filename ); //add the file ot the list qf->next = NULL; if ( lastqf ) { lastqf->next = qf; } else { qfiles = qf;} lastqf = qf; #if defined( WIN32 ) | defined( _WIN32 ) //find the next file done = !FindNextFile( handle, &filedata ); } //end while #else } //end for globfree( &globbuf ); #endif } //end else return qfiles; } //end of the function FindQuakeFilesWithPakFilter //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== quakefile_t *FindQuakeFiles( char *filter ) { char *str; char newfilter[_MAX_PATH]; char pakfilter[_MAX_PATH]; char filefilter[_MAX_PATH]; strcpy( newfilter, filter ); ConvertPath( newfilter ); strcpy( pakfilter, newfilter ); str = StringContains( pakfilter, ".pak", false ); if ( !str ) { str = StringContains( pakfilter, ".pk3", false ); } if ( str ) { str += strlen( ".pak" ); if ( *str ) { *str++ = '\0'; while ( *str == '\\' || *str == '/' ) str++; strcpy( filefilter, str ); return FindQuakeFilesWithPakFilter( pakfilter, filefilter ); } //end if } //end else return FindQuakeFilesWithPakFilter( NULL, newfilter ); } //end of the function FindQuakeFiles //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int LoadQuakeFile( quakefile_t *qf, void **bufferptr ) { FILE *fp; void *buffer; int length; unzFile zf; if ( qf->zipfile ) { //open the zip file zf = unzOpen( qf->pakfile ); //set the file pointer qf->zipinfo.file = ( (unz_s *) zf )->file; //open the Quake file in the zip file unzOpenCurrentFile( &qf->zipinfo ); //allocate memory for the buffer length = qf->length; buffer = GetMemory( length + 1 ); //read the Quake file from the zip file length = unzReadCurrentFile( &qf->zipinfo, buffer, length ); //close the Quake file in the zip file unzCloseCurrentFile( &qf->zipinfo ); //close the zip file unzClose( zf ); *bufferptr = buffer; return length; } //end if else { fp = SafeOpenRead( qf->filename ); if ( qf->offset ) { fseek( fp, qf->offset, SEEK_SET ); } length = qf->length; if ( !length ) { length = Q_filelength( fp ); } buffer = GetMemory( length + 1 ); ( (char *)buffer )[length] = 0; SafeRead( fp, buffer, length ); fclose( fp ); *bufferptr = buffer; return length; } //end else } //end of the function LoadQuakeFile //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int ReadQuakeFile( quakefile_t *qf, void *buffer, int offset, int length ) { FILE *fp; int read; unzFile zf; char tmpbuf[1024]; if ( qf->zipfile ) { //open the zip file zf = unzOpen( qf->pakfile ); //set the file pointer qf->zipinfo.file = ( (unz_s *) zf )->file; //open the Quake file in the zip file unzOpenCurrentFile( &qf->zipinfo ); // while ( offset > 0 ) { read = offset; if ( read > sizeof( tmpbuf ) ) { read = sizeof( tmpbuf ); } unzReadCurrentFile( &qf->zipinfo, tmpbuf, read ); offset -= read; } //end while //read the Quake file from the zip file length = unzReadCurrentFile( &qf->zipinfo, buffer, length ); //close the Quake file in the zip file unzCloseCurrentFile( &qf->zipinfo ); //close the zip file unzClose( zf ); return length; } //end if else { fp = SafeOpenRead( qf->filename ); if ( qf->offset ) { fseek( fp, qf->offset, SEEK_SET ); } if ( offset ) { fseek( fp, offset, SEEK_CUR ); } SafeRead( fp, buffer, length ); fclose( fp ); return length; } //end else } //end of the function ReadQuakeFile