]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - libs/pak/pakstuff.cpp
more eol-style
[xonotic/netradiant.git] / libs / pak / pakstuff.cpp
index f3ae725e9571bb057b9014d75661bf5a09710f29..b6028cc79aee9d4a2084a29d42acb8f623246694 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);
+  }
+}
+