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;
66 bool operator ==(const PK3FileInfo& rhs) const { return strcmp(m_pName, rhs.m_pName) == 0; }
69 #define __PATHSEPERATOR '/'
75 #if defined (__linux__) || defined (__APPLE__)
79 #include <sys/types.h>
85 LogFile(const char* pName)
87 #if defined (__linux__) || defined (__APPLE__)
88 // leo: use ~/.q3a/radiant/paklog instead of /tmp/paklog.txt
91 home = getenv("HOME");
98 while( (pwd = getpwent()) != NULL )
99 if( pwd->pw_uid == id )
111 if (path[strlen(path)-1] != '/')
113 strcat (path, ".q3a/radiant/paklog");
114 m_pFile = fopen(path, "w");
118 m_pFile = fopen(pName, "w");
127 void Log(const char *pFormat, ...)
133 va_start(arg_ptr, pFormat);
134 fprintf(m_pFile, pFormat, arg_ptr);
139 LogFile g_LogFile("/tmp/paklog.txt");
142 template <class T> class StrPtr : public Str
151 StrPtr(const char *pStr, T *p) : Str(pStr)
171 template <class T> class PtrList
204 while (pl && pl->m_pNext)
208 pl->m_pNext = new PtrList(ip);
213 PtrList *p = m_pNext;
216 while (p->m_pNext != this && p->m_pNext != NULL)
220 if (p->m_pNext == this)
222 p->m_pNext = m_pNext;
227 virtual PtrList* Find(T *ip)
229 PtrList *p = m_pNext;
232 if (*p->m_pPtr == *ip)
241 // remove vp from the list
244 PtrList *p = Find(ip);
263 PtrList *p = m_pNext;
274 typedef PtrList<unzFile> ZFileList;
275 typedef PtrList<Str> StrList;
276 typedef PtrList<PK3FileInfo> PK3List;
279 StrList g_PK3TexturePaths;
282 #define WORK_LEN 1024
283 #define TEXTURE_PATH "textures"
284 #define PATH_SEPERATORS "/\\:\0"
287 char* __StrDup(char* pStr)
292 return strcpy(new char[strlen(pStr)+1], pStr);
295 char* __StrDup(const char* pStr)
300 return strcpy(new char[strlen(pStr)+1], pStr);
304 #define MEM_BLOCKSIZE 4096
305 void* __qblockmalloc(size_t nSize)
308 // round up to threshold
309 int nAllocSize = nSize % MEM_BLOCKSIZE;
312 nSize += MEM_BLOCKSIZE - nAllocSize;
314 b = malloc(nSize + 1);
315 memset (b, 0, nSize);
319 void* __qmalloc (size_t nSize)
322 b = malloc(nSize + 1);
323 memset (b, 0, nSize);
333 void __ExtractFilePath (const char *path, char *dest)
337 src = path + strlen(path) - 1;
340 // back up until a \ or the start
342 while (src != path && *(src-1) != __PATHSEPERATOR)
345 memcpy (dest, path, src-path);
349 void __ExtractFileName (const char *path, char *dest)
353 src = path + strlen(path) - 1;
356 // back up until a \ or the start
358 while (src != path && *(src-1) != '/'
359 && *(src-1) != '\\' )
369 void __ExtractFileBase (const char *path, char *dest)
373 src = path + strlen(path) - 1;
376 // back up until a \ or the start
378 while (src != path && *(src-1) != '/'
379 && *(src-1) != '\\' )
382 while (*src && *src != '.')
389 void __ExtractFileExtension (const char *path, char *dest)
393 src = path + strlen(path) - 1;
396 // back up until a . or the start
398 while (src != path && *(src-1) != '.')
402 *dest = 0; // no extension
410 void __ConvertDOSToUnixName( char *dst, const char *src )
427 static void AddSlash(Str& str)
429 int nLen = str.GetLength();
432 if (str[nLen-1] != '\\' && str[nLen-1] != '/')
437 static void FindReplace(Str& strContents, const char* pTag, const char* pValue)
439 if (strcmp(pTag, pValue) == 0)
441 for (int nPos = strContents.Find(pTag); nPos >= 0; nPos = strContents.Find(pTag))
443 int nRightLen = strContents.GetLength() - strlen(pTag) - nPos;
444 Str strLeft(strContents.Left(nPos));
445 Str strRight(strContents.Right(nRightLen));
448 strContents = strLeft;
456 void ClearFileList(FILELIST **list)
463 *list = (*list)->next;
468 void ClearDirList(DIRLIST **list)
475 *list = (*list)->next;
480 DIRECTORY *FindPakDir(DIRECTORY *dir, char *name)
482 DIRECTORY *currentPtr;
484 for(currentPtr = dir; currentPtr; currentPtr = currentPtr->next)
486 if(!stricmp(name, currentPtr->name))
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
502 bool LoadPK3FileList(FILELIST **filelist, const char *pattern)
504 char cSearch[WORK_LEN];
505 __ConvertDOSToUnixName( cSearch, pattern );
506 char cPath[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;
515 PK3List *p = g_PK3Files.Next();
519 PK3FileInfo *pKey = p->Ptr();
520 if (strstr(pKey->m_pName, cPath) && strstr(pKey->m_pName, pCompare))
522 __ExtractFileName(pKey->m_pName, cWork);
523 AddToFileListAlphabetized(filelist, cWork, 0, 0, false);
527 return (*filelist) != NULL;
530 bool GetPackFileList(FILELIST **filelist, char *pattern)
534 DIRECTORY *dummy = paktextures;
542 return LoadPK3FileList(filelist, pattern);
547 for(i = 0; pattern[i] != '\0'; i++)
549 if(pattern[i] == '\\')
553 while(strchr(str1, '/'))
555 str2 = strchr(str1, '/');
557 dummy = FindPakDir(dummy, str1);
562 for(temp = dummy->files; temp; temp=temp->next)
564 AddToFileListAlphabetized(filelist, temp->filename, temp->offset, 0, false);
569 bool GetPackTextureDirs(DIRLIST **dirlist)
579 StrList *pl = g_PK3TexturePaths.Next();
582 AddToDirListAlphabetized(dirlist, pl->Ref(), 0);
588 for (i = 0; i < dirsize; i++)
590 if(!strnicmp(pakdirptr[i].name, "textures", 8))
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';
603 AddToDirListAlphabetized(dirlist, buf, 0);
609 bool AddToDirListAlphabetized(DIRLIST **list, char *dirname, int from)
611 DIRLIST *currentPtr, *previousPtr, *newPtr;
614 for(currentPtr = *list; currentPtr; currentPtr = currentPtr->next)
616 if(!stricmp(dirname, currentPtr->dirname))
624 if((newPtr = (DIRLIST *)__qmalloc(sizeof(DIRLIST))) == NULL)
627 strcpy(newPtr->dirname, dirname);
630 while(currentPtr != NULL && stricmp(dirname, currentPtr->dirname) > 0)
632 previousPtr = currentPtr;
633 currentPtr = currentPtr->next;
635 if(previousPtr == NULL)
637 newPtr->next = *list;
642 previousPtr->next = newPtr;
643 newPtr->next = currentPtr;
648 bool AddToFileListAlphabetized(FILELIST **list, char *filename, UInt32 offset, UInt32 size, bool dirs)
650 FILELIST *currentPtr, *previousPtr, *newPtr;
652 for(currentPtr = *list; currentPtr; currentPtr = currentPtr->next)
654 if(!stricmp(filename, currentPtr->filename))
662 if((newPtr = (FILELIST *)__qmalloc(sizeof(FILELIST))) == NULL)
665 strcpy(newPtr->filename, filename);
666 newPtr->offset = offset;
669 while(currentPtr != NULL && stricmp(filename, currentPtr->filename) > 0)
671 previousPtr = currentPtr;
672 currentPtr = currentPtr->next;
674 if(previousPtr == NULL)
676 newPtr->next = *list;
681 previousPtr->next = newPtr;
682 newPtr->next = currentPtr;
687 int PakLoadAnyFile(const char *filename, void **bufferptr)
689 char cWork[WORK_LEN];
692 // leo: hack to be able to use pak files from multiple directories
693 for (int i = 0; i < g_numBasePaths; i++)
697 // need to lookup the file without the base/texture path on it
698 Str strBase(g_strBasePaths[i]);
700 __ConvertDOSToUnixName(cWork, strBase);
701 Str strFile(filename);
702 __ConvertDOSToUnixName(strFile, strFile);
705 FindReplace(strFile, cWork, "");
707 PK3FileInfo infoFind;
708 infoFind.m_pName = __StrDup(strFile.GetBuffer());
709 PK3List *pList = g_PK3Files.Find(&infoFind);
712 pInfo = pList->Ptr();
713 memcpy(pInfo->m_zFile, &pInfo->m_zInfo, sizeof(unz_s));
714 if (unzOpenCurrentFile(pInfo->m_zFile) == UNZ_OK)
716 void *buffer = __qblockmalloc(pInfo->m_lSize+1);
717 int n = unzReadCurrentFile(pInfo->m_zFile , buffer, pInfo->m_lSize);
719 unzCloseCurrentFile(pInfo->m_zFile);
726 sprintf(cWork, "PAK failed on %s\n", filename);
727 g_LogFile.Log(cWork);
732 for (int i = 0; i < dirsize; i++)
734 if(!stricmp(filename, pakdirptr[i].name))
736 if (fseek(pakfile[m_nPAKIndex], pakdirptr[i].offset, SEEK_SET) >= 0)
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)
743 return pakdirptr[i].size;
749 sprintf(cWork, "PAK failed on %s\n", filename);
750 g_LogFile.Log(cWork);
757 DIRECTORY *AddPakDir(DIRECTORY **dir, char *name)
759 DIRECTORY *currentPtr, *previousPtr, *newPtr;
761 for(currentPtr = *dir; currentPtr; currentPtr = currentPtr->next)
763 if(!stricmp(name, currentPtr->name))
771 if((newPtr = (DIRECTORY *)__qmalloc(sizeof(DIRECTORY))) == NULL)
774 strcpy(newPtr->name, name);
775 newPtr->files = NULL;
777 while(currentPtr != NULL && stricmp(name, currentPtr->name) > 0)
779 previousPtr = currentPtr;
780 currentPtr = currentPtr->next;
782 if(previousPtr == NULL)
789 previousPtr->next = newPtr;
790 newPtr->next = currentPtr;
798 // Opens a PK3 ( or zip ) file and creates a list of filenames
799 // and zip info structures
801 bool OpenPK3(const char *filename)
803 char cFilename[WORK_LEN];
804 char cName[WORK_LEN];
805 char cWork[WORK_LEN];
807 unzFile *zFile = new unzFile(unzOpen(filename));
811 int nStatus = unzGoToFirstFile(*zFile);
812 while (nStatus == UNZ_OK)
815 unzGetCurrentFileInfo(*zFile, &zInfo, cFilename, WORK_LEN, NULL, 0, NULL, 0);
817 __ConvertDOSToUnixName( cWork, cFilename);
818 if (strstr(cWork, ".") != NULL)
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);
827 char *p = strstr(cFilename, TEXTURE_PATH);
830 // FIXME: path differences per os ?
831 // catch solo directory entry
832 if (strlen(p) > strlen(TEXTURE_PATH) + 1)
834 // skip textures + path seperator
835 p += strlen(TEXTURE_PATH) + 1;
836 int nEnd = strcspn(p, PATH_SEPERATORS);
837 strncpy(cName, p, nEnd);
841 StrList *pl = g_PK3TexturePaths.Next();
844 if (strcmpi(pl->Ref(), cName) == 0)
846 // already have this, continue
854 g_PK3TexturePaths.Add(new Str(cName));
858 nStatus = unzGoToNextFile(*zFile);
861 return (zFile != NULL);
864 void closePK3(unzFile zf)
869 void OpenPakFile(const char *filename)
874 pakopen = g_bPK3 = OpenPK3(filename);
877 void ClearPaKDir(DIRECTORY **dir)
879 DIRECTORY *d1 = *dir, *d2;
883 ClearFileList(&(d1->files));
890 void CleanUpPakDirs()
892 ClearPaKDir(&paktextures);
895 g_PK3TexturePaths.RemoveAll();
896 g_PK3Files.RemoveAll();
899 void ClosePakFile(void)
905 ZFileList *p = g_zFiles.Next();
908 unzFile uz = p->Ref();
915 fclose(pakfile[m_nPAKIndex]);
923 void WINAPI InitPakFile(const char * pBasePath, const char *pName)
925 if (g_numBasePaths == 0)
931 strcpy(g_strBasePaths[g_numBasePaths], pBasePath);
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;
942 dir = opendir (pBasePath);
945 while ((dirlist = readdir (dir)) != NULL)
947 if (strstr (dirlist->d_name, ".pk3") == NULL)
949 sprintf(cWork, "%s/%s", pBasePath, dirlist->d_name);
956 char cWork[WORK_LEN];
957 Str strPath(pBasePath);
961 struct _finddata_t fileinfo;
962 int handle = _findfirst (strPath, &fileinfo);
967 sprintf(cWork, "%s/%s", pBasePath, fileinfo.name);
969 } while (_findnext( handle, &fileinfo ) != -1);