Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

pakstuff.cpp (24419B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 
     23 #include <stdio.h>
     24 #include <stdarg.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <windows.h>
     28 #include "io.h"
     29 #include "pakstuff.h"
     30 #include "unzip.h"
     31 //#include "cmdlib.h"
     32 #include "str.h"
     33 
     34 int m_nPAKIndex;
     35 FILE* pakfile[16];
     36 struct PACKDirectory	pakdir;
     37 PACKDirPtr				pakdirptr = &pakdir;
     38 UInt16					dirsize;
     39 boolean					pakopen = false;
     40 int						f_type;
     41 DIRECTORY				*paktextures = NULL;
     42 boolean					HavePakColormap;
     43 UInt32					PakColormapOffset;
     44 UInt32					PakColormapSize;
     45 DIRECTORY				*dirhead = NULL;
     46 boolean g_bPK3 = false;
     47 char g_strBasePath[1024];
     48 
     49 struct PK3FileInfo
     50 {
     51   unzFile m_zFile;
     52   char *m_pName;
     53   unz_s m_zInfo;
     54   long m_lSize;
     55   ~PK3FileInfo()
     56   {
     57     delete []m_pName;
     58   }
     59   bool operator ==(const PK3FileInfo& rhs) const { return strcmp(m_pName, rhs.m_pName) == 0; }
     60 };
     61 
     62 #define __PATHSEPERATOR   '/'
     63 
     64 #define LOG_PAKFAIL
     65 
     66 #ifdef LOG_PAKFAIL
     67 
     68 class LogFile
     69 {
     70 public:
     71   FILE *m_pFile;
     72   LogFile(const char* pName)
     73   {
     74     m_pFile = fopen(pName, "w");
     75   }
     76   ~LogFile()
     77   {
     78     if (m_pFile)
     79     {
     80       fclose(m_pFile);
     81     }
     82   }
     83   void Log(const char *pFormat, ...)
     84   {
     85     va_list arg_ptr;
     86     va_start(arg_ptr, pFormat);
     87     fprintf(m_pFile, pFormat, arg_ptr);
     88     va_end(arg_ptr);
     89   }
     90 };
     91 
     92 LogFile g_LogFile("c:\\paklog.txt");
     93 #endif
     94 
     95 template <class T> class StrPtr : public Str
     96 {
     97 protected:
     98   T* m_pPtr;
     99   StrPtr()
    100   {
    101     m_pPtr = NULL;
    102   }
    103 
    104   StrPtr(const char *pStr, T *p) : Str(pStr)
    105   {
    106     m_pPtr = p;
    107   }
    108 
    109   T* Ptr()
    110   {
    111     return m_pPtr;
    112   }
    113 
    114   T& Ref()
    115   {
    116     return *m_pPtr;
    117   }
    118     
    119 
    120 };
    121 // PtrList
    122 // a list of ptrs
    123 // 
    124 template <class T> class PtrList
    125 {
    126 protected:
    127   T *m_pPtr;
    128   PtrList *m_pNext;
    129 
    130 public:
    131 
    132   PtrList()
    133   {
    134     m_pNext = NULL;
    135     m_pPtr = NULL;
    136   }
    137 
    138   PtrList(T *ip)
    139   {
    140     m_pNext = NULL;
    141     m_pPtr = ip;
    142   }
    143 
    144   ~PtrList()
    145   {
    146     delete m_pPtr;
    147   }
    148 
    149   PtrList* Next()
    150   {
    151     return m_pNext;
    152   }
    153 
    154   void Add(T *ip)
    155   {
    156     PtrList *pl = this;
    157     while (pl && pl->m_pNext)
    158     {
    159       pl = pl->Next();
    160     }
    161     pl->m_pNext = new PtrList(ip);
    162   }
    163 
    164   void Remove()
    165   {
    166     PtrList *p = m_pNext;
    167     if (p)
    168     {
    169       while (p->m_pNext != this && p->m_pNext != NULL)
    170       {
    171         p = p->m_pNext;
    172       }
    173       if (p->m_pNext == this)
    174       {
    175         p->m_pNext = m_pNext;
    176       }
    177     }
    178   }
    179 
    180   virtual PtrList* Find(T *ip)
    181   {
    182     PtrList *p = m_pNext;
    183     while (p)
    184     {
    185       if (*p->m_pPtr == *ip)
    186       {
    187         return p;
    188       }
    189       p = p->m_pNext;
    190     }
    191     return NULL;
    192   }
    193 
    194   // remove vp from the list
    195   void Remove(T *ip)
    196   {
    197     PtrList *p = Find(ip);
    198     if (p)
    199     {
    200       p->Remove();
    201     }
    202   }
    203 
    204   T* Ptr()
    205   {
    206     return m_pPtr;
    207   }
    208 
    209   T& Ref()
    210   {
    211     return *m_pPtr;
    212   }
    213 
    214   void RemoveAll()
    215   {
    216     PtrList *p = m_pNext;
    217     while (p)
    218     {
    219       PtrList *p2 = p;
    220       p = p->m_pNext;
    221       delete p2;
    222     }
    223   }
    224 };
    225 
    226 
    227 typedef PtrList<unzFile> ZFileList;
    228 typedef PtrList<Str> StrList;
    229 typedef PtrList<PK3FileInfo> PK3List;
    230 
    231 
    232 StrList g_PK3TexturePaths;
    233 PK3List g_PK3Files;
    234 ZFileList g_zFiles;
    235 #define WORK_LEN 1024
    236 #define TEXTURE_PATH "textures"
    237 #define PATH_SEPERATORS "/\\:\0"
    238 
    239 
    240 char* __StrDup(char* pStr)
    241 { 
    242   if (pStr)
    243   {
    244     return strcpy(new char[strlen(pStr)+1], pStr); 
    245   }
    246   return NULL;
    247 }
    248 
    249 char* __StrDup(const char* pStr)
    250 { 
    251   if (pStr)
    252   {
    253     return strcpy(new char[strlen(pStr)+1], pStr); 
    254   }
    255   return NULL;
    256 }
    257 
    258 #define MEM_BLOCKSIZE 4096
    259 void* __qblockmalloc(size_t nSize)
    260 {
    261 	void *b;
    262   // round up to threshold
    263   int nAllocSize = nSize % MEM_BLOCKSIZE;
    264   if ( nAllocSize > 0)
    265   {
    266     nSize += MEM_BLOCKSIZE - nAllocSize;
    267   }
    268 	b = malloc(nSize + 1);
    269 	memset (b, 0, nSize);
    270 	return b;
    271 }
    272 
    273 void* __qmalloc (size_t nSize)
    274 {
    275 	void *b;
    276 	b = malloc(nSize + 1);
    277 	memset (b, 0, nSize);
    278 	return b;
    279 }
    280 
    281 
    282 /*
    283 ====================
    284 Extract file parts
    285 ====================
    286 */
    287 void __ExtractFilePath (const char *path, char *dest)
    288 {
    289 	const char *src;
    290 
    291 	src = path + strlen(path) - 1;
    292 
    293 //
    294 // back up until a \ or the start
    295 //
    296 	while (src != path && *(src-1) != __PATHSEPERATOR)
    297 		src--;
    298 
    299 	memcpy (dest, path, src-path);
    300 	dest[src-path] = 0;
    301 }
    302 
    303 void __ExtractFileName (const char *path, char *dest)
    304 {
    305 	const char *src;
    306 
    307 	src = path + strlen(path) - 1;
    308 
    309 //
    310 // back up until a \ or the start
    311 //
    312 	while (src != path && *(src-1) != '/' 
    313 		 && *(src-1) != '\\' )
    314 		src--;
    315 
    316 	while (*src)
    317 	{
    318 		*dest++ = *src++;
    319 	}
    320 	*dest = 0;
    321 }
    322 
    323 void __ExtractFileBase (const char *path, char *dest)
    324 {
    325 	const char *src;
    326 
    327 	src = path + strlen(path) - 1;
    328 
    329 //
    330 // back up until a \ or the start
    331 //
    332 	while (src != path && *(src-1) != '/' 
    333 		 && *(src-1) != '\\' )
    334 		src--;
    335 
    336 	while (*src && *src != '.')
    337 	{
    338 		*dest++ = *src++;
    339 	}
    340 	*dest = 0;
    341 }
    342 
    343 void __ExtractFileExtension (const char *path, char *dest)
    344 {
    345 	const char *src;
    346 
    347 	src = path + strlen(path) - 1;
    348 
    349 //
    350 // back up until a . or the start
    351 //
    352 	while (src != path && *(src-1) != '.')
    353 		src--;
    354 	if (src == path)
    355 	{
    356 		*dest = 0;	// no extension
    357 		return;
    358 	}
    359 
    360 	strcpy (dest,src);
    361 }
    362 
    363 
    364 void __ConvertDOSToUnixName( char *dst, const char *src )
    365 {
    366 	while ( *src )
    367 	{
    368 		if ( *src == '\\' )
    369 			*dst = '/';
    370 		else
    371 			*dst = *src;
    372 		dst++; src++;
    373 	}
    374 	*dst = 0;
    375 }
    376 
    377 
    378 
    379 
    380 
    381 void AddSlash(Str& str)
    382 {
    383   int nLen = str.GetLength();
    384   if (nLen > 0)
    385   {
    386     if (str[nLen-1] != '\\' && str[nLen-1] != '/')
    387       str += '\\';
    388   }
    389 }
    390 
    391 void FindReplace(Str& strContents, const char* pTag, const char* pValue)
    392 {
    393   if (strcmp(pTag, pValue) == 0)
    394     return;
    395   for (int nPos = strContents.Find(pTag); nPos >= 0; nPos = strContents.Find(pTag))
    396   {
    397     int nRightLen = strContents.GetLength() - strlen(pTag) - nPos;
    398     Str strLeft(strContents.Left(nPos));
    399     Str strRight(strContents.Right(nRightLen));
    400     strLeft += pValue;
    401     strLeft += strRight;
    402     strContents = strLeft;
    403   }
    404 }
    405 
    406 
    407 
    408 
    409 
    410 void ProgError(char *errstr, ...)
    411 {
    412   va_list args;
    413 
    414   va_start(args, errstr);
    415   printf("\nProgram Error: *** ");
    416   vprintf(errstr, args);
    417   printf(" ***\n");
    418   va_end(args);
    419   exit(5);
    420 }
    421 
    422 boolean ReadBytes(FILE *file, void *addr, UInt32 size)
    423 {
    424   while (size > 0x8000)
    425     {
    426       if (fread(addr, 1, 0x8000, file) != 0x8000)
    427 	return false;
    428       addr = (char *)addr + 0x8000;
    429       size -= 0x8000;
    430     }
    431   if (fread(addr, 1, size, file) != size)
    432     return false;
    433   return true;
    434 }
    435 int ReadMagic(FILE *file)
    436 {
    437   UInt8 buf[4];
    438 
    439   if (ReadBytes(file, buf, 4) == FALSE)
    440     return FTYPE_ERROR;
    441   if (!strncmp(reinterpret_cast<const char*>(&buf[0]), "IWAD", 4))
    442     return FTYPE_IWAD;
    443   if (!strncmp(reinterpret_cast<const char*>(&buf[0]), "PWAD", 4))
    444     return FTYPE_PWAD;
    445   if (!strncmp(reinterpret_cast<const char*>(&buf[0]), "PACK", 4))
    446     return FTYPE_PACK;
    447   if (!strncmp(reinterpret_cast<const char*>(&buf[0]), "WAD2", 4))
    448     return FTYPE_WAD2;
    449   if (buf[0] == 0x17 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0)
    450     return FTYPE_BSP;
    451   if (!strncmp(reinterpret_cast<const char*>(&buf[0]), "IDPO", 4))
    452     return FTYPE_MODEL;
    453   if (!strncmp(reinterpret_cast<const char*>(&buf[0]), "IDSP", 4))
    454     return FTYPE_SPRITE;
    455   if (!strncmp(reinterpret_cast<const char*>(&buf[0]), "RIFF", 4))
    456     return FTYPE_WAV;
    457   if (!strncmp(reinterpret_cast<const char*>(&buf[0]), ".snd", 4))
    458     return FTYPE_AU;
    459   if (buf[0] == 'P')
    460     {
    461       if (buf[1] == '1')
    462 	return FTYPE_PBM_ASC;
    463       if (buf[1] == '2')
    464 	return FTYPE_PGM_ASC;
    465       if (buf[1] == '3')
    466 	return FTYPE_PPM_ASC;
    467       if (buf[1] == '4')
    468 	return FTYPE_PBM_RAW;
    469       if (buf[1] == '5')
    470 	return FTYPE_PGM_RAW;
    471       if (buf[1] == '6')
    472 	return FTYPE_PPM_RAW;
    473     }
    474   if (buf[0] == 'B' && buf[1] == 'M')
    475     return FTYPE_BMP;
    476   if (!strncmp(reinterpret_cast<const char*>(&buf[0]), "GIF8", 4))
    477     return FTYPE_GIF;
    478   if (buf[0] == 0x0a && buf[1] == 0x05 && buf[2] == 0x01 && buf[3] == 0x08)
    479     return FTYPE_PCX;
    480   return FTYPE_UNKNOWN;
    481 }
    482 FILE *OpenFileReadMagic(const char *filename, int *ftype_r)
    483 {
    484   FILE *f;
    485 
    486   *ftype_r = FTYPE_ERROR;
    487   if ((f = fopen(filename, "rb")) == NULL)
    488     return NULL;
    489   *ftype_r = ReadMagic(f);
    490   if (*ftype_r == FTYPE_ERROR)
    491     {
    492       fclose(f);
    493       return NULL;
    494     }
    495   return f;
    496 }
    497 boolean WriteBytes(FILE *file, void *addr, UInt32 size)
    498 {
    499   while (size > 0x8000)
    500     {
    501       if (fwrite(addr, 1, 0x8000, file) != 0x8000)
    502 	return FALSE;
    503       addr = (char *)addr + 0x8000;
    504       size -= 0x8000;
    505     }
    506   if (fwrite(addr, 1, size, file) != size)
    507     return FALSE;
    508   return TRUE;
    509 }
    510 char *ConvertFilePath(char *filename)
    511 {
    512   char *cp;
    513   
    514   if (filename == NULL)
    515     ProgError("BUG: cannot convert a NULL pathname");
    516   for (cp = filename; *cp; cp++)
    517     if (*cp == '/' || *cp == '\\')
    518       {
    519 #ifdef QEU_DOS
    520 	*cp = '\\';
    521 #else
    522 	*cp = '/';
    523 #endif
    524       }
    525   return filename;
    526 }
    527 
    528 /*
    529  * Read the PACK directory into memory.  The optional offset to the
    530  * start of the PACK file is given in "offset".  The number of files in
    531  * the directory is returned in *dirsize_r.
    532  */
    533 PACKDirPtr ReadPACKDirectory(FILE *packfile, UInt32 offset, UInt16 *dirsize_r)
    534 {
    535   PACKDirPtr dir;
    536   UInt32     pos, size;
    537   UInt16     max, i;
    538 
    539   *dirsize_r = 0;
    540   if (packfile == NULL)
    541     return NULL;
    542   if ((fseek(packfile, offset, SEEK_SET) < 0)
    543       || (ReadMagic(packfile) != FTYPE_PACK)
    544       || (ReadInt32(packfile, &pos) == FALSE)
    545       || (ReadInt32(packfile, &size) == FALSE)
    546       || (size == 0L)
    547       || (size / sizeof(struct PACKDirectory) > 65535L)
    548       || (fseek(packfile, offset + pos, SEEK_SET) < 0))
    549     return NULL;
    550   dir = (PACKDirPtr)__qmalloc(size);
    551   max = (UInt16)(size / sizeof(struct PACKDirectory));
    552   for (i = 0; i < max; i++)
    553     {
    554       if (ReadBytes(packfile, &dir[i], sizeof(struct PACKDirectory)) == FALSE)
    555 	{
    556 	  free(dir);
    557 	  return NULL;
    558 	}
    559       ConvertFilePath(dir[i].name);
    560       dir[i].offset = SwapInt32(dir[i].offset);
    561       dir[i].size = SwapInt32(dir[i].size);
    562     }
    563   *dirsize_r = max;
    564   return dir;
    565 }
    566 
    567 /*
    568  * Print the contents of the PACK directory in "outf".
    569  */
    570 void DumpPACKDirectory(FILE *outf, PACKDirPtr dir, UInt16 dirsize)
    571 {
    572 	UInt16 i;
    573 	UInt32 sum;
    574 	char   buf[57];
    575 
    576 	if (outf == NULL || dir == NULL || dirsize == 0)
    577 		return;
    578 	fprintf(outf, "num    offset     size    file name\n");
    579 	fprintf(outf, "       (hex)      (dec)\n");
    580 	sum = 0L;
    581 	for (i = 0; i < dirsize; i++)
    582 	{
    583 		if(!strnicmp(dir[i].name, "textures", 8))
    584 		{
    585    	   strncpy(buf, dir[i].name, 56);
    586 	      buf[56] = '\0';
    587       	fprintf(outf, "%3u  0x%08lx  %6ld   %s\n",
    588 		      i, dir[i].offset, dir[i].size, buf);
    589 	      sum += dir[i].size;
    590 		}
    591     }
    592 	fprintf(outf, "\nTotal size for %3u entries:  %7lu bytes.\n", dirsize, sum);
    593 	fprintf(outf, "Size of the PACK directory:  %7lu bytes.\n",
    594 		(UInt32)dirsize * (UInt32)sizeof(struct PACKDirectory));
    595 	fprintf(outf, "Total (header + data + dir): %7lu bytes.\n",
    596 		12L + sum + (UInt32)dirsize * (UInt32)sizeof(struct PACKDirectory));
    597 }
    598 
    599 void ClearFileList(FILELIST **list)
    600 {
    601 	FILELIST	*temp;
    602 
    603 	while(*list)
    604 	{
    605 		temp = *list;
    606 		*list = (*list)->next;
    607 		free(temp);
    608 	}
    609 }
    610 
    611 void ClearDirList(DIRLIST **list)
    612 {
    613 	DIRLIST	*temp;
    614 
    615 	while(*list)
    616 	{
    617 		temp = *list;
    618 		*list = (*list)->next;
    619 		free(temp);
    620 	}
    621 }
    622 
    623 DIRECTORY *FindPakDir(DIRECTORY *dir, char *name)
    624 {
    625 	DIRECTORY	*currentPtr;
    626 
    627 	for(currentPtr = dir; currentPtr; currentPtr = currentPtr->next)
    628 	{
    629 		if(!stricmp(name, currentPtr->name))
    630 		{
    631 			return currentPtr;
    632 		}
    633 	}
    634 	return NULL;
    635 }
    636 
    637 
    638 // LoadPK3FileList
    639 // ---------------
    640 //
    641 // This gets passed a file mask which we want to remove as 
    642 // we are only interested in the directory name and any given
    643 // extension. Only handles explicit filenames or *.something
    644 //
    645 boolean LoadPK3FileList(FILELIST **filelist, const char *pattern)
    646 {
    647   char cSearch[WORK_LEN];
    648 	__ConvertDOSToUnixName( cSearch, pattern );
    649   char cPath[WORK_LEN];
    650   char cExt[WORK_LEN];
    651   char cFile[WORK_LEN];
    652   char cWork[WORK_LEN];
    653   __ExtractFilePath(pattern, cPath);
    654   __ExtractFileName(pattern, cFile);
    655   __ExtractFileExtension(pattern, cExt);
    656   const char *pCompare = (strnicmp(cFile, "*.", 2) == 0) ? cExt : cFile;
    657   strcpy(cWork, cPath);
    658   sprintf(cPath, "textures/%s", cWork);
    659 
    660   PK3List *p = g_PK3Files.Next();
    661   while (p != NULL)
    662   {
    663     // qualify the path
    664     PK3FileInfo *pKey = p->Ptr();
    665     if (strstr(pKey->m_pName, cPath) && strstr(pKey->m_pName, pCompare))
    666     {
    667       __ExtractFileName(pKey->m_pName, cWork); 
    668       AddToFileListAlphabetized(filelist, cWork, 0, 0, false);
    669     }
    670     p = p->Next();
    671   }
    672   return (*filelist) != NULL;
    673 }
    674 
    675 boolean GetPackFileList(FILELIST **filelist, char *pattern)
    676 {
    677 	char					*str1, *str2;
    678 	int						i;
    679 	DIRECTORY				*dummy = paktextures;
    680 	FILELIST				*temp;
    681 
    682 	if (!pakopen)
    683 		return false;
    684 
    685   if (g_bPK3)
    686   {
    687     return LoadPK3FileList(filelist, pattern);
    688   }
    689 
    690 	str1 = pattern;
    691 
    692 	for(i = 0; pattern[i] != '\0'; i++)
    693 	{
    694 		if(pattern[i] == '\\')
    695 			pattern[i] = '/';
    696 	}
    697 
    698 	while(strchr(str1, '/'))
    699 	{
    700 		str2 = strchr(str1, '/');
    701 		*str2++ = '\0';
    702 		dummy = FindPakDir(dummy, str1);
    703 		if(!dummy)
    704 			return false;
    705 		str1 = str2;
    706 	}
    707 	for(temp = dummy->files; temp; temp=temp->next)
    708 	{
    709 	  AddToFileListAlphabetized(filelist, temp->filename, temp->offset, 0, false);
    710 	}
    711 	return true;
    712 }
    713 
    714 boolean GetPackTextureDirs(DIRLIST **dirlist)
    715 {
    716 	UInt16					i;
    717 	char					buf[57];
    718 
    719 	if (!pakopen)
    720 		return 1;
    721 
    722   if (g_bPK3)
    723   {
    724     StrList *pl = g_PK3TexturePaths.Next();
    725     while (pl != NULL)
    726     {
    727       AddToDirListAlphabetized(dirlist, pl->Ref(), 0);
    728       pl = pl->Next();
    729     }
    730     return true;
    731   }
    732 
    733 	for (i = 0; i < dirsize; i++)
    734 	{
    735 		if(!strnicmp(pakdirptr[i].name, "textures", 8))
    736 		{
    737 			strncpy(buf, &(pakdirptr[i].name[9]), 46);
    738 			if(strchr(buf, '\\'))
    739 	      	*strchr(buf, '\\') = '\0';
    740 			else if(strchr(buf, '/'))
    741 	      	*strchr(buf, '/') = '\0';
    742 			else
    743 	      	buf[56] = '\0';
    744 
    745 			if(strchr(buf, '.'))
    746 				continue;
    747 
    748 			AddToDirListAlphabetized(dirlist, buf, 0);
    749 		}
    750 	}
    751 	return true;
    752 }
    753 
    754 boolean AddToDirListAlphabetized(DIRLIST **list, char *dirname, int from)
    755 {
    756 	DIRLIST	*currentPtr, *previousPtr, *newPtr;
    757 
    758 	strlwr(dirname);
    759 	for(currentPtr = *list; currentPtr; currentPtr = currentPtr->next)
    760 	{
    761 		if(!stricmp(dirname, currentPtr->dirname))
    762 		{
    763 			return false;
    764 		}
    765 	}
    766 	previousPtr = NULL;
    767 	currentPtr = *list;
    768 
    769 	if((newPtr = (DIRLIST *)__qmalloc(sizeof(DIRLIST))) == NULL)
    770 		return false;
    771 
    772 	strcpy(newPtr->dirname, dirname);
    773 	newPtr->from = from;
    774 
    775 	while(currentPtr != NULL && stricmp(dirname, currentPtr->dirname) > 0)
    776 	{
    777 		previousPtr = currentPtr;
    778 		currentPtr = currentPtr->next;
    779 	} //End while
    780 	if(previousPtr == NULL)
    781 	{
    782 		newPtr->next = *list;
    783 		*list = newPtr;
    784 	} //End if
    785 	else
    786 	{
    787 		previousPtr->next = newPtr;
    788 		newPtr->next = currentPtr;
    789 	} //End else
    790 	return true;
    791 }
    792 
    793 boolean AddToFileListAlphabetized(FILELIST **list, char *filename, UInt32 offset, UInt32 size, boolean dirs)
    794 {
    795 	FILELIST	*currentPtr, *previousPtr, *newPtr;
    796 
    797 	for(currentPtr = *list; currentPtr; currentPtr = currentPtr->next)
    798 	{
    799 		if(!stricmp(filename, currentPtr->filename))
    800 		{
    801 			return false;
    802 		}
    803 	}
    804 	previousPtr = NULL;
    805 	currentPtr = *list;
    806 
    807 	if((newPtr = (FILELIST *)__qmalloc(sizeof(FILELIST))) == NULL)
    808 		return false;
    809 
    810 	strcpy(newPtr->filename, filename);
    811 	newPtr->offset = offset;
    812 	newPtr->size = size;
    813 
    814 	while(currentPtr != NULL && stricmp(filename, currentPtr->filename) > 0)
    815 	{
    816 		previousPtr = currentPtr;
    817 		currentPtr = currentPtr->next;
    818 	} //End while
    819 	if(previousPtr == NULL)
    820 	{
    821 		newPtr->next = *list;
    822 		*list = newPtr;
    823 	} //End if
    824 	else
    825 	{
    826 		previousPtr->next = newPtr;
    827 		newPtr->next = currentPtr;
    828 	} //End else
    829 	return true;
    830 }
    831 
    832 boolean PakLoadFile(const char *filename, void **bufferptr)
    833 {
    834 	FILELIST	*p = NULL;
    835 	DIRECTORY	*dummy;
    836 	void		*buffer;
    837 	char		*str1, *str2;
    838 
    839 	if(!pakopen)
    840 		return false;
    841 
    842   Str str(filename);
    843   __ConvertDOSToUnixName(str, str);
    844 
    845 	dummy = paktextures;
    846 	str1 = str;
    847 
    848 	while(strchr(str1, '/'))
    849 	{
    850 		str2 = strchr(str1, '/');
    851 		*str2++ = '\0';
    852 		dummy = FindPakDir(dummy, str1);
    853 		if(!dummy)
    854 			return false;
    855 		str1 = str2;
    856 	}
    857 
    858   // FIXME: add error handling routines
    859 	for(p = dummy->files; p; p = p->next)
    860 	{
    861 		if(!stricmp(str1, p->filename))
    862 		{
    863 			if (fseek(pakfile[m_nPAKIndex], p->offset, SEEK_SET) < 0)
    864 			{
    865 				//Sys_Printf("Unexpected EOF in pakfile\n");
    866 				return false;
    867 			}
    868 			if((buffer = __qmalloc(p->size+5)) == NULL)
    869 				//Error("Could not allocate memory");
    870 	
    871 			if(fread(buffer, 1, p->size, pakfile[m_nPAKIndex]) != p->size)
    872 			{
    873 				//Sys_Printf("Error reading %s from pak\n", str1);
    874 				free(buffer);
    875 				return false;
    876 			}
    877 			*bufferptr = buffer;
    878 			return true;
    879 		}
    880 	}
    881 	return false;
    882 }
    883 
    884 int PakLoadAnyFile(const char *filename, void **bufferptr)
    885 {
    886   char cWork[WORK_LEN];
    887   if (g_bPK3)
    888   {
    889     PK3FileInfo *pInfo;
    890     Str strKey;
    891     // need to lookup the file without the base/texture path on it
    892     Str strBase(g_strBasePath);
    893     AddSlash(strBase);
    894     __ConvertDOSToUnixName(cWork, strBase);
    895     Str strFile(filename);
    896     __ConvertDOSToUnixName(strFile, strFile);
    897     strFile.MakeLower();
    898     strlwr(cWork);
    899     FindReplace(strFile, cWork, "");
    900 
    901     PK3FileInfo infoFind;
    902     infoFind.m_pName = __StrDup(strFile.GetBuffer());
    903     PK3List *pList = g_PK3Files.Find(&infoFind);
    904     if (pList)
    905     {
    906       pInfo = pList->Ptr();
    907       memcpy(pInfo->m_zFile, &pInfo->m_zInfo, sizeof(unz_s));
    908       if (unzOpenCurrentFile(pInfo->m_zFile) == UNZ_OK)
    909       {
    910         void *buffer = __qblockmalloc(pInfo->m_lSize+1);
    911         int n = unzReadCurrentFile(pInfo->m_zFile , buffer, pInfo->m_lSize);
    912         *bufferptr = buffer;
    913         unzCloseCurrentFile(pInfo->m_zFile);
    914         return n;
    915       }
    916     }
    917 #ifdef LOG_PAKFAIL
    918     sprintf(cWork, "PAK failed on %s\n", filename);
    919     g_LogFile.Log(cWork);
    920 #endif
    921     return -1;
    922   }
    923 
    924 	for (int i = 0; i < dirsize; i++)
    925 	{
    926 		if(!stricmp(filename, pakdirptr[i].name))
    927 		{
    928 			if (fseek(pakfile[m_nPAKIndex], pakdirptr[i].offset, SEEK_SET) >= 0)
    929       {
    930 	      void *buffer = __qmalloc (pakdirptr[i].size+1);
    931 	      ((char *)buffer)[pakdirptr[i].size] = 0;
    932 			  if (fread(buffer, 1, pakdirptr[i].size, pakfile[m_nPAKIndex]) == pakdirptr[i].size)
    933         {
    934           *bufferptr = buffer;
    935           return pakdirptr[i].size;
    936         }
    937       }
    938 		}
    939 	}
    940 #ifdef LOG_PAKFAIL
    941     sprintf(cWork, "PAK failed on %s\n", filename);
    942     g_LogFile.Log(cWork);
    943 #endif
    944   return -1;
    945 }
    946 
    947 
    948 
    949 DIRECTORY *AddPakDir(DIRECTORY **dir, char *name)
    950 {
    951 	DIRECTORY	*currentPtr, *previousPtr, *newPtr;
    952 
    953 	for(currentPtr = *dir; currentPtr; currentPtr = currentPtr->next)
    954 	{
    955 		if(!stricmp(name, currentPtr->name))
    956 		{
    957 			return currentPtr;
    958 		}
    959 	}
    960 	previousPtr = NULL;
    961 	currentPtr = *dir;
    962 
    963 	if((newPtr = (DIRECTORY *)__qmalloc(sizeof(DIRECTORY))) == NULL)
    964 		return NULL;
    965 
    966 	strcpy(newPtr->name, name);
    967 	newPtr->files = NULL;
    968 
    969 	while(currentPtr != NULL && stricmp(name, currentPtr->name) > 0)
    970 	{
    971 		previousPtr = currentPtr;
    972 		currentPtr = currentPtr->next;
    973 	}
    974 	if(previousPtr == NULL)
    975 	{
    976 		newPtr->next = *dir;
    977 		*dir = newPtr;
    978 	}
    979 	else
    980 	{
    981 		previousPtr->next = newPtr;
    982 		newPtr->next = currentPtr;
    983 	}
    984 	return newPtr;
    985 }
    986 
    987 
    988 // OpenPK3
    989 // -------
    990 // Opens a PK3 ( or zip ) file and creates a list of filenames
    991 // and zip info structures
    992 // 
    993 boolean OpenPK3(const char *filename)
    994 {
    995   char cFilename[WORK_LEN];
    996   char cName[WORK_LEN];
    997   char cWork[WORK_LEN];
    998   unz_file_info zInfo;
    999   unzFile *zFile = new unzFile(unzOpen(filename));
   1000   g_zFiles.Add(zFile);
   1001   if (zFile != NULL)
   1002   {
   1003     int nStatus = unzGoToFirstFile(*zFile);
   1004     while (nStatus == UNZ_OK)
   1005     {
   1006       cFilename[0] = '\0';
   1007       unzGetCurrentFileInfo(*zFile, &zInfo, cFilename, WORK_LEN, NULL, 0, NULL, 0);
   1008       strlwr(cFilename);
   1009     	__ConvertDOSToUnixName( cWork, cFilename);
   1010       if (strstr(cWork, ".") != NULL)
   1011       {
   1012         PK3FileInfo *pInfo = new PK3FileInfo();
   1013         pInfo->m_pName = __StrDup(cWork);
   1014         memcpy(&pInfo->m_zInfo, (unz_s*)*zFile, sizeof(unz_s));
   1015         pInfo->m_lSize = zInfo.uncompressed_size;
   1016         pInfo->m_zFile = *zFile;
   1017         g_PK3Files.Add(pInfo);
   1018       }
   1019       char *p = strstr(cFilename, TEXTURE_PATH);
   1020       if (p != NULL)
   1021       {
   1022         // FIXME: path differences per os ?
   1023         // catch solo directory entry
   1024         if (strlen(p) > strlen(TEXTURE_PATH) + 1)
   1025         {
   1026           // skip textures + path seperator
   1027           p += strlen(TEXTURE_PATH) + 1;
   1028           int nEnd = strcspn(p, PATH_SEPERATORS);
   1029           strncpy(cName, p, nEnd);
   1030           cName[nEnd] = '\0';
   1031 
   1032           boolean bFound = false;
   1033           StrList *pl = g_PK3TexturePaths.Next();
   1034           while (pl != NULL)
   1035           {
   1036             if (strcmpi(pl->Ref(), cName) == 0)
   1037             {
   1038               // already have this, continue
   1039               bFound = true;
   1040               break;
   1041             }
   1042             pl = pl->Next();
   1043           }
   1044           if (!bFound)
   1045           {
   1046             g_PK3TexturePaths.Add(new Str(cName));
   1047           }
   1048         }
   1049       }
   1050       nStatus = unzGoToNextFile(*zFile);
   1051     }
   1052   }
   1053   return (zFile != NULL);
   1054 }
   1055 
   1056 void closePK3(unzFile zf)
   1057 {
   1058   unzClose(zf);
   1059 }
   1060 
   1061 void OpenPakFile(const char *filename)
   1062 {
   1063 	int			i;
   1064 	char		*str1, *str2;
   1065 	DIRECTORY	*dummy;
   1066 
   1067 	if(!pakopen)
   1068 		paktextures = NULL;
   1069 
   1070 	HavePakColormap = false;
   1071 
   1072   Str strTest(filename);
   1073   strTest.MakeLower();
   1074   if (strTest.Find("pk3") >= 0 || strTest.Find("zip") >= 0)
   1075   {
   1076     pakopen = g_bPK3 = OpenPK3(filename);
   1077     return;
   1078   }
   1079 
   1080 
   1081 	if((pakfile[m_nPAKIndex] = OpenFileReadMagic(filename, &f_type)) == NULL)
   1082 	{
   1083     //FIXME: error routine
   1084 		//Sys_Printf("ERROR: Could not open %s", filename);
   1085 		return;
   1086 	}
   1087 	if(f_type != FTYPE_PACK)
   1088 	{
   1089 		//Sys_Printf("ERROR: %s is not a valid pack file", filename);
   1090 		if(f_type != FTYPE_ERROR)
   1091 			fclose(pakfile[m_nPAKIndex]);
   1092 		return;
   1093 	}
   1094 	pakdirptr = ReadPACKDirectory(pakfile[m_nPAKIndex], 0, &dirsize);
   1095 	if (pakdirptr == NULL)
   1096 	{
   1097 		//Sys_Printf("ERROR: Could not read pack directory", filename);
   1098 		fclose(pakfile[m_nPAKIndex]);
   1099 		return;
   1100 	}
   1101 	if (dirsize == 0)
   1102 	{
   1103 		fclose(pakfile[m_nPAKIndex]);
   1104 		return;
   1105 	}
   1106 	for (i = 0; i < dirsize; i++)
   1107 	{
   1108 		if(!strnicmp("textures/", pakdirptr[i].name, 9))
   1109 		{
   1110 			dummy = paktextures;
   1111 			str1 = pakdirptr[i].name+9;
   1112 			while(strchr(str1, '/'))
   1113 			{
   1114 				str2 = strchr(str1, '/');
   1115 				*str2++ = '\0';
   1116 					dummy = AddPakDir(dummy==paktextures?&paktextures:&dummy, str1);
   1117 				str1 = str2;
   1118 			}
   1119 
   1120 			AddToFileListAlphabetized(&(dummy->files), str1, pakdirptr[i].offset, pakdirptr[i].size, true);
   1121 		}
   1122 		else if(!strnicmp("pics/colormap.pcx", pakdirptr[i].name, 17))
   1123 		{
   1124 			HavePakColormap = true;
   1125 			PakColormapOffset = pakdirptr[i].offset;
   1126 			PakColormapSize = pakdirptr[i].size;
   1127 		}
   1128 	}
   1129 	pakopen = true;
   1130 
   1131 }
   1132 
   1133 void ClearPaKDir(DIRECTORY **dir)
   1134 {
   1135 	DIRECTORY	*d1 = *dir, *d2;
   1136 
   1137 	while(d1)
   1138 	{
   1139 		ClearFileList(&(d1->files));
   1140 		d2 = d1;
   1141 		d1 = d1->next;
   1142 		free(d2);
   1143 	}
   1144 }
   1145 
   1146 void CleanUpPakDirs()
   1147 {
   1148   ClearPaKDir(&paktextures);
   1149   paktextures = NULL;
   1150   dirhead = NULL;
   1151   g_PK3TexturePaths.RemoveAll();
   1152   g_PK3Files.RemoveAll();
   1153 }
   1154 
   1155 void ClosePakFile(void)
   1156 {
   1157 	if(pakopen)
   1158   {
   1159     if (g_bPK3)
   1160     {
   1161       ZFileList *p = g_zFiles.Next();
   1162       while (p != NULL)
   1163       {
   1164         unzFile uz = p->Ref();
   1165         closePK3(uz);
   1166         p = p->Next();
   1167       }
   1168     }
   1169     else
   1170     {
   1171       fclose(pakfile[m_nPAKIndex]);
   1172     }
   1173   }
   1174 	pakopen = false;
   1175   CleanUpPakDirs();
   1176 }
   1177 
   1178 
   1179 void WINAPI InitPakFile(const char * pBasePath, const char *pName)
   1180 {
   1181   m_nPAKIndex = 0;
   1182   pakopen = false;
   1183 	paktextures = NULL;
   1184   strcpy(g_strBasePath, pBasePath);
   1185   if (pName == NULL)
   1186   {
   1187     char cWork[WORK_LEN];
   1188 	  Str strPath(pBasePath);
   1189     AddSlash(strPath);
   1190   	strPath += "*.pk3";
   1191   	bool bGo = true;
   1192 	  struct _finddata_t fileinfo;
   1193   	int handle = _findfirst (strPath, &fileinfo);
   1194 	  if (handle != -1)
   1195   	{
   1196 	  	do
   1197 		  {
   1198         sprintf(cWork, "%s\\%s", pBasePath, fileinfo.name);
   1199         OpenPakFile(cWork);
   1200 		  } while (_findnext( handle, &fileinfo ) != -1);
   1201 	    _findclose (handle);
   1202     }
   1203 	}
   1204   else
   1205   {
   1206 	  OpenPakFile(pName);
   1207   }
   1208 }
   1209