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