]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/pak/pakstuff.cpp
uncrustify! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / libs / pak / pakstuff.cpp
1 /*
2    Copyright (C) 1999-2007 id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
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.
11
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.
16
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
20  */
21
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #if defined ( __linux__ ) || defined ( __APPLE__ )
27 #include <dirent.h>
28 #endif
29 #ifdef _WIN32
30 #include <io.h>
31 #endif
32 #include "pakstuff.h"
33 #include "unzip.h"
34 #include "str.h"
35
36 #ifndef TRUE
37 #define TRUE 1
38 #define FALSE 0
39 #endif
40
41 int m_nPAKIndex;
42 FILE*                   pakfile[16];
43 struct PACKDirectory pakdir;
44 PACKDirPtr pakdirptr = &pakdir;
45 UInt16 dirsize;
46 bool pakopen = false;
47 int f_type;
48 DIRECTORY       *paktextures = NULL;
49 UInt32 PakColormapOffset;
50 UInt32 PakColormapSize;
51 DIRECTORY           *dirhead = NULL;
52 bool g_bPK3 = false;
53 char g_strBasePaths[16][1024];
54 int g_numBasePaths = 0;
55
56 struct PK3FileInfo
57 {
58         unzFile m_zFile;
59         char *m_pName;
60         unz_s m_zInfo;
61         long m_lSize;
62         ~PK3FileInfo(){
63                 delete []m_pName;
64         }
65         bool operator ==( const PK3FileInfo& rhs ) const { return strcmp( m_pName, rhs.m_pName ) == 0; }
66 };
67
68 #define __PATHSEPERATOR   '/'
69
70 //#define LOG_PAKFAIL
71
72 #ifdef LOG_PAKFAIL
73
74 #if defined ( __linux__ ) || defined ( __APPLE__ )
75 #include <unistd.h>
76 #include <pwd.h>
77 #endif
78 #include <sys/types.h>
79
80 class LogFile
81 {
82 public:
83 FILE *m_pFile;
84 LogFile( const char* pName ){
85 #if defined ( __linux__ ) || defined ( __APPLE__ )
86         // leo: use ~/.q3a/radiant/paklog instead of /tmp/paklog.txt
87         char *home = NULL;
88
89         home = getenv( "HOME" );
90         if ( home == NULL ) {
91                 uid_t id = getuid();
92                 struct passwd *pwd;
93
94                 setpwent();
95                 while ( ( pwd = getpwent() ) != NULL )
96                         if ( pwd->pw_uid == id ) {
97                                 home = pwd->pw_dir;
98                                 break;
99                         }
100                 endpwent();
101         }
102
103         if ( home != NULL ) {
104                 char path[PATH_MAX];
105                 strcpy( path, home );
106                 if ( path[strlen( path ) - 1] != '/' ) {
107                         strcat( path, "/" );
108                 }
109                 strcat( path, ".q3a/radiant/paklog" );
110                 m_pFile = fopen( path, "w" );
111         }
112         else
113 #endif
114         m_pFile = fopen( pName, "w" );
115 }
116 ~LogFile(){
117         if ( m_pFile ) {
118                 fclose( m_pFile );
119         }
120 }
121 void Log( const char *pFormat, ... ){
122         if ( m_pFile == NULL ) {
123                 return;
124         }
125
126         va_list arg_ptr;
127         va_start( arg_ptr, pFormat );
128         fprintf( m_pFile, pFormat, arg_ptr );
129         va_end( arg_ptr );
130 }
131 };
132
133 LogFile g_LogFile( "/tmp/paklog.txt" );
134 #endif
135
136 template <class T> class StrPtr : public Str
137 {
138 protected:
139 T* m_pPtr;
140 StrPtr(){
141         m_pPtr = NULL;
142 }
143
144 StrPtr( const char *pStr, T *p ) : Str( pStr ){
145         m_pPtr = p;
146 }
147
148 T* Ptr(){
149         return m_pPtr;
150 }
151
152 T& Ref(){
153         return *m_pPtr;
154 }
155
156
157 };
158 // PtrList
159 // a list of ptrs
160 //
161 template <class T> class PtrList
162 {
163 protected:
164 T *m_pPtr;
165 PtrList *m_pNext;
166
167 public:
168
169 PtrList(){
170         m_pNext = NULL;
171         m_pPtr = NULL;
172 }
173
174 PtrList( T *ip ){
175         m_pNext = NULL;
176         m_pPtr = ip;
177 }
178
179 virtual ~PtrList(){
180         delete m_pPtr;
181 }
182
183 PtrList* Next(){
184         return m_pNext;
185 }
186
187 void Add( T *ip ){
188         PtrList *pl = this;
189         while ( pl && pl->m_pNext )
190         {
191                 pl = pl->Next();
192         }
193         pl->m_pNext = new PtrList( ip );
194 }
195
196 void Remove(){
197         PtrList *p = m_pNext;
198         if ( p ) {
199                 while ( p->m_pNext != this && p->m_pNext != NULL )
200                 {
201                         p = p->m_pNext;
202                 }
203                 if ( p->m_pNext == this ) {
204                         p->m_pNext = m_pNext;
205                 }
206         }
207 }
208
209 virtual PtrList* Find( T *ip ){
210         PtrList *p = m_pNext;
211         while ( p )
212         {
213                 if ( *p->m_pPtr == *ip ) {
214                         return p;
215                 }
216                 p = p->m_pNext;
217         }
218         return NULL;
219 }
220
221 // remove vp from the list
222 void Remove( T *ip ){
223         PtrList *p = Find( ip );
224         if ( p ) {
225                 p->Remove();
226         }
227 }
228
229 T* Ptr(){
230         return m_pPtr;
231 }
232
233 T& Ref(){
234         return *m_pPtr;
235 }
236
237 void RemoveAll(){
238         PtrList *p = m_pNext;
239         while ( p )
240         {
241                 PtrList *p2 = p;
242                 p = p->m_pNext;
243                 delete p2;
244         }
245 }
246 };
247
248
249 typedef PtrList<unzFile> ZFileList;
250 typedef PtrList<Str> StrList;
251 typedef PtrList<PK3FileInfo> PK3List;
252
253
254 StrList g_PK3TexturePaths;
255 PK3List g_PK3Files;
256 ZFileList g_zFiles;
257 #define WORK_LEN 1024
258 #define TEXTURE_PATH "textures"
259 #define PATH_SEPERATORS "/\\:\0"
260
261 /*
262    char* __StrDup(char* pStr)
263    {
264    if (pStr == NULL)
265     pStr = "";
266
267    return strcpy(new char[strlen(pStr)+1], pStr);
268    }
269
270    char* __StrDup(const char* pStr)
271    {
272    if (pStr == NULL)
273     pStr = "";
274
275    return strcpy(new char[strlen(pStr)+1], pStr);
276    }
277  */
278
279 #define MEM_BLOCKSIZE 4096
280 void* __qblockmalloc( size_t nSize ){
281         void *b;
282         // round up to threshold
283         int nAllocSize = nSize % MEM_BLOCKSIZE;
284         if ( nAllocSize > 0 ) {
285                 nSize += MEM_BLOCKSIZE - nAllocSize;
286         }
287         b = malloc( nSize + 1 );
288         memset( b, 0, nSize );
289         return b;
290 }
291
292 void* __qmalloc( size_t nSize ){
293         void *b;
294         b = malloc( nSize + 1 );
295         memset( b, 0, nSize );
296         return b;
297 }
298
299
300 /*
301    ====================
302    Extract file parts
303    ====================
304  */
305 void __ExtractFilePath( const char *path, char *dest ){
306         const char *src;
307
308         src = path + strlen( path ) - 1;
309
310 //
311 // back up until a \ or the start
312 //
313         while ( src != path && *( src - 1 ) != __PATHSEPERATOR )
314                 src--;
315
316         memcpy( dest, path, src - path );
317         dest[src - path] = 0;
318 }
319
320 void __ExtractFileName( const char *path, char *dest ){
321         const char *src;
322
323         src = path + strlen( path ) - 1;
324
325 //
326 // back up until a \ or the start
327 //
328         while ( src != path && *( src - 1 ) != '/'
329                         && *( src - 1 ) != '\\' )
330                 src--;
331
332         while ( *src )
333         {
334                 *dest++ = *src++;
335         }
336         *dest = 0;
337 }
338
339 void __ExtractFileBase( const char *path, char *dest ){
340         const char *src;
341
342         src = path + strlen( path ) - 1;
343
344 //
345 // back up until a \ or the start
346 //
347         while ( src != path && *( src - 1 ) != '/'
348                         && *( src - 1 ) != '\\' )
349                 src--;
350
351         while ( *src && *src != '.' )
352         {
353                 *dest++ = *src++;
354         }
355         *dest = 0;
356 }
357
358 void __ExtractFileExtension( const char *path, char *dest ){
359         const char *src;
360
361         src = path + strlen( path ) - 1;
362
363 //
364 // back up until a . or the start
365 //
366         while ( src != path && *( src - 1 ) != '.' )
367                 src--;
368         if ( src == path ) {
369                 *dest = 0;  // no extension
370                 return;
371         }
372
373         strcpy( dest,src );
374 }
375
376
377 void __ConvertDOSToUnixName( char *dst, const char *src ){
378         while ( *src )
379         {
380                 if ( *src == '\\' ) {
381                         *dst = '/';
382                 }
383                 else{
384                         *dst = *src;
385                 }
386                 dst++; src++;
387         }
388         *dst = 0;
389 }
390
391
392
393
394
395 static void AddSlash( Str& str ){
396         int nLen = str.GetLength();
397         if ( nLen > 0 ) {
398                 if ( str[nLen - 1] != '\\' && str[nLen - 1] != '/' ) {
399                         str += '\\';
400                 }
401         }
402 }
403
404 static void FindReplace( Str& strContents, const char* pTag, const char* pValue ){
405         if ( strcmp( pTag, pValue ) == 0 ) {
406                 return;
407         }
408         for ( int nPos = strContents.Find( pTag ); nPos >= 0; nPos = strContents.Find( pTag ) )
409         {
410                 int nRightLen = strContents.GetLength() - strlen( pTag ) - nPos;
411                 Str strLeft( strContents.Left( nPos ) );
412                 Str strRight( strContents.Right( nRightLen ) );
413                 strLeft += pValue;
414                 strLeft += strRight;
415                 strContents = strLeft;
416         }
417 }
418
419
420
421
422
423 void ClearFileList( FILELIST **list ){
424         FILELIST    *temp;
425
426         while ( *list )
427         {
428                 temp = *list;
429                 *list = ( *list )->next;
430                 free( temp );
431         }
432 }
433
434 void ClearDirList( DIRLIST **list ){
435         DIRLIST *temp;
436
437         while ( *list )
438         {
439                 temp = *list;
440                 *list = ( *list )->next;
441                 free( temp );
442         }
443 }
444
445 DIRECTORY *FindPakDir( DIRECTORY *dir, char *name ){
446         DIRECTORY   *currentPtr;
447
448         for ( currentPtr = dir; currentPtr; currentPtr = currentPtr->next )
449         {
450                 if ( !stricmp( name, currentPtr->name ) ) {
451                         return currentPtr;
452                 }
453         }
454         return NULL;
455 }
456
457
458 // LoadPK3FileList
459 // ---------------
460 //
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
464 //
465 bool LoadPK3FileList( FILELIST **filelist, const char *pattern ){
466         char cSearch[WORK_LEN];
467         __ConvertDOSToUnixName( cSearch, pattern );
468         char cPath[WORK_LEN];
469         char cExt[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;
476
477         PK3List *p = g_PK3Files.Next();
478         while ( p != NULL )
479         {
480                 // qualify the path
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 );
485                 }
486                 p = p->Next();
487         }
488         return ( *filelist ) != NULL;
489 }
490
491 bool GetPackFileList( FILELIST **filelist, char *pattern ){
492         char                    *str1, *str2;
493         int i;
494         DIRECTORY               *dummy = paktextures;
495         FILELIST                *temp;
496
497         if ( !pakopen ) {
498                 return false;
499         }
500
501         if ( g_bPK3 ) {
502                 return LoadPK3FileList( filelist, pattern );
503         }
504
505         str1 = pattern;
506
507         for ( i = 0; pattern[i] != '\0'; i++ )
508         {
509                 if ( pattern[i] == '\\' ) {
510                         pattern[i] = '/';
511                 }
512         }
513
514         while ( strchr( str1, '/' ) )
515         {
516                 str2 = strchr( str1, '/' );
517                 *str2++ = '\0';
518                 dummy = FindPakDir( dummy, str1 );
519                 if ( !dummy ) {
520                         return false;
521                 }
522                 str1 = str2;
523         }
524         for ( temp = dummy->files; temp; temp = temp->next )
525         {
526                 AddToFileListAlphabetized( filelist, temp->filename, temp->offset, 0, false );
527         }
528         return true;
529 }
530
531 bool GetPackTextureDirs( DIRLIST **dirlist ){
532         UInt16 i;
533         char buf[57];
534
535         if ( !pakopen ) {
536                 return 1;
537         }
538
539         if ( g_bPK3 ) {
540                 StrList *pl = g_PK3TexturePaths.Next();
541                 while ( pl != NULL )
542                 {
543                         AddToDirListAlphabetized( dirlist, pl->Ref(), 0 );
544                         pl = pl->Next();
545                 }
546                 return true;
547         }
548
549         for ( i = 0; i < dirsize; i++ )
550         {
551                 if ( !strnicmp( pakdirptr[i].name, "textures", 8 ) ) {
552                         strncpy( buf, &( pakdirptr[i].name[9] ), 46 );
553                         if ( strchr( buf, '\\' ) ) {
554                                 *strchr( buf, '\\' ) = '\0';
555                         }
556                         else if ( strchr( buf, '/' ) ) {
557                                 *strchr( buf, '/' ) = '\0';
558                         }
559                         else{
560                                 buf[56] = '\0';
561                         }
562
563                         if ( strchr( buf, '.' ) ) {
564                                 continue;
565                         }
566
567                         AddToDirListAlphabetized( dirlist, buf, 0 );
568                 }
569         }
570         return true;
571 }
572
573 bool AddToDirListAlphabetized( DIRLIST **list, char *dirname, int from ){
574         DIRLIST *currentPtr, *previousPtr, *newPtr;
575
576         strlwr( dirname );
577         for ( currentPtr = *list; currentPtr; currentPtr = currentPtr->next )
578         {
579                 if ( !stricmp( dirname, currentPtr->dirname ) ) {
580                         return false;
581                 }
582         }
583         previousPtr = NULL;
584         currentPtr = *list;
585
586         if ( ( newPtr = (DIRLIST *)__qmalloc( sizeof( DIRLIST ) ) ) == NULL ) {
587                 return false;
588         }
589
590         strcpy( newPtr->dirname, dirname );
591         newPtr->from = from;
592
593         while ( currentPtr != NULL && stricmp( dirname, currentPtr->dirname ) > 0 )
594         {
595                 previousPtr = currentPtr;
596                 currentPtr = currentPtr->next;
597         } //End while
598         if ( previousPtr == NULL ) {
599                 newPtr->next = *list;
600                 *list = newPtr;
601         } //End if
602         else
603         {
604                 previousPtr->next = newPtr;
605                 newPtr->next = currentPtr;
606         } //End else
607         return true;
608 }
609
610 bool AddToFileListAlphabetized( FILELIST **list, char *filename, UInt32 offset, UInt32 size, bool dirs ){
611         FILELIST    *currentPtr, *previousPtr, *newPtr;
612
613         for ( currentPtr = *list; currentPtr; currentPtr = currentPtr->next )
614         {
615                 if ( !stricmp( filename, currentPtr->filename ) ) {
616                         return false;
617                 }
618         }
619         previousPtr = NULL;
620         currentPtr = *list;
621
622         if ( ( newPtr = (FILELIST *)__qmalloc( sizeof( FILELIST ) ) ) == NULL ) {
623                 return false;
624         }
625
626         strcpy( newPtr->filename, filename );
627         newPtr->offset = offset;
628         newPtr->size = size;
629
630         while ( currentPtr != NULL && stricmp( filename, currentPtr->filename ) > 0 )
631         {
632                 previousPtr = currentPtr;
633                 currentPtr = currentPtr->next;
634         } //End while
635         if ( previousPtr == NULL ) {
636                 newPtr->next = *list;
637                 *list = newPtr;
638         } //End if
639         else
640         {
641                 previousPtr->next = newPtr;
642                 newPtr->next = currentPtr;
643         } //End else
644         return true;
645 }
646
647 int PakLoadAnyFile( const char *filename, void **bufferptr ){
648         char cWork[WORK_LEN];
649         if ( g_bPK3 ) {
650                 // leo: hack to be able to use pak files from multiple directories
651                 for ( int i = 0; i < g_numBasePaths; i++ )
652                 {
653                         PK3FileInfo *pInfo;
654                         Str strKey;
655                         // need to lookup the file without the base/texture path on it
656                         Str strBase( g_strBasePaths[i] );
657                         AddSlash( strBase );
658                         __ConvertDOSToUnixName( cWork, strBase );
659                         Str strFile( filename );
660                         __ConvertDOSToUnixName( strFile, strFile );
661                         strFile.MakeLower();
662                         strlwr( cWork );
663                         FindReplace( strFile, cWork, "" );
664
665                         PK3FileInfo infoFind;
666                         infoFind.m_pName = __StrDup( strFile.GetBuffer() );
667                         PK3List *pList = g_PK3Files.Find( &infoFind );
668                         if ( pList ) {
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 );
674                                         *bufferptr = buffer;
675                                         unzCloseCurrentFile( pInfo->m_zFile );
676                                         return n;
677                                 }
678                         }
679                 }
680
681 #ifdef LOG_PAKFAIL
682                 sprintf( cWork, "PAK failed on %s\n", filename );
683                 g_LogFile.Log( cWork );
684 #endif
685                 return -1;
686         }
687
688         for ( int i = 0; i < dirsize; i++ )
689         {
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 ) {
695                                         *bufferptr = buffer;
696                                         return pakdirptr[i].size;
697                                 }
698                         }
699                 }
700         }
701 #ifdef LOG_PAKFAIL
702         sprintf( cWork, "PAK failed on %s\n", filename );
703         g_LogFile.Log( cWork );
704 #endif
705         return -1;
706 }
707
708
709
710 DIRECTORY *AddPakDir( DIRECTORY **dir, char *name ){
711         DIRECTORY   *currentPtr, *previousPtr, *newPtr;
712
713         for ( currentPtr = *dir; currentPtr; currentPtr = currentPtr->next )
714         {
715                 if ( !stricmp( name, currentPtr->name ) ) {
716                         return currentPtr;
717                 }
718         }
719         previousPtr = NULL;
720         currentPtr = *dir;
721
722         if ( ( newPtr = (DIRECTORY *)__qmalloc( sizeof( DIRECTORY ) ) ) == NULL ) {
723                 return NULL;
724         }
725
726         strcpy( newPtr->name, name );
727         newPtr->files = NULL;
728
729         while ( currentPtr != NULL && stricmp( name, currentPtr->name ) > 0 )
730         {
731                 previousPtr = currentPtr;
732                 currentPtr = currentPtr->next;
733         }
734         if ( previousPtr == NULL ) {
735                 newPtr->next = *dir;
736                 *dir = newPtr;
737         }
738         else
739         {
740                 previousPtr->next = newPtr;
741                 newPtr->next = currentPtr;
742         }
743         return newPtr;
744 }
745
746
747 // OpenPK3
748 // -------
749 // Opens a PK3 ( or zip ) file and creates a list of filenames
750 // and zip info structures
751 //
752 bool OpenPK3( const char *filename ){
753         char cFilename[WORK_LEN];
754         char cName[WORK_LEN];
755         char cWork[WORK_LEN];
756         unz_file_info zInfo;
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 )
762                 {
763                         cFilename[0] = '\0';
764                         unzGetCurrentFileInfo( *zFile, &zInfo, cFilename, WORK_LEN, NULL, 0, NULL, 0 );
765                         strlwr( cFilename );
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 );
774                         }
775                         char *p = strstr( cFilename, TEXTURE_PATH );
776                         if ( p != NULL ) {
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 );
784                                         cName[nEnd] = '\0';
785
786                                         bool bFound = false;
787                                         StrList *pl = g_PK3TexturePaths.Next();
788                                         while ( pl != NULL )
789                                         {
790                                                 if ( strcmpi( pl->Ref(), cName ) == 0 ) {
791                                                         // already have this, continue
792                                                         bFound = true;
793                                                         break;
794                                                 }
795                                                 pl = pl->Next();
796                                         }
797                                         if ( !bFound ) {
798                                                 g_PK3TexturePaths.Add( new Str( cName ) );
799                                         }
800                                 }
801                         }
802                         nStatus = unzGoToNextFile( *zFile );
803                 }
804         }
805         return ( zFile != NULL );
806 }
807
808 void closePK3( unzFile zf ){
809         unzClose( zf );
810 }
811
812 void OpenPakFile( const char *filename ){
813         if ( !pakopen ) {
814                 paktextures = NULL;
815         }
816
817         pakopen = g_bPK3 = OpenPK3( filename );
818 }
819
820 void ClearPaKDir( DIRECTORY **dir ){
821         DIRECTORY   *d1 = *dir, *d2;
822
823         while ( d1 )
824         {
825                 ClearFileList( &( d1->files ) );
826                 d2 = d1;
827                 d1 = d1->next;
828                 free( d2 );
829         }
830 }
831
832 void CleanUpPakDirs(){
833         ClearPaKDir( &paktextures );
834         paktextures = NULL;
835         dirhead = NULL;
836         g_PK3TexturePaths.RemoveAll();
837         g_PK3Files.RemoveAll();
838 }
839
840 void ClosePakFile( void ){
841         if ( pakopen ) {
842                 if ( g_bPK3 ) {
843                         ZFileList *p = g_zFiles.Next();
844                         while ( p != NULL )
845                         {
846                                 unzFile uz = p->Ref();
847                                 closePK3( uz );
848                                 p = p->Next();
849                         }
850                 }
851                 else
852                 {
853                         fclose( pakfile[m_nPAKIndex] );
854                 }
855         }
856         pakopen = false;
857         CleanUpPakDirs();
858 }
859
860
861 void WINAPI InitPakFile( const char * pBasePath, const char *pName ){
862         if ( g_numBasePaths == 0 ) {
863                 m_nPAKIndex = 0;
864                 pakopen = false;
865                 paktextures = NULL;
866         }
867         strcpy( g_strBasePaths[g_numBasePaths], pBasePath );
868         g_numBasePaths++;
869
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;
875                 DIR *dir;
876
877                 dir = opendir( pBasePath );
878                 if ( dir != NULL ) {
879                         while ( ( dirlist = readdir( dir ) ) != NULL )
880                         {
881                                 if ( strstr( dirlist->d_name, ".pk3" ) == NULL ) {
882                                         continue;
883                                 }
884                                 sprintf( cWork, "%s/%s", pBasePath, dirlist->d_name );
885                                 OpenPakFile( cWork );
886                         }
887                         closedir( dir );
888                 }
889 #endif
890 #ifdef _WIN32
891                 char cWork[WORK_LEN];
892                 Str strPath( pBasePath );
893                 AddSlash( strPath );
894                 strPath += "*.pk3";
895                 bool bGo = true;
896                 struct _finddata_t fileinfo;
897                 int handle = _findfirst( strPath, &fileinfo );
898                 if ( handle != -1 ) {
899                         do
900                         {
901                                 sprintf( cWork, "%s/%s", pBasePath, fileinfo.name );
902                                 OpenPakFile( cWork );
903                         } while ( _findnext( handle, &fileinfo ) != -1 );
904                         _findclose( handle );
905                 }
906 #endif
907         }
908         else
909         {
910                 OpenPakFile( pName );
911         }
912 }