]> git.xonotic.org Git - xonotic/netradiant.git/blobdiff - libs/pak/pakstuff.cpp
uncrustify! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / libs / pak / pakstuff.cpp
index f3ae725e9571bb057b9014d75661bf5a09710f29..9fb89e70b276fe2ff8b54491bf92ccb66e6ea3fd 100644 (file)
-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
-*/\r
-\r
-#include <stdio.h>\r
-#include <stdarg.h>\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#if defined (__linux__) || defined (__APPLE__)\r
-#include <dirent.h>\r
-#endif\r
-#ifdef _WIN32\r
-#include <io.h>\r
-#endif\r
-#include "pakstuff.h"\r
-#include "unzip.h"\r
-#include "str.h"\r
-\r
-#ifndef TRUE\r
-#define TRUE 1\r
-#define FALSE 0\r
-#endif\r
-\r
-int                     m_nPAKIndex;\r
-FILE*                   pakfile[16];\r
-struct PACKDirectory   pakdir;\r
-PACKDirPtr             pakdirptr = &pakdir;\r
-UInt16                 dirsize;\r
-bool                   pakopen = false;\r
-int                    f_type;\r
-DIRECTORY              *paktextures = NULL;\r
-UInt32                 PakColormapOffset;\r
-UInt32                 PakColormapSize;\r
-DIRECTORY                      *dirhead = NULL;\r
-bool                    g_bPK3 = false;\r
-char                    g_strBasePaths[16][1024];\r
-int                     g_numBasePaths = 0;\r
-\r
-struct PK3FileInfo\r
-{\r
-  unzFile m_zFile;\r
-  char *m_pName;\r
-  unz_s m_zInfo;\r
-  long m_lSize;\r
-  ~PK3FileInfo()\r
-  {\r
-    delete []m_pName;\r
-  }\r
-  bool operator ==(const PK3FileInfo& rhs) const { return strcmp(m_pName, rhs.m_pName) == 0; }\r
-};\r
-\r
-#define __PATHSEPERATOR   '/'\r
-\r
-//#define LOG_PAKFAIL\r
-\r
-#ifdef LOG_PAKFAIL\r
-\r
-#if defined (__linux__) || defined (__APPLE__)\r
-#include <unistd.h>\r
-#include <pwd.h>\r
-#endif\r
-#include <sys/types.h>\r
-\r
-class LogFile\r
-{\r
-public:\r
-  FILE *m_pFile;\r
-  LogFile(const char* pName)\r
-  {\r
-#if defined (__linux__) || defined (__APPLE__)\r
-    // leo: use ~/.q3a/radiant/paklog instead of /tmp/paklog.txt\r
-    char *home = NULL;\r
\r
-    home = getenv("HOME");\r
-    if ( home == NULL )\r
-    {\r
-      uid_t id = getuid();\r
-      struct passwd *pwd;\r
\r
-      setpwent();\r
-      while( (pwd = getpwent()) != NULL )\r
-       if( pwd->pw_uid == id )\r
-       {\r
-         home = pwd->pw_dir;\r
-         break;\r
-       }\r
-      endpwent();\r
-    }\r
-\r
-    if (home != NULL)\r
-    {\r
-      char path[PATH_MAX];\r
-      strcpy (path, home);\r
-      if (path[strlen(path)-1] != '/')\r
-       strcat (path, "/");\r
-      strcat (path, ".q3a/radiant/paklog");\r
-      m_pFile = fopen(path, "w");\r
-    }\r
-    else\r
-#endif\r
-      m_pFile = fopen(pName, "w");\r
-  }\r
-  ~LogFile()\r
-  {\r
-    if (m_pFile)\r
-    {\r
-      fclose(m_pFile);\r
-    }\r
-  }\r
-  void Log(const char *pFormat, ...)\r
-  {\r
-    if (m_pFile == NULL)\r
-      return;\r
-\r
-    va_list arg_ptr;\r
-    va_start(arg_ptr, pFormat);\r
-    fprintf(m_pFile, pFormat, arg_ptr);\r
-    va_end(arg_ptr);\r
-  }\r
-};\r
-\r
-LogFile g_LogFile("/tmp/paklog.txt");\r
-#endif\r
-\r
-template <class T> class StrPtr : public Str\r
-{\r
-protected:\r
-  T* m_pPtr;\r
-  StrPtr()\r
-  {\r
-    m_pPtr = NULL;\r
-  }\r
-\r
-  StrPtr(const char *pStr, T *p) : Str(pStr)\r
-  {\r
-    m_pPtr = p;\r
-  }\r
-\r
-  T* Ptr()\r
-  {\r
-    return m_pPtr;\r
-  }\r
-\r
-  T& Ref()\r
-  {\r
-    return *m_pPtr;\r
-  }\r
-    \r
-\r
-};\r
-// PtrList\r
-// a list of ptrs\r
-// \r
-template <class T> class PtrList\r
-{\r
-protected:\r
-  T *m_pPtr;\r
-  PtrList *m_pNext;\r
-\r
-public:\r
-\r
-  PtrList()\r
-  {\r
-    m_pNext = NULL;\r
-    m_pPtr = NULL;\r
-  }\r
-\r
-  PtrList(T *ip)\r
-  {\r
-    m_pNext = NULL;\r
-    m_pPtr = ip;\r
-  }\r
-\r
-  virtual ~PtrList()\r
-  {\r
-    delete m_pPtr;\r
-  }\r
-\r
-  PtrList* Next()\r
-  {\r
-    return m_pNext;\r
-  }\r
-\r
-  void Add(T *ip)\r
-  {\r
-    PtrList *pl = this;\r
-    while (pl && pl->m_pNext)\r
-    {\r
-      pl = pl->Next();\r
-    }\r
-    pl->m_pNext = new PtrList(ip);\r
-  }\r
-\r
-  void Remove()\r
-  {\r
-    PtrList *p = m_pNext;\r
-    if (p)\r
-    {\r
-      while (p->m_pNext != this && p->m_pNext != NULL)\r
-      {\r
-        p = p->m_pNext;\r
-      }\r
-      if (p->m_pNext == this)\r
-      {\r
-        p->m_pNext = m_pNext;\r
-      }\r
-    }\r
-  }\r
-\r
-  virtual PtrList* Find(T *ip)\r
-  {\r
-    PtrList *p = m_pNext;\r
-    while (p)\r
-    {\r
-      if (*p->m_pPtr == *ip)\r
-      {\r
-        return p;\r
-      }\r
-      p = p->m_pNext;\r
-    }\r
-    return NULL;\r
-  }\r
-\r
-  // remove vp from the list\r
-  void Remove(T *ip)\r
-  {\r
-    PtrList *p = Find(ip);\r
-    if (p)\r
-    {\r
-      p->Remove();\r
-    }\r
-  }\r
-\r
-  T* Ptr()\r
-  {\r
-    return m_pPtr;\r
-  }\r
-\r
-  T& Ref()\r
-  {\r
-    return *m_pPtr;\r
-  }\r
-\r
-  void RemoveAll()\r
-  {\r
-    PtrList *p = m_pNext;\r
-    while (p)\r
-    {\r
-      PtrList *p2 = p;\r
-      p = p->m_pNext;\r
-      delete p2;\r
-    }\r
-  }\r
-};\r
-\r
-\r
-typedef PtrList<unzFile> ZFileList;\r
-typedef PtrList<Str> StrList;\r
-typedef PtrList<PK3FileInfo> PK3List;\r
-\r
-\r
-StrList g_PK3TexturePaths;\r
-PK3List g_PK3Files;\r
-ZFileList g_zFiles;\r
-#define WORK_LEN 1024\r
-#define TEXTURE_PATH "textures"\r
-#define PATH_SEPERATORS "/\\:\0"\r
-\r
-/*\r
-char* __StrDup(char* pStr)\r
-{\r
-  if (pStr == NULL)\r
-    pStr = "";\r
-\r
-  return strcpy(new char[strlen(pStr)+1], pStr); \r
-}\r
-\r
-char* __StrDup(const char* pStr)\r
-{ \r
-  if (pStr == NULL)\r
-    pStr = "";\r
-\r
-  return strcpy(new char[strlen(pStr)+1], pStr); \r
-}\r
-*/\r
-\r
-#define MEM_BLOCKSIZE 4096\r
-void* __qblockmalloc(size_t nSize)\r
-{\r
-       void *b;\r
-  // round up to threshold\r
-  int nAllocSize = nSize % MEM_BLOCKSIZE;\r
-  if ( nAllocSize > 0)\r
-  {\r
-    nSize += MEM_BLOCKSIZE - nAllocSize;\r
-  }\r
-       b = malloc(nSize + 1);\r
-       memset (b, 0, nSize);\r
-       return b;\r
-}\r
-\r
-void* __qmalloc (size_t nSize)\r
-{\r
-       void *b;\r
-       b = malloc(nSize + 1);\r
-       memset (b, 0, nSize);\r
-       return b;\r
-}\r
-\r
-\r
-/*\r
-====================\r
-Extract file parts\r
-====================\r
-*/\r
-void __ExtractFilePath (const char *path, char *dest)\r
-{\r
-       const char *src;\r
-\r
-       src = path + strlen(path) - 1;\r
-\r
-//\r
-// back up until a \ or the start\r
-//\r
-       while (src != path && *(src-1) != __PATHSEPERATOR)\r
-               src--;\r
-\r
-       memcpy (dest, path, src-path);\r
-       dest[src-path] = 0;\r
-}\r
-\r
-void __ExtractFileName (const char *path, char *dest)\r
-{\r
-       const char *src;\r
-\r
-       src = path + strlen(path) - 1;\r
-\r
-//\r
-// back up until a \ or the start\r
-//\r
-       while (src != path && *(src-1) != '/' \r
-                && *(src-1) != '\\' )\r
-               src--;\r
-\r
-       while (*src)\r
-       {\r
-               *dest++ = *src++;\r
-       }\r
-       *dest = 0;\r
-}\r
-\r
-void __ExtractFileBase (const char *path, char *dest)\r
-{\r
-       const char *src;\r
-\r
-       src = path + strlen(path) - 1;\r
-\r
-//\r
-// back up until a \ or the start\r
-//\r
-       while (src != path && *(src-1) != '/' \r
-                && *(src-1) != '\\' )\r
-               src--;\r
-\r
-       while (*src && *src != '.')\r
-       {\r
-               *dest++ = *src++;\r
-       }\r
-       *dest = 0;\r
-}\r
-\r
-void __ExtractFileExtension (const char *path, char *dest)\r
-{\r
-       const char *src;\r
-\r
-       src = path + strlen(path) - 1;\r
-\r
-//\r
-// back up until a . or the start\r
-//\r
-       while (src != path && *(src-1) != '.')\r
-               src--;\r
-       if (src == path)\r
-       {\r
-               *dest = 0;      // no extension\r
-               return;\r
-       }\r
-\r
-       strcpy (dest,src);\r
-}\r
-\r
-\r
-void __ConvertDOSToUnixName( char *dst, const char *src )\r
-{\r
-       while ( *src )\r
-       {\r
-               if ( *src == '\\' )\r
-                       *dst = '/';\r
-               else\r
-                       *dst = *src;\r
-               dst++; src++;\r
-       }\r
-       *dst = 0;\r
-}\r
-\r
-\r
-\r
-\r
-\r
-static void AddSlash(Str& str)\r
-{\r
-  int nLen = str.GetLength();\r
-  if (nLen > 0)\r
-  {\r
-    if (str[nLen-1] != '\\' && str[nLen-1] != '/')\r
-      str += '\\';\r
-  }\r
-}\r
-\r
-static void FindReplace(Str& strContents, const char* pTag, const char* pValue)\r
-{\r
-  if (strcmp(pTag, pValue) == 0)\r
-    return;\r
-  for (int nPos = strContents.Find(pTag); nPos >= 0; nPos = strContents.Find(pTag))\r
-  {\r
-    int nRightLen = strContents.GetLength() - strlen(pTag) - nPos;\r
-    Str strLeft(strContents.Left(nPos));\r
-    Str strRight(strContents.Right(nRightLen));\r
-    strLeft += pValue;\r
-    strLeft += strRight;\r
-    strContents = strLeft;\r
-  }\r
-}\r
-\r
-\r
-\r
-\r
-\r
-void ClearFileList(FILELIST **list)\r
-{\r
-       FILELIST        *temp;\r
-\r
-       while(*list)\r
-       {\r
-               temp = *list;\r
-               *list = (*list)->next;\r
-               free(temp);\r
-       }\r
-}\r
-\r
-void ClearDirList(DIRLIST **list)\r
-{\r
-       DIRLIST *temp;\r
-\r
-       while(*list)\r
-       {\r
-               temp = *list;\r
-               *list = (*list)->next;\r
-               free(temp);\r
-       }\r
-}\r
-\r
-DIRECTORY *FindPakDir(DIRECTORY *dir, char *name)\r
-{\r
-       DIRECTORY       *currentPtr;\r
-\r
-       for(currentPtr = dir; currentPtr; currentPtr = currentPtr->next)\r
-       {\r
-               if(!stricmp(name, currentPtr->name))\r
-               {\r
-                       return currentPtr;\r
-               }\r
-       }\r
-       return NULL;\r
-}\r
-\r
-\r
-// LoadPK3FileList\r
-// ---------------\r
-//\r
-// This gets passed a file mask which we want to remove as \r
-// we are only interested in the directory name and any given\r
-// extension. Only handles explicit filenames or *.something\r
-//\r
-bool LoadPK3FileList(FILELIST **filelist, const char *pattern)\r
-{\r
-  char cSearch[WORK_LEN];\r
-       __ConvertDOSToUnixName( cSearch, pattern );\r
-  char cPath[WORK_LEN];\r
-  char cExt[WORK_LEN];\r
-  char cFile[WORK_LEN];\r
-  char cWork[WORK_LEN];\r
-  __ExtractFilePath(pattern, cPath);\r
-  __ExtractFileName(pattern, cFile);\r
-  __ExtractFileExtension(pattern, cExt);\r
-  const char *pCompare = (strnicmp(cFile, "*.", 2) == 0) ? cExt : cFile;\r
-\r
-  PK3List *p = g_PK3Files.Next();\r
-  while (p != NULL)\r
-  {\r
-    // qualify the path\r
-    PK3FileInfo *pKey = p->Ptr();\r
-    if (strstr(pKey->m_pName, cPath) && strstr(pKey->m_pName, pCompare))\r
-    {\r
-      __ExtractFileName(pKey->m_pName, cWork); \r
-      AddToFileListAlphabetized(filelist, cWork, 0, 0, false);\r
-    }\r
-    p = p->Next();\r
-  }\r
-  return (*filelist) != NULL;\r
-}\r
-\r
-bool GetPackFileList(FILELIST **filelist, char *pattern)\r
-{\r
-       char                                    *str1, *str2;\r
-       int                                             i;\r
-       DIRECTORY                               *dummy = paktextures;\r
-       FILELIST                                *temp;\r
-\r
-       if (!pakopen)\r
-               return false;\r
-\r
-  if (g_bPK3)\r
-  {\r
-    return LoadPK3FileList(filelist, pattern);\r
-  }\r
-\r
-       str1 = pattern;\r
-\r
-       for(i = 0; pattern[i] != '\0'; i++)\r
-       {\r
-               if(pattern[i] == '\\')\r
-                       pattern[i] = '/';\r
-       }\r
-\r
-       while(strchr(str1, '/'))\r
-       {\r
-               str2 = strchr(str1, '/');\r
-               *str2++ = '\0';\r
-               dummy = FindPakDir(dummy, str1);\r
-               if(!dummy)\r
-                       return false;\r
-               str1 = str2;\r
-       }\r
-       for(temp = dummy->files; temp; temp=temp->next)\r
-       {\r
-         AddToFileListAlphabetized(filelist, temp->filename, temp->offset, 0, false);\r
-       }\r
-       return true;\r
-}\r
-\r
-bool GetPackTextureDirs(DIRLIST **dirlist)\r
-{\r
-       UInt16                                  i;\r
-       char                                    buf[57];\r
-\r
-       if (!pakopen)\r
-               return 1;\r
-\r
-  if (g_bPK3)\r
-  {\r
-    StrList *pl = g_PK3TexturePaths.Next();\r
-    while (pl != NULL)\r
-    {\r
-      AddToDirListAlphabetized(dirlist, pl->Ref(), 0);\r
-      pl = pl->Next();\r
-    }\r
-    return true;\r
-  }\r
-\r
-       for (i = 0; i < dirsize; i++)\r
-       {\r
-               if(!strnicmp(pakdirptr[i].name, "textures", 8))\r
-               {\r
-                       strncpy(buf, &(pakdirptr[i].name[9]), 46);\r
-                       if(strchr(buf, '\\'))\r
-               *strchr(buf, '\\') = '\0';\r
-                       else if(strchr(buf, '/'))\r
-               *strchr(buf, '/') = '\0';\r
-                       else\r
-               buf[56] = '\0';\r
-\r
-                       if(strchr(buf, '.'))\r
-                               continue;\r
-\r
-                       AddToDirListAlphabetized(dirlist, buf, 0);\r
-               }\r
-       }\r
-       return true;\r
-}\r
-\r
-bool AddToDirListAlphabetized(DIRLIST **list, char *dirname, int from)\r
-{\r
-       DIRLIST *currentPtr, *previousPtr, *newPtr;\r
-\r
-       strlwr(dirname);\r
-       for(currentPtr = *list; currentPtr; currentPtr = currentPtr->next)\r
-       {\r
-               if(!stricmp(dirname, currentPtr->dirname))\r
-               {\r
-                       return false;\r
-               }\r
-       }\r
-       previousPtr = NULL;\r
-       currentPtr = *list;\r
-\r
-       if((newPtr = (DIRLIST *)__qmalloc(sizeof(DIRLIST))) == NULL)\r
-               return false;\r
-\r
-       strcpy(newPtr->dirname, dirname);\r
-       newPtr->from = from;\r
-\r
-       while(currentPtr != NULL && stricmp(dirname, currentPtr->dirname) > 0)\r
-       {\r
-               previousPtr = currentPtr;\r
-               currentPtr = currentPtr->next;\r
-       } //End while\r
-       if(previousPtr == NULL)\r
-       {\r
-               newPtr->next = *list;\r
-               *list = newPtr;\r
-       } //End if\r
-       else\r
-       {\r
-               previousPtr->next = newPtr;\r
-               newPtr->next = currentPtr;\r
-       } //End else\r
-       return true;\r
-}\r
-\r
-bool AddToFileListAlphabetized(FILELIST **list, char *filename, UInt32 offset, UInt32 size, bool dirs)\r
-{\r
-       FILELIST        *currentPtr, *previousPtr, *newPtr;\r
-\r
-       for(currentPtr = *list; currentPtr; currentPtr = currentPtr->next)\r
-       {\r
-               if(!stricmp(filename, currentPtr->filename))\r
-               {\r
-                       return false;\r
-               }\r
-       }\r
-       previousPtr = NULL;\r
-       currentPtr = *list;\r
-\r
-       if((newPtr = (FILELIST *)__qmalloc(sizeof(FILELIST))) == NULL)\r
-               return false;\r
-\r
-       strcpy(newPtr->filename, filename);\r
-       newPtr->offset = offset;\r
-       newPtr->size = size;\r
-\r
-       while(currentPtr != NULL && stricmp(filename, currentPtr->filename) > 0)\r
-       {\r
-               previousPtr = currentPtr;\r
-               currentPtr = currentPtr->next;\r
-       } //End while\r
-       if(previousPtr == NULL)\r
-       {\r
-               newPtr->next = *list;\r
-               *list = newPtr;\r
-       } //End if\r
-       else\r
-       {\r
-               previousPtr->next = newPtr;\r
-               newPtr->next = currentPtr;\r
-       } //End else\r
-       return true;\r
-}\r
-\r
-int PakLoadAnyFile(const char *filename, void **bufferptr)\r
-{\r
-  char cWork[WORK_LEN];\r
-  if (g_bPK3)\r
-  {\r
-    // leo: hack to be able to use pak files from multiple directories\r
-    for (int i = 0; i < g_numBasePaths; i++)\r
-    {\r
-      PK3FileInfo *pInfo;\r
-      Str strKey;\r
-      // need to lookup the file without the base/texture path on it\r
-      Str strBase(g_strBasePaths[i]);\r
-      AddSlash(strBase);\r
-      __ConvertDOSToUnixName(cWork, strBase);\r
-      Str strFile(filename);\r
-      __ConvertDOSToUnixName(strFile, strFile);\r
-      strFile.MakeLower();\r
-      strlwr(cWork);\r
-      FindReplace(strFile, cWork, "");\r
-\r
-      PK3FileInfo infoFind;\r
-      infoFind.m_pName = __StrDup(strFile.GetBuffer());\r
-      PK3List *pList = g_PK3Files.Find(&infoFind);\r
-      if (pList)\r
-      {\r
-       pInfo = pList->Ptr();\r
-       memcpy(pInfo->m_zFile, &pInfo->m_zInfo, sizeof(unz_s));\r
-       if (unzOpenCurrentFile(pInfo->m_zFile) == UNZ_OK)\r
-        {\r
-          void *buffer = __qblockmalloc(pInfo->m_lSize+1);\r
-         int n = unzReadCurrentFile(pInfo->m_zFile , buffer, pInfo->m_lSize);\r
-         *bufferptr = buffer;\r
-         unzCloseCurrentFile(pInfo->m_zFile);\r
-         return n;\r
-       }\r
-      }\r
-    }\r
-\r
-#ifdef LOG_PAKFAIL\r
-    sprintf(cWork, "PAK failed on %s\n", filename);\r
-    g_LogFile.Log(cWork);\r
-#endif\r
-    return -1;\r
-  }\r
-\r
-  for (int i = 0; i < dirsize; i++)\r
-  {\r
-    if(!stricmp(filename, pakdirptr[i].name))\r
-    {\r
-      if (fseek(pakfile[m_nPAKIndex], pakdirptr[i].offset, SEEK_SET) >= 0)\r
-      {\r
-       void *buffer = __qmalloc (pakdirptr[i].size+1);\r
-       ((char *)buffer)[pakdirptr[i].size] = 0;\r
-       if (fread(buffer, 1, pakdirptr[i].size, pakfile[m_nPAKIndex]) == pakdirptr[i].size)\r
-        {\r
-          *bufferptr = buffer;\r
-          return pakdirptr[i].size;\r
-        }\r
-      }\r
-    }\r
-  }\r
-#ifdef LOG_PAKFAIL\r
-  sprintf(cWork, "PAK failed on %s\n", filename);\r
-  g_LogFile.Log(cWork);\r
-#endif\r
-  return -1;\r
-}\r
-\r
-\r
-\r
-DIRECTORY *AddPakDir(DIRECTORY **dir, char *name)\r
-{\r
-       DIRECTORY       *currentPtr, *previousPtr, *newPtr;\r
-\r
-       for(currentPtr = *dir; currentPtr; currentPtr = currentPtr->next)\r
-       {\r
-               if(!stricmp(name, currentPtr->name))\r
-               {\r
-                       return currentPtr;\r
-               }\r
-       }\r
-       previousPtr = NULL;\r
-       currentPtr = *dir;\r
-\r
-       if((newPtr = (DIRECTORY *)__qmalloc(sizeof(DIRECTORY))) == NULL)\r
-               return NULL;\r
-\r
-       strcpy(newPtr->name, name);\r
-       newPtr->files = NULL;\r
-\r
-       while(currentPtr != NULL && stricmp(name, currentPtr->name) > 0)\r
-       {\r
-               previousPtr = currentPtr;\r
-               currentPtr = currentPtr->next;\r
-       }\r
-       if(previousPtr == NULL)\r
-       {\r
-               newPtr->next = *dir;\r
-               *dir = newPtr;\r
-       }\r
-       else\r
-       {\r
-               previousPtr->next = newPtr;\r
-               newPtr->next = currentPtr;\r
-       }\r
-       return newPtr;\r
-}\r
-\r
-\r
-// OpenPK3\r
-// -------\r
-// Opens a PK3 ( or zip ) file and creates a list of filenames\r
-// and zip info structures\r
-// \r
-bool OpenPK3(const char *filename)\r
-{\r
-  char cFilename[WORK_LEN];\r
-  char cName[WORK_LEN];\r
-  char cWork[WORK_LEN];\r
-  unz_file_info zInfo;\r
-  unzFile *zFile = new unzFile(unzOpen(filename));\r
-  g_zFiles.Add(zFile);\r
-  if (zFile != NULL)\r
-  {\r
-    int nStatus = unzGoToFirstFile(*zFile);\r
-    while (nStatus == UNZ_OK)\r
-    {\r
-      cFilename[0] = '\0';\r
-      unzGetCurrentFileInfo(*zFile, &zInfo, cFilename, WORK_LEN, NULL, 0, NULL, 0);\r
-      strlwr(cFilename);\r
-       __ConvertDOSToUnixName( cWork, cFilename);\r
-      if (strstr(cWork, ".") != NULL)\r
-      {\r
-        PK3FileInfo *pInfo = new PK3FileInfo();\r
-        pInfo->m_pName = __StrDup(cWork);\r
-        memcpy(&pInfo->m_zInfo, (unz_s*)*zFile, sizeof(unz_s));\r
-        pInfo->m_lSize = zInfo.uncompressed_size;\r
-        pInfo->m_zFile = *zFile;\r
-        g_PK3Files.Add(pInfo);\r
-      }\r
-      char *p = strstr(cFilename, TEXTURE_PATH);\r
-      if (p != NULL)\r
-      {\r
-        // FIXME: path differences per os ?\r
-        // catch solo directory entry\r
-        if (strlen(p) > strlen(TEXTURE_PATH) + 1)\r
-        {\r
-          // skip textures + path seperator\r
-          p += strlen(TEXTURE_PATH) + 1;\r
-          int nEnd = strcspn(p, PATH_SEPERATORS);\r
-          strncpy(cName, p, nEnd);\r
-          cName[nEnd] = '\0';\r
-\r
-          bool bFound = false;\r
-          StrList *pl = g_PK3TexturePaths.Next();\r
-          while (pl != NULL)\r
-          {\r
-            if (strcmpi(pl->Ref(), cName) == 0)\r
-            {\r
-              // already have this, continue\r
-              bFound = true;\r
-              break;\r
-            }\r
-            pl = pl->Next();\r
-          }\r
-          if (!bFound)\r
-          {\r
-            g_PK3TexturePaths.Add(new Str(cName));\r
-          }\r
-        }\r
-      }\r
-      nStatus = unzGoToNextFile(*zFile);\r
-    }\r
-  }\r
-  return (zFile != NULL);\r
-}\r
-\r
-void closePK3(unzFile zf)\r
-{\r
-  unzClose(zf);\r
-}\r
-\r
-void OpenPakFile(const char *filename)\r
-{\r
-  if(!pakopen)\r
-    paktextures = NULL;\r
-\r
-  pakopen = g_bPK3 = OpenPK3(filename);\r
-}\r
-\r
-void ClearPaKDir(DIRECTORY **dir)\r
-{\r
-       DIRECTORY       *d1 = *dir, *d2;\r
-\r
-       while(d1)\r
-       {\r
-               ClearFileList(&(d1->files));\r
-               d2 = d1;\r
-               d1 = d1->next;\r
-               free(d2);\r
-       }\r
-}\r
-\r
-void CleanUpPakDirs()\r
-{\r
-  ClearPaKDir(&paktextures);\r
-  paktextures = NULL;\r
-  dirhead = NULL;\r
-  g_PK3TexturePaths.RemoveAll();\r
-  g_PK3Files.RemoveAll();\r
-}\r
-\r
-void ClosePakFile(void)\r
-{\r
-       if(pakopen)\r
-  {\r
-    if (g_bPK3)\r
-    {\r
-      ZFileList *p = g_zFiles.Next();\r
-      while (p != NULL)\r
-      {\r
-        unzFile uz = p->Ref();\r
-        closePK3(uz);\r
-        p = p->Next();\r
-      }\r
-    }\r
-    else\r
-    {\r
-      fclose(pakfile[m_nPAKIndex]);\r
-    }\r
-  }\r
-       pakopen = false;\r
-  CleanUpPakDirs();\r
-}\r
-\r
-\r
-void WINAPI InitPakFile(const char * pBasePath, const char *pName)\r
-{\r
-  if (g_numBasePaths == 0)\r
-  {\r
-    m_nPAKIndex = 0;\r
-    pakopen = false;\r
-    paktextures = NULL;\r
-  }\r
-  strcpy(g_strBasePaths[g_numBasePaths], pBasePath);\r
-  g_numBasePaths++;\r
-  \r
-  if (pName == NULL)\r
-  {\r
-    //++timo FIXME: use some kind of compatibility lib here!\r
-#if defined (__linux__) || defined (__APPLE__)\r
-    char cWork[WORK_LEN];\r
-    struct dirent *dirlist;\r
-    DIR *dir;\r
-    \r
-    dir = opendir (pBasePath);\r
-    if (dir != NULL)\r
-    {\r
-      while ((dirlist = readdir (dir)) != NULL)\r
-      {\r
-        if (strstr (dirlist->d_name, ".pk3") == NULL)\r
-          continue;\r
-        sprintf(cWork, "%s/%s", pBasePath, dirlist->d_name);\r
-        OpenPakFile(cWork);\r
-      }\r
-      closedir (dir);\r
-    }\r
-#endif\r
-#ifdef _WIN32\r
-    char cWork[WORK_LEN];\r
-    Str strPath(pBasePath);\r
-    AddSlash(strPath);\r
-    strPath += "*.pk3";\r
-    bool bGo = true;\r
-    struct _finddata_t fileinfo;\r
-    int handle = _findfirst (strPath, &fileinfo);\r
-    if (handle != -1)\r
-    {\r
-      do\r
-      {\r
-        sprintf(cWork, "%s/%s", pBasePath, fileinfo.name);\r
-        OpenPakFile(cWork);\r
-      } while (_findnext( handle, &fileinfo ) != -1);\r
-      _findclose (handle);\r
-    }\r
-#endif\r
-  }\r
-  else\r
-  {\r
-    OpenPakFile(pName);\r
-  }\r
-}\r
-\r
+/*
+   Copyright (C) 1999-2007 id Software, Inc. and contributors.
+   For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+   This file is part of GtkRadiant.
+
+   GtkRadiant is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   GtkRadiant is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GtkRadiant; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined ( __linux__ ) || defined ( __APPLE__ )
+#include <dirent.h>
+#endif
+#ifdef _WIN32
+#include <io.h>
+#endif
+#include "pakstuff.h"
+#include "unzip.h"
+#include "str.h"
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+int m_nPAKIndex;
+FILE*                   pakfile[16];
+struct PACKDirectory pakdir;
+PACKDirPtr pakdirptr = &pakdir;
+UInt16 dirsize;
+bool pakopen = false;
+int f_type;
+DIRECTORY       *paktextures = NULL;
+UInt32 PakColormapOffset;
+UInt32 PakColormapSize;
+DIRECTORY           *dirhead = NULL;
+bool g_bPK3 = false;
+char g_strBasePaths[16][1024];
+int g_numBasePaths = 0;
+
+struct PK3FileInfo
+{
+       unzFile m_zFile;
+       char *m_pName;
+       unz_s m_zInfo;
+       long m_lSize;
+       ~PK3FileInfo(){
+               delete []m_pName;
+       }
+       bool operator ==( const PK3FileInfo& rhs ) const { return strcmp( m_pName, rhs.m_pName ) == 0; }
+};
+
+#define __PATHSEPERATOR   '/'
+
+//#define LOG_PAKFAIL
+
+#ifdef LOG_PAKFAIL
+
+#if defined ( __linux__ ) || defined ( __APPLE__ )
+#include <unistd.h>
+#include <pwd.h>
+#endif
+#include <sys/types.h>
+
+class LogFile
+{
+public:
+FILE *m_pFile;
+LogFile( const char* pName ){
+#if defined ( __linux__ ) || defined ( __APPLE__ )
+       // leo: use ~/.q3a/radiant/paklog instead of /tmp/paklog.txt
+       char *home = NULL;
+
+       home = getenv( "HOME" );
+       if ( home == NULL ) {
+               uid_t id = getuid();
+               struct passwd *pwd;
+
+               setpwent();
+               while ( ( pwd = getpwent() ) != NULL )
+                       if ( pwd->pw_uid == id ) {
+                               home = pwd->pw_dir;
+                               break;
+                       }
+               endpwent();
+       }
+
+       if ( home != NULL ) {
+               char path[PATH_MAX];
+               strcpy( path, home );
+               if ( path[strlen( path ) - 1] != '/' ) {
+                       strcat( path, "/" );
+               }
+               strcat( path, ".q3a/radiant/paklog" );
+               m_pFile = fopen( path, "w" );
+       }
+       else
+#endif
+       m_pFile = fopen( pName, "w" );
+}
+~LogFile(){
+       if ( m_pFile ) {
+               fclose( m_pFile );
+       }
+}
+void Log( const char *pFormat, ... ){
+       if ( m_pFile == NULL ) {
+               return;
+       }
+
+       va_list arg_ptr;
+       va_start( arg_ptr, pFormat );
+       fprintf( m_pFile, pFormat, arg_ptr );
+       va_end( arg_ptr );
+}
+};
+
+LogFile g_LogFile( "/tmp/paklog.txt" );
+#endif
+
+template <class T> class StrPtr : public Str
+{
+protected:
+T* m_pPtr;
+StrPtr(){
+       m_pPtr = NULL;
+}
+
+StrPtr( const char *pStr, T *p ) : Str( pStr ){
+       m_pPtr = p;
+}
+
+T* Ptr(){
+       return m_pPtr;
+}
+
+T& Ref(){
+       return *m_pPtr;
+}
+
+
+};
+// PtrList
+// a list of ptrs
+//
+template <class T> class PtrList
+{
+protected:
+T *m_pPtr;
+PtrList *m_pNext;
+
+public:
+
+PtrList(){
+       m_pNext = NULL;
+       m_pPtr = NULL;
+}
+
+PtrList( T *ip ){
+       m_pNext = NULL;
+       m_pPtr = ip;
+}
+
+virtual ~PtrList(){
+       delete m_pPtr;
+}
+
+PtrList* Next(){
+       return m_pNext;
+}
+
+void Add( T *ip ){
+       PtrList *pl = this;
+       while ( pl && pl->m_pNext )
+       {
+               pl = pl->Next();
+       }
+       pl->m_pNext = new PtrList( ip );
+}
+
+void Remove(){
+       PtrList *p = m_pNext;
+       if ( p ) {
+               while ( p->m_pNext != this && p->m_pNext != NULL )
+               {
+                       p = p->m_pNext;
+               }
+               if ( p->m_pNext == this ) {
+                       p->m_pNext = m_pNext;
+               }
+       }
+}
+
+virtual PtrList* Find( T *ip ){
+       PtrList *p = m_pNext;
+       while ( p )
+       {
+               if ( *p->m_pPtr == *ip ) {
+                       return p;
+               }
+               p = p->m_pNext;
+       }
+       return NULL;
+}
+
+// remove vp from the list
+void Remove( T *ip ){
+       PtrList *p = Find( ip );
+       if ( p ) {
+               p->Remove();
+       }
+}
+
+T* Ptr(){
+       return m_pPtr;
+}
+
+T& Ref(){
+       return *m_pPtr;
+}
+
+void RemoveAll(){
+       PtrList *p = m_pNext;
+       while ( p )
+       {
+               PtrList *p2 = p;
+               p = p->m_pNext;
+               delete p2;
+       }
+}
+};
+
+
+typedef PtrList<unzFile> ZFileList;
+typedef PtrList<Str> StrList;
+typedef PtrList<PK3FileInfo> PK3List;
+
+
+StrList g_PK3TexturePaths;
+PK3List g_PK3Files;
+ZFileList g_zFiles;
+#define WORK_LEN 1024
+#define TEXTURE_PATH "textures"
+#define PATH_SEPERATORS "/\\:\0"
+
+/*
+   char* __StrDup(char* pStr)
+   {
+   if (pStr == NULL)
+    pStr = "";
+
+   return strcpy(new char[strlen(pStr)+1], pStr);
+   }
+
+   char* __StrDup(const char* pStr)
+   {
+   if (pStr == NULL)
+    pStr = "";
+
+   return strcpy(new char[strlen(pStr)+1], pStr);
+   }
+ */
+
+#define MEM_BLOCKSIZE 4096
+void* __qblockmalloc( size_t nSize ){
+       void *b;
+       // round up to threshold
+       int nAllocSize = nSize % MEM_BLOCKSIZE;
+       if ( nAllocSize > 0 ) {
+               nSize += MEM_BLOCKSIZE - nAllocSize;
+       }
+       b = malloc( nSize + 1 );
+       memset( b, 0, nSize );
+       return b;
+}
+
+void* __qmalloc( size_t nSize ){
+       void *b;
+       b = malloc( nSize + 1 );
+       memset( b, 0, nSize );
+       return b;
+}
+
+
+/*
+   ====================
+   Extract file parts
+   ====================
+ */
+void __ExtractFilePath( const char *path, char *dest ){
+       const char *src;
+
+       src = path + strlen( path ) - 1;
+
+//
+// back up until a \ or the start
+//
+       while ( src != path && *( src - 1 ) != __PATHSEPERATOR )
+               src--;
+
+       memcpy( dest, path, src - path );
+       dest[src - path] = 0;
+}
+
+void __ExtractFileName( const char *path, char *dest ){
+       const char *src;
+
+       src = path + strlen( path ) - 1;
+
+//
+// back up until a \ or the start
+//
+       while ( src != path && *( src - 1 ) != '/'
+                       && *( src - 1 ) != '\\' )
+               src--;
+
+       while ( *src )
+       {
+               *dest++ = *src++;
+       }
+       *dest = 0;
+}
+
+void __ExtractFileBase( const char *path, char *dest ){
+       const char *src;
+
+       src = path + strlen( path ) - 1;
+
+//
+// back up until a \ or the start
+//
+       while ( src != path && *( src - 1 ) != '/'
+                       && *( src - 1 ) != '\\' )
+               src--;
+
+       while ( *src && *src != '.' )
+       {
+               *dest++ = *src++;
+       }
+       *dest = 0;
+}
+
+void __ExtractFileExtension( const char *path, char *dest ){
+       const char *src;
+
+       src = path + strlen( path ) - 1;
+
+//
+// back up until a . or the start
+//
+       while ( src != path && *( src - 1 ) != '.' )
+               src--;
+       if ( src == path ) {
+               *dest = 0;  // no extension
+               return;
+       }
+
+       strcpy( dest,src );
+}
+
+
+void __ConvertDOSToUnixName( char *dst, const char *src ){
+       while ( *src )
+       {
+               if ( *src == '\\' ) {
+                       *dst = '/';
+               }
+               else{
+                       *dst = *src;
+               }
+               dst++; src++;
+       }
+       *dst = 0;
+}
+
+
+
+
+
+static void AddSlash( Str& str ){
+       int nLen = str.GetLength();
+       if ( nLen > 0 ) {
+               if ( str[nLen - 1] != '\\' && str[nLen - 1] != '/' ) {
+                       str += '\\';
+               }
+       }
+}
+
+static void FindReplace( Str& strContents, const char* pTag, const char* pValue ){
+       if ( strcmp( pTag, pValue ) == 0 ) {
+               return;
+       }
+       for ( int nPos = strContents.Find( pTag ); nPos >= 0; nPos = strContents.Find( pTag ) )
+       {
+               int nRightLen = strContents.GetLength() - strlen( pTag ) - nPos;
+               Str strLeft( strContents.Left( nPos ) );
+               Str strRight( strContents.Right( nRightLen ) );
+               strLeft += pValue;
+               strLeft += strRight;
+               strContents = strLeft;
+       }
+}
+
+
+
+
+
+void ClearFileList( FILELIST **list ){
+       FILELIST    *temp;
+
+       while ( *list )
+       {
+               temp = *list;
+               *list = ( *list )->next;
+               free( temp );
+       }
+}
+
+void ClearDirList( DIRLIST **list ){
+       DIRLIST *temp;
+
+       while ( *list )
+       {
+               temp = *list;
+               *list = ( *list )->next;
+               free( temp );
+       }
+}
+
+DIRECTORY *FindPakDir( DIRECTORY *dir, char *name ){
+       DIRECTORY   *currentPtr;
+
+       for ( currentPtr = dir; currentPtr; currentPtr = currentPtr->next )
+       {
+               if ( !stricmp( name, currentPtr->name ) ) {
+                       return currentPtr;
+               }
+       }
+       return NULL;
+}
+
+
+// LoadPK3FileList
+// ---------------
+//
+// This gets passed a file mask which we want to remove as
+// we are only interested in the directory name and any given
+// extension. Only handles explicit filenames or *.something
+//
+bool LoadPK3FileList( FILELIST **filelist, const char *pattern ){
+       char cSearch[WORK_LEN];
+       __ConvertDOSToUnixName( cSearch, pattern );
+       char cPath[WORK_LEN];
+       char cExt[WORK_LEN];
+       char cFile[WORK_LEN];
+       char cWork[WORK_LEN];
+       __ExtractFilePath( pattern, cPath );
+       __ExtractFileName( pattern, cFile );
+       __ExtractFileExtension( pattern, cExt );
+       const char *pCompare = ( strnicmp( cFile, "*.", 2 ) == 0 ) ? cExt : cFile;
+
+       PK3List *p = g_PK3Files.Next();
+       while ( p != NULL )
+       {
+               // qualify the path
+               PK3FileInfo *pKey = p->Ptr();
+               if ( strstr( pKey->m_pName, cPath ) && strstr( pKey->m_pName, pCompare ) ) {
+                       __ExtractFileName( pKey->m_pName, cWork );
+                       AddToFileListAlphabetized( filelist, cWork, 0, 0, false );
+               }
+               p = p->Next();
+       }
+       return ( *filelist ) != NULL;
+}
+
+bool GetPackFileList( FILELIST **filelist, char *pattern ){
+       char                    *str1, *str2;
+       int i;
+       DIRECTORY               *dummy = paktextures;
+       FILELIST                *temp;
+
+       if ( !pakopen ) {
+               return false;
+       }
+
+       if ( g_bPK3 ) {
+               return LoadPK3FileList( filelist, pattern );
+       }
+
+       str1 = pattern;
+
+       for ( i = 0; pattern[i] != '\0'; i++ )
+       {
+               if ( pattern[i] == '\\' ) {
+                       pattern[i] = '/';
+               }
+       }
+
+       while ( strchr( str1, '/' ) )
+       {
+               str2 = strchr( str1, '/' );
+               *str2++ = '\0';
+               dummy = FindPakDir( dummy, str1 );
+               if ( !dummy ) {
+                       return false;
+               }
+               str1 = str2;
+       }
+       for ( temp = dummy->files; temp; temp = temp->next )
+       {
+               AddToFileListAlphabetized( filelist, temp->filename, temp->offset, 0, false );
+       }
+       return true;
+}
+
+bool GetPackTextureDirs( DIRLIST **dirlist ){
+       UInt16 i;
+       char buf[57];
+
+       if ( !pakopen ) {
+               return 1;
+       }
+
+       if ( g_bPK3 ) {
+               StrList *pl = g_PK3TexturePaths.Next();
+               while ( pl != NULL )
+               {
+                       AddToDirListAlphabetized( dirlist, pl->Ref(), 0 );
+                       pl = pl->Next();
+               }
+               return true;
+       }
+
+       for ( i = 0; i < dirsize; i++ )
+       {
+               if ( !strnicmp( pakdirptr[i].name, "textures", 8 ) ) {
+                       strncpy( buf, &( pakdirptr[i].name[9] ), 46 );
+                       if ( strchr( buf, '\\' ) ) {
+                               *strchr( buf, '\\' ) = '\0';
+                       }
+                       else if ( strchr( buf, '/' ) ) {
+                               *strchr( buf, '/' ) = '\0';
+                       }
+                       else{
+                               buf[56] = '\0';
+                       }
+
+                       if ( strchr( buf, '.' ) ) {
+                               continue;
+                       }
+
+                       AddToDirListAlphabetized( dirlist, buf, 0 );
+               }
+       }
+       return true;
+}
+
+bool AddToDirListAlphabetized( DIRLIST **list, char *dirname, int from ){
+       DIRLIST *currentPtr, *previousPtr, *newPtr;
+
+       strlwr( dirname );
+       for ( currentPtr = *list; currentPtr; currentPtr = currentPtr->next )
+       {
+               if ( !stricmp( dirname, currentPtr->dirname ) ) {
+                       return false;
+               }
+       }
+       previousPtr = NULL;
+       currentPtr = *list;
+
+       if ( ( newPtr = (DIRLIST *)__qmalloc( sizeof( DIRLIST ) ) ) == NULL ) {
+               return false;
+       }
+
+       strcpy( newPtr->dirname, dirname );
+       newPtr->from = from;
+
+       while ( currentPtr != NULL && stricmp( dirname, currentPtr->dirname ) > 0 )
+       {
+               previousPtr = currentPtr;
+               currentPtr = currentPtr->next;
+       } //End while
+       if ( previousPtr == NULL ) {
+               newPtr->next = *list;
+               *list = newPtr;
+       } //End if
+       else
+       {
+               previousPtr->next = newPtr;
+               newPtr->next = currentPtr;
+       } //End else
+       return true;
+}
+
+bool AddToFileListAlphabetized( FILELIST **list, char *filename, UInt32 offset, UInt32 size, bool dirs ){
+       FILELIST    *currentPtr, *previousPtr, *newPtr;
+
+       for ( currentPtr = *list; currentPtr; currentPtr = currentPtr->next )
+       {
+               if ( !stricmp( filename, currentPtr->filename ) ) {
+                       return false;
+               }
+       }
+       previousPtr = NULL;
+       currentPtr = *list;
+
+       if ( ( newPtr = (FILELIST *)__qmalloc( sizeof( FILELIST ) ) ) == NULL ) {
+               return false;
+       }
+
+       strcpy( newPtr->filename, filename );
+       newPtr->offset = offset;
+       newPtr->size = size;
+
+       while ( currentPtr != NULL && stricmp( filename, currentPtr->filename ) > 0 )
+       {
+               previousPtr = currentPtr;
+               currentPtr = currentPtr->next;
+       } //End while
+       if ( previousPtr == NULL ) {
+               newPtr->next = *list;
+               *list = newPtr;
+       } //End if
+       else
+       {
+               previousPtr->next = newPtr;
+               newPtr->next = currentPtr;
+       } //End else
+       return true;
+}
+
+int PakLoadAnyFile( const char *filename, void **bufferptr ){
+       char cWork[WORK_LEN];
+       if ( g_bPK3 ) {
+               // leo: hack to be able to use pak files from multiple directories
+               for ( int i = 0; i < g_numBasePaths; i++ )
+               {
+                       PK3FileInfo *pInfo;
+                       Str strKey;
+                       // need to lookup the file without the base/texture path on it
+                       Str strBase( g_strBasePaths[i] );
+                       AddSlash( strBase );
+                       __ConvertDOSToUnixName( cWork, strBase );
+                       Str strFile( filename );
+                       __ConvertDOSToUnixName( strFile, strFile );
+                       strFile.MakeLower();
+                       strlwr( cWork );
+                       FindReplace( strFile, cWork, "" );
+
+                       PK3FileInfo infoFind;
+                       infoFind.m_pName = __StrDup( strFile.GetBuffer() );
+                       PK3List *pList = g_PK3Files.Find( &infoFind );
+                       if ( pList ) {
+                               pInfo = pList->Ptr();
+                               memcpy( pInfo->m_zFile, &pInfo->m_zInfo, sizeof( unz_s ) );
+                               if ( unzOpenCurrentFile( pInfo->m_zFile ) == UNZ_OK ) {
+                                       void *buffer = __qblockmalloc( pInfo->m_lSize + 1 );
+                                       int n = unzReadCurrentFile( pInfo->m_zFile, buffer, pInfo->m_lSize );
+                                       *bufferptr = buffer;
+                                       unzCloseCurrentFile( pInfo->m_zFile );
+                                       return n;
+                               }
+                       }
+               }
+
+#ifdef LOG_PAKFAIL
+               sprintf( cWork, "PAK failed on %s\n", filename );
+               g_LogFile.Log( cWork );
+#endif
+               return -1;
+       }
+
+       for ( int i = 0; i < dirsize; i++ )
+       {
+               if ( !stricmp( filename, pakdirptr[i].name ) ) {
+                       if ( fseek( pakfile[m_nPAKIndex], pakdirptr[i].offset, SEEK_SET ) >= 0 ) {
+                               void *buffer = __qmalloc( pakdirptr[i].size + 1 );
+                               ( (char *)buffer )[pakdirptr[i].size] = 0;
+                               if ( fread( buffer, 1, pakdirptr[i].size, pakfile[m_nPAKIndex] ) == pakdirptr[i].size ) {
+                                       *bufferptr = buffer;
+                                       return pakdirptr[i].size;
+                               }
+                       }
+               }
+       }
+#ifdef LOG_PAKFAIL
+       sprintf( cWork, "PAK failed on %s\n", filename );
+       g_LogFile.Log( cWork );
+#endif
+       return -1;
+}
+
+
+
+DIRECTORY *AddPakDir( DIRECTORY **dir, char *name ){
+       DIRECTORY   *currentPtr, *previousPtr, *newPtr;
+
+       for ( currentPtr = *dir; currentPtr; currentPtr = currentPtr->next )
+       {
+               if ( !stricmp( name, currentPtr->name ) ) {
+                       return currentPtr;
+               }
+       }
+       previousPtr = NULL;
+       currentPtr = *dir;
+
+       if ( ( newPtr = (DIRECTORY *)__qmalloc( sizeof( DIRECTORY ) ) ) == NULL ) {
+               return NULL;
+       }
+
+       strcpy( newPtr->name, name );
+       newPtr->files = NULL;
+
+       while ( currentPtr != NULL && stricmp( name, currentPtr->name ) > 0 )
+       {
+               previousPtr = currentPtr;
+               currentPtr = currentPtr->next;
+       }
+       if ( previousPtr == NULL ) {
+               newPtr->next = *dir;
+               *dir = newPtr;
+       }
+       else
+       {
+               previousPtr->next = newPtr;
+               newPtr->next = currentPtr;
+       }
+       return newPtr;
+}
+
+
+// OpenPK3
+// -------
+// Opens a PK3 ( or zip ) file and creates a list of filenames
+// and zip info structures
+//
+bool OpenPK3( const char *filename ){
+       char cFilename[WORK_LEN];
+       char cName[WORK_LEN];
+       char cWork[WORK_LEN];
+       unz_file_info zInfo;
+       unzFile *zFile = new unzFile( unzOpen( filename ) );
+       g_zFiles.Add( zFile );
+       if ( zFile != NULL ) {
+               int nStatus = unzGoToFirstFile( *zFile );
+               while ( nStatus == UNZ_OK )
+               {
+                       cFilename[0] = '\0';
+                       unzGetCurrentFileInfo( *zFile, &zInfo, cFilename, WORK_LEN, NULL, 0, NULL, 0 );
+                       strlwr( cFilename );
+                       __ConvertDOSToUnixName( cWork, cFilename );
+                       if ( strstr( cWork, "." ) != NULL ) {
+                               PK3FileInfo *pInfo = new PK3FileInfo();
+                               pInfo->m_pName = __StrDup( cWork );
+                               memcpy( &pInfo->m_zInfo, (unz_s*)*zFile, sizeof( unz_s ) );
+                               pInfo->m_lSize = zInfo.uncompressed_size;
+                               pInfo->m_zFile = *zFile;
+                               g_PK3Files.Add( pInfo );
+                       }
+                       char *p = strstr( cFilename, TEXTURE_PATH );
+                       if ( p != NULL ) {
+                               // FIXME: path differences per os ?
+                               // catch solo directory entry
+                               if ( strlen( p ) > strlen( TEXTURE_PATH ) + 1 ) {
+                                       // skip textures + path seperator
+                                       p += strlen( TEXTURE_PATH ) + 1;
+                                       int nEnd = strcspn( p, PATH_SEPERATORS );
+                                       strncpy( cName, p, nEnd );
+                                       cName[nEnd] = '\0';
+
+                                       bool bFound = false;
+                                       StrList *pl = g_PK3TexturePaths.Next();
+                                       while ( pl != NULL )
+                                       {
+                                               if ( strcmpi( pl->Ref(), cName ) == 0 ) {
+                                                       // already have this, continue
+                                                       bFound = true;
+                                                       break;
+                                               }
+                                               pl = pl->Next();
+                                       }
+                                       if ( !bFound ) {
+                                               g_PK3TexturePaths.Add( new Str( cName ) );
+                                       }
+                               }
+                       }
+                       nStatus = unzGoToNextFile( *zFile );
+               }
+       }
+       return ( zFile != NULL );
+}
+
+void closePK3( unzFile zf ){
+       unzClose( zf );
+}
+
+void OpenPakFile( const char *filename ){
+       if ( !pakopen ) {
+               paktextures = NULL;
+       }
+
+       pakopen = g_bPK3 = OpenPK3( filename );
+}
+
+void ClearPaKDir( DIRECTORY **dir ){
+       DIRECTORY   *d1 = *dir, *d2;
+
+       while ( d1 )
+       {
+               ClearFileList( &( d1->files ) );
+               d2 = d1;
+               d1 = d1->next;
+               free( d2 );
+       }
+}
+
+void CleanUpPakDirs(){
+       ClearPaKDir( &paktextures );
+       paktextures = NULL;
+       dirhead = NULL;
+       g_PK3TexturePaths.RemoveAll();
+       g_PK3Files.RemoveAll();
+}
+
+void ClosePakFile( void ){
+       if ( pakopen ) {
+               if ( g_bPK3 ) {
+                       ZFileList *p = g_zFiles.Next();
+                       while ( p != NULL )
+                       {
+                               unzFile uz = p->Ref();
+                               closePK3( uz );
+                               p = p->Next();
+                       }
+               }
+               else
+               {
+                       fclose( pakfile[m_nPAKIndex] );
+               }
+       }
+       pakopen = false;
+       CleanUpPakDirs();
+}
+
+
+void WINAPI InitPakFile( const char * pBasePath, const char *pName ){
+       if ( g_numBasePaths == 0 ) {
+               m_nPAKIndex = 0;
+               pakopen = false;
+               paktextures = NULL;
+       }
+       strcpy( g_strBasePaths[g_numBasePaths], pBasePath );
+       g_numBasePaths++;
+
+       if ( pName == NULL ) {
+               //++timo FIXME: use some kind of compatibility lib here!
+#if defined ( __linux__ ) || defined ( __APPLE__ )
+               char cWork[WORK_LEN];
+               struct dirent *dirlist;
+               DIR *dir;
+
+               dir = opendir( pBasePath );
+               if ( dir != NULL ) {
+                       while ( ( dirlist = readdir( dir ) ) != NULL )
+                       {
+                               if ( strstr( dirlist->d_name, ".pk3" ) == NULL ) {
+                                       continue;
+                               }
+                               sprintf( cWork, "%s/%s", pBasePath, dirlist->d_name );
+                               OpenPakFile( cWork );
+                       }
+                       closedir( dir );
+               }
+#endif
+#ifdef _WIN32
+               char cWork[WORK_LEN];
+               Str strPath( pBasePath );
+               AddSlash( strPath );
+               strPath += "*.pk3";
+               bool bGo = true;
+               struct _finddata_t fileinfo;
+               int handle = _findfirst( strPath, &fileinfo );
+               if ( handle != -1 ) {
+                       do
+                       {
+                               sprintf( cWork, "%s/%s", pBasePath, fileinfo.name );
+                               OpenPakFile( cWork );
+                       } while ( _findnext( handle, &fileinfo ) != -1 );
+                       _findclose( handle );
+               }
+#endif
+       }
+       else
+       {
+               OpenPakFile( pName );
+       }
+}