2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 #if defined ( __linux__ ) || defined ( __APPLE__ )
43 struct PACKDirectory pakdir;
44 PACKDirPtr pakdirptr = &pakdir;
48 DIRECTORY *paktextures = NULL;
49 UInt32 PakColormapOffset;
50 UInt32 PakColormapSize;
51 DIRECTORY *dirhead = NULL;
53 char g_strBasePaths[16][1024];
54 int g_numBasePaths = 0;
65 bool operator ==( const PK3FileInfo& rhs ) const { return strcmp( m_pName, rhs.m_pName ) == 0; }
68 #define __PATHSEPERATOR '/'
74 #if defined ( __linux__ ) || defined ( __APPLE__ )
78 #include <sys/types.h>
84 LogFile( const char* pName ){
85 #if defined ( __linux__ ) || defined ( __APPLE__ )
86 // leo: use ~/.q3a/radiant/paklog instead of /tmp/paklog.txt
89 home = getenv( "HOME" );
95 while ( ( pwd = getpwent() ) != NULL )
96 if ( pwd->pw_uid == id ) {
103 if ( home != NULL ) {
105 strcpy( path, home );
106 if ( path[strlen( path ) - 1] != '/' ) {
109 strcat( path, ".q3a/radiant/paklog" );
110 m_pFile = fopen( path, "w" );
114 m_pFile = fopen( pName, "w" );
121 void Log( const char *pFormat, ... ){
122 if ( m_pFile == NULL ) {
127 va_start( arg_ptr, pFormat );
128 fprintf( m_pFile, pFormat, arg_ptr );
133 LogFile g_LogFile( "/tmp/paklog.txt" );
136 template <class T> class StrPtr : public Str
144 StrPtr( const char *pStr, T *p ) : Str( pStr ){
161 template <class T> class PtrList
189 while ( pl && pl->m_pNext )
193 pl->m_pNext = new PtrList( ip );
197 PtrList *p = m_pNext;
199 while ( p->m_pNext != this && p->m_pNext != NULL )
203 if ( p->m_pNext == this ) {
204 p->m_pNext = m_pNext;
209 virtual PtrList* Find( T *ip ){
210 PtrList *p = m_pNext;
213 if ( *p->m_pPtr == *ip ) {
221 // remove vp from the list
222 void Remove( T *ip ){
223 PtrList *p = Find( ip );
238 PtrList *p = m_pNext;
249 typedef PtrList<unzFile> ZFileList;
250 typedef PtrList<Str> StrList;
251 typedef PtrList<PK3FileInfo> PK3List;
254 StrList g_PK3TexturePaths;
257 #define WORK_LEN 1024
258 #define TEXTURE_PATH "textures"
259 #define PATH_SEPERATORS "/\\:\0"
262 char* __StrDup(char* pStr)
267 return strcpy(new char[strlen(pStr)+1], pStr);
270 char* __StrDup(const char* pStr)
275 return strcpy(new char[strlen(pStr)+1], pStr);
279 #define MEM_BLOCKSIZE 4096
280 void* __qblockmalloc( size_t nSize ){
282 // round up to threshold
283 int nAllocSize = nSize % MEM_BLOCKSIZE;
284 if ( nAllocSize > 0 ) {
285 nSize += MEM_BLOCKSIZE - nAllocSize;
287 b = malloc( nSize + 1 );
288 memset( b, 0, nSize );
292 void* __qmalloc( size_t nSize ){
294 b = malloc( nSize + 1 );
295 memset( b, 0, nSize );
305 void __ExtractFilePath( const char *path, char *dest ){
308 src = path + strlen( path ) - 1;
311 // back up until a \ or the start
313 while ( src != path && *( src - 1 ) != __PATHSEPERATOR )
316 memcpy( dest, path, src - path );
317 dest[src - path] = 0;
320 void __ExtractFileName( const char *path, char *dest ){
323 src = path + strlen( path ) - 1;
326 // back up until a \ or the start
328 while ( src != path && *( src - 1 ) != '/'
329 && *( src - 1 ) != '\\' )
339 void __ExtractFileBase( const char *path, char *dest ){
342 src = path + strlen( path ) - 1;
345 // back up until a \ or the start
347 while ( src != path && *( src - 1 ) != '/'
348 && *( src - 1 ) != '\\' )
351 while ( *src && *src != '.' )
358 void __ExtractFileExtension( const char *path, char *dest ){
361 src = path + strlen( path ) - 1;
364 // back up until a . or the start
366 while ( src != path && *( src - 1 ) != '.' )
369 *dest = 0; // no extension
377 void __ConvertDOSToUnixName( char *dst, const char *src ){
380 if ( *src == '\\' ) {
395 static void AddSlash( Str& str ){
396 int nLen = str.GetLength();
398 if ( str[nLen - 1] != '\\' && str[nLen - 1] != '/' ) {
404 static void FindReplace( Str& strContents, const char* pTag, const char* pValue ){
405 if ( strcmp( pTag, pValue ) == 0 ) {
408 for ( int nPos = strContents.Find( pTag ); nPos >= 0; nPos = strContents.Find( pTag ) )
410 int nRightLen = strContents.GetLength() - strlen( pTag ) - nPos;
411 Str strLeft( strContents.Left( nPos ) );
412 Str strRight( strContents.Right( nRightLen ) );
415 strContents = strLeft;
423 void ClearFileList( FILELIST **list ){
429 *list = ( *list )->next;
434 void ClearDirList( DIRLIST **list ){
440 *list = ( *list )->next;
445 DIRECTORY *FindPakDir( DIRECTORY *dir, char *name ){
446 DIRECTORY *currentPtr;
448 for ( currentPtr = dir; currentPtr; currentPtr = currentPtr->next )
450 if ( !stricmp( name, currentPtr->name ) ) {
461 // This gets passed a file mask which we want to remove as
462 // we are only interested in the directory name and any given
463 // extension. Only handles explicit filenames or *.something
465 bool LoadPK3FileList( FILELIST **filelist, const char *pattern ){
466 char cSearch[WORK_LEN];
467 __ConvertDOSToUnixName( cSearch, pattern );
468 char cPath[WORK_LEN];
470 char cFile[WORK_LEN];
471 char cWork[WORK_LEN];
472 __ExtractFilePath( pattern, cPath );
473 __ExtractFileName( pattern, cFile );
474 __ExtractFileExtension( pattern, cExt );
475 const char *pCompare = ( strnicmp( cFile, "*.", 2 ) == 0 ) ? cExt : cFile;
477 PK3List *p = g_PK3Files.Next();
481 PK3FileInfo *pKey = p->Ptr();
482 if ( strstr( pKey->m_pName, cPath ) && strstr( pKey->m_pName, pCompare ) ) {
483 __ExtractFileName( pKey->m_pName, cWork );
484 AddToFileListAlphabetized( filelist, cWork, 0, 0, false );
488 return ( *filelist ) != NULL;
491 bool GetPackFileList( FILELIST **filelist, char *pattern ){
494 DIRECTORY *dummy = paktextures;
502 return LoadPK3FileList( filelist, pattern );
507 for ( i = 0; pattern[i] != '\0'; i++ )
509 if ( pattern[i] == '\\' ) {
514 while ( strchr( str1, '/' ) )
516 str2 = strchr( str1, '/' );
518 dummy = FindPakDir( dummy, str1 );
524 for ( temp = dummy->files; temp; temp = temp->next )
526 AddToFileListAlphabetized( filelist, temp->filename, temp->offset, 0, false );
531 bool GetPackTextureDirs( DIRLIST **dirlist ){
540 StrList *pl = g_PK3TexturePaths.Next();
543 AddToDirListAlphabetized( dirlist, pl->Ref(), 0 );
549 for ( i = 0; i < dirsize; i++ )
551 if ( !strnicmp( pakdirptr[i].name, "textures", 8 ) ) {
552 strncpy( buf, &( pakdirptr[i].name[9] ), 46 );
553 if ( strchr( buf, '\\' ) ) {
554 *strchr( buf, '\\' ) = '\0';
556 else if ( strchr( buf, '/' ) ) {
557 *strchr( buf, '/' ) = '\0';
563 if ( strchr( buf, '.' ) ) {
567 AddToDirListAlphabetized( dirlist, buf, 0 );
573 bool AddToDirListAlphabetized( DIRLIST **list, char *dirname, int from ){
574 DIRLIST *currentPtr, *previousPtr, *newPtr;
577 for ( currentPtr = *list; currentPtr; currentPtr = currentPtr->next )
579 if ( !stricmp( dirname, currentPtr->dirname ) ) {
586 if ( ( newPtr = (DIRLIST *)__qmalloc( sizeof( DIRLIST ) ) ) == NULL ) {
590 strcpy( newPtr->dirname, dirname );
593 while ( currentPtr != NULL && stricmp( dirname, currentPtr->dirname ) > 0 )
595 previousPtr = currentPtr;
596 currentPtr = currentPtr->next;
598 if ( previousPtr == NULL ) {
599 newPtr->next = *list;
604 previousPtr->next = newPtr;
605 newPtr->next = currentPtr;
610 bool AddToFileListAlphabetized( FILELIST **list, char *filename, UInt32 offset, UInt32 size, bool dirs ){
611 FILELIST *currentPtr, *previousPtr, *newPtr;
613 for ( currentPtr = *list; currentPtr; currentPtr = currentPtr->next )
615 if ( !stricmp( filename, currentPtr->filename ) ) {
622 if ( ( newPtr = (FILELIST *)__qmalloc( sizeof( FILELIST ) ) ) == NULL ) {
626 strcpy( newPtr->filename, filename );
627 newPtr->offset = offset;
630 while ( currentPtr != NULL && stricmp( filename, currentPtr->filename ) > 0 )
632 previousPtr = currentPtr;
633 currentPtr = currentPtr->next;
635 if ( previousPtr == NULL ) {
636 newPtr->next = *list;
641 previousPtr->next = newPtr;
642 newPtr->next = currentPtr;
647 int PakLoadAnyFile( const char *filename, void **bufferptr ){
648 char cWork[WORK_LEN];
650 // leo: hack to be able to use pak files from multiple directories
651 for ( int i = 0; i < g_numBasePaths; i++ )
655 // need to lookup the file without the base/texture path on it
656 Str strBase( g_strBasePaths[i] );
658 __ConvertDOSToUnixName( cWork, strBase );
659 Str strFile( filename );
660 __ConvertDOSToUnixName( strFile, strFile );
663 FindReplace( strFile, cWork, "" );
665 PK3FileInfo infoFind;
666 infoFind.m_pName = __StrDup( strFile.GetBuffer() );
667 PK3List *pList = g_PK3Files.Find( &infoFind );
669 pInfo = pList->Ptr();
670 memcpy( pInfo->m_zFile, &pInfo->m_zInfo, sizeof( unz_s ) );
671 if ( unzOpenCurrentFile( pInfo->m_zFile ) == UNZ_OK ) {
672 void *buffer = __qblockmalloc( pInfo->m_lSize + 1 );
673 int n = unzReadCurrentFile( pInfo->m_zFile, buffer, pInfo->m_lSize );
675 unzCloseCurrentFile( pInfo->m_zFile );
682 sprintf( cWork, "PAK failed on %s\n", filename );
683 g_LogFile.Log( cWork );
688 for ( int i = 0; i < dirsize; i++ )
690 if ( !stricmp( filename, pakdirptr[i].name ) ) {
691 if ( fseek( pakfile[m_nPAKIndex], pakdirptr[i].offset, SEEK_SET ) >= 0 ) {
692 void *buffer = __qmalloc( pakdirptr[i].size + 1 );
693 ( (char *)buffer )[pakdirptr[i].size] = 0;
694 if ( fread( buffer, 1, pakdirptr[i].size, pakfile[m_nPAKIndex] ) == pakdirptr[i].size ) {
696 return pakdirptr[i].size;
702 sprintf( cWork, "PAK failed on %s\n", filename );
703 g_LogFile.Log( cWork );
710 DIRECTORY *AddPakDir( DIRECTORY **dir, char *name ){
711 DIRECTORY *currentPtr, *previousPtr, *newPtr;
713 for ( currentPtr = *dir; currentPtr; currentPtr = currentPtr->next )
715 if ( !stricmp( name, currentPtr->name ) ) {
722 if ( ( newPtr = (DIRECTORY *)__qmalloc( sizeof( DIRECTORY ) ) ) == NULL ) {
726 strcpy( newPtr->name, name );
727 newPtr->files = NULL;
729 while ( currentPtr != NULL && stricmp( name, currentPtr->name ) > 0 )
731 previousPtr = currentPtr;
732 currentPtr = currentPtr->next;
734 if ( previousPtr == NULL ) {
740 previousPtr->next = newPtr;
741 newPtr->next = currentPtr;
749 // Opens a PK3 ( or zip ) file and creates a list of filenames
750 // and zip info structures
752 bool OpenPK3( const char *filename ){
753 char cFilename[WORK_LEN];
754 char cName[WORK_LEN];
755 char cWork[WORK_LEN];
757 unzFile *zFile = new unzFile( unzOpen( filename ) );
758 g_zFiles.Add( zFile );
759 if ( zFile != NULL ) {
760 int nStatus = unzGoToFirstFile( *zFile );
761 while ( nStatus == UNZ_OK )
764 unzGetCurrentFileInfo( *zFile, &zInfo, cFilename, WORK_LEN, NULL, 0, NULL, 0 );
766 __ConvertDOSToUnixName( cWork, cFilename );
767 if ( strstr( cWork, "." ) != NULL ) {
768 PK3FileInfo *pInfo = new PK3FileInfo();
769 pInfo->m_pName = __StrDup( cWork );
770 memcpy( &pInfo->m_zInfo, (unz_s*)*zFile, sizeof( unz_s ) );
771 pInfo->m_lSize = zInfo.uncompressed_size;
772 pInfo->m_zFile = *zFile;
773 g_PK3Files.Add( pInfo );
775 char *p = strstr( cFilename, TEXTURE_PATH );
777 // FIXME: path differences per os ?
778 // catch solo directory entry
779 if ( strlen( p ) > strlen( TEXTURE_PATH ) + 1 ) {
780 // skip textures + path seperator
781 p += strlen( TEXTURE_PATH ) + 1;
782 int nEnd = strcspn( p, PATH_SEPERATORS );
783 strncpy( cName, p, nEnd );
787 StrList *pl = g_PK3TexturePaths.Next();
790 if ( strcmpi( pl->Ref(), cName ) == 0 ) {
791 // already have this, continue
798 g_PK3TexturePaths.Add( new Str( cName ) );
802 nStatus = unzGoToNextFile( *zFile );
805 return ( zFile != NULL );
808 void closePK3( unzFile zf ){
812 void OpenPakFile( const char *filename ){
817 pakopen = g_bPK3 = OpenPK3( filename );
820 void ClearPaKDir( DIRECTORY **dir ){
821 DIRECTORY *d1 = *dir, *d2;
825 ClearFileList( &( d1->files ) );
832 void CleanUpPakDirs(){
833 ClearPaKDir( &paktextures );
836 g_PK3TexturePaths.RemoveAll();
837 g_PK3Files.RemoveAll();
840 void ClosePakFile( void ){
843 ZFileList *p = g_zFiles.Next();
846 unzFile uz = p->Ref();
853 fclose( pakfile[m_nPAKIndex] );
861 void WINAPI InitPakFile( const char * pBasePath, const char *pName ){
862 if ( g_numBasePaths == 0 ) {
867 strcpy( g_strBasePaths[g_numBasePaths], pBasePath );
870 if ( pName == NULL ) {
871 //++timo FIXME: use some kind of compatibility lib here!
872 #if defined ( __linux__ ) || defined ( __APPLE__ )
873 char cWork[WORK_LEN];
874 struct dirent *dirlist;
877 dir = opendir( pBasePath );
879 while ( ( dirlist = readdir( dir ) ) != NULL )
881 if ( strstr( dirlist->d_name, ".pk3" ) == NULL ) {
884 sprintf( cWork, "%s/%s", pBasePath, dirlist->d_name );
885 OpenPakFile( cWork );
891 char cWork[WORK_LEN];
892 Str strPath( pBasePath );
896 struct _finddata_t fileinfo;
897 int handle = _findfirst( strPath, &fileinfo );
898 if ( handle != -1 ) {
901 sprintf( cWork, "%s/%s", pBasePath, fileinfo.name );
902 OpenPakFile( cWork );
903 } while ( _findnext( handle, &fileinfo ) != -1 );
904 _findclose( handle );
910 OpenPakFile( pName );