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