]> git.xonotic.org Git - xonotic/netradiant.git/blobdiff - plugins/vfspak/vfs.cpp
uncrustify! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / plugins / vfspak / vfs.cpp
index 09484c50911a009623c7d68ebe3e878e67957c55..08e0bc13f0d1356481fd271079b2195b8aa8a9f3 100644 (file)
-/*\r
-Copyright (c) 2001, Loki software, inc.\r
-All rights reserved.\r
-\r
-Redistribution and use in source and binary forms, with or without modification, \r
-are permitted provided that the following conditions are met:\r
-\r
-Redistributions of source code must retain the above copyright notice, this list \r
-of conditions and the following disclaimer.\r
-\r
-Redistributions in binary form must reproduce the above copyright notice, this\r
-list of conditions and the following disclaimer in the documentation and/or\r
-other materials provided with the distribution.\r
-\r
-Neither the name of Loki software nor the names of its contributors may be used \r
-to endorse or promote products derived from this software without specific prior \r
-written permission. \r
-\r
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' \r
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE \r
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \r
-DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY \r
-DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES \r
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \r
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON \r
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT \r
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS \r
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \r
-*/\r
-\r
-//\r
-// Rules:\r
-//\r
-// - Directories should be searched in the following order: ~/.q3a/baseq3,\r
-//   install dir (/usr/local/games/quake3/baseq3) and cd_path (/mnt/cdrom/baseq3).\r
-//\r
-// - Pak files are searched first inside the directories.\r
-// - Case insensitive.\r
-// - Unix-style slashes (/)\r
-//\r
-// Leonardo Zide (leo@lokigames.com)\r
-//\r
-\r
-#include <glib.h>\r
-#include <stdio.h>\r
-#if defined __linux__ || defined (__APPLE__)\r
-       #include <dirent.h>\r
-       #include <unistd.h>\r
-       #define WINAPI\r
-#else\r
-       #include <wtypes.h>\r
-       #include <io.h>\r
-       #define R_OK 04\r
-       #define S_ISDIR(mode) (mode & _S_IFDIR)\r
-#endif\r
-\r
-#include "str.h"\r
-#include <stdlib.h>\r
-#include <sys/stat.h>\r
-#include "vfs.h"\r
-#include "vfspak.h"\r
-\r
-typedef struct\r
-{\r
-  char magic[4];         // Name of the new WAD format ("PACK")\r
-  gint32 diroffset;      // Position of WAD directory from start of file\r
-  gint32 dirsize;        // Number of entries * 0x40 (64 char)\r
-} pakheader_t;\r
-\r
-typedef struct\r
-{\r
-  char filename[0x38];   // Name of the file, Unix style, with extension, 50 chars, padded with '\0'.\r
-  gint32 offset;         // Position of the entry in PACK file\r
-  gint32 size;           // Size of the entry in PACK file\r
-} pakentry_t;\r
-\r
-typedef struct\r
-{\r
-  char*   name;\r
-  pakentry_t entry;\r
-  FILE *pak;\r
-} VFS_PAKFILE;\r
-\r
-// =============================================================================\r
-// Global variables\r
-\r
-static GSList* g_unzFiles;\r
-static GSList* g_pakFiles;\r
-static char    g_strDirs[VFS_MAXDIRS][PATH_MAX];\r
-static int     g_numDirs;\r
-static bool    g_bUsePak = true;\r
-\r
-// =============================================================================\r
-// Static functions\r
-\r
-static void vfsAddSlash (char *str)\r
-{\r
-  int n = strlen (str);\r
-  if (n > 0)\r
-  {\r
-    if (str[n-1] != '\\' && str[n-1] != '/')\r
-      strcat (str, "/");\r
-  }\r
-}\r
-\r
-static void vfsFixDOSName (char *src)\r
-{\r
-  if (src == NULL)\r
-    return;\r
-\r
-  while (*src)\r
-  {\r
-    if (*src == '\\')\r
-      *src = '/';\r
-    src++;\r
-  }\r
-}\r
-\r
-static void vfsInitPakFile (const char *filename)\r
-{\r
-  pakheader_t header;\r
-  FILE *f;\r
-  long i;\r
-\r
-  f = fopen (filename, "rb");\r
-  if (f == NULL)\r
-    return;\r
-\r
-  // read header\r
-  fread (header.magic, 1, 4, f);\r
-  fread (&header.diroffset, 1, 4, f);\r
-  fread (&header.dirsize, 1, 4, f);\r
-\r
-  // fix endianess\r
-  header.diroffset = GINT32_FROM_LE (header.diroffset);\r
-  header.dirsize = GINT32_FROM_LE (header.dirsize);\r
-\r
-  // check that the magic header\r
-  if (strncmp (header.magic, "PACK", 4))\r
-  {\r
-    fclose (f);\r
-    return;\r
-  }\r
-\r
-  g_FuncTable.m_pfnSysPrintf("  pak file: %s\n", filename);\r
-\r
-  g_unzFiles = g_slist_append (g_unzFiles, f);\r
-  fseek (f, header.diroffset, SEEK_SET);\r
-\r
-  for (i = 0; i < (long)(header.dirsize/sizeof (pakentry_t)); i++)\r
-  {\r
-    VFS_PAKFILE* file;\r
-\r
-    file = (VFS_PAKFILE*)g_malloc (sizeof (VFS_PAKFILE));\r
-    g_pakFiles = g_slist_append (g_pakFiles, file);\r
-\r
-    fread (file->entry.filename, 1, sizeof (file->entry.filename), f);\r
-    fread (&file->entry.offset, 1, sizeof (file->entry.offset), f);\r
-    fread (&file->entry.size, 1, sizeof (file->entry.size), f);\r
-    file->pak = f;\r
-\r
-    // fix endianess\r
-    file->entry.offset = GINT32_FROM_LE (file->entry.offset);\r
-    file->entry.size = GINT32_FROM_LE (file->entry.size);\r
-\r
-    // fix filename\r
-    vfsFixDOSName (file->entry.filename);\r
-    g_strdown (file->entry.filename);\r
-    //g_FuncTable.m_pfnSysPrintf("vfs file from pak: %s\n", file->entry.filename);\r
-  }\r
-}\r
-\r
-static GSList* vfsGetListInternal (const char *dir, const char *ext, bool directories)\r
-{\r
-  GSList *lst, *lst_aux, *files = NULL;\r
-  char dirname[NAME_MAX], extension[NAME_MAX], filename[NAME_MAX];\r
-  int dirlen;\r
-  char *ptr;\r
-  //struct dirent *dirlist;\r
-  char *dirlist;\r
-  struct stat st;\r
-  GDir *diskdir;\r
-  int i;\r
-\r
-  dirname[0] = '\0';\r
-  if (dir != NULL)\r
-  {\r
-    strcat (dirname, dir);\r
-       g_strdown (dirname);\r
-       vfsFixDOSName (dirname);\r
-       vfsAddSlash (dirname);\r
-       Sys_Printf("vfs dirname_1: %s\n", dirname);\r
-  }\r
-  //else\r
-  //  dirname[0] = '\0';\r
-  dirlen = strlen (dirname);\r
-\r
-  if (ext != NULL)\r
-    strcpy (extension, ext);\r
-  else\r
-    extension[0] = '\0';\r
-  g_strdown (extension);\r
-\r
-  for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))\r
-  {\r
-    VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;\r
-    gboolean found = FALSE;\r
-    ptr = file->entry.filename;\r
-\r
-    // check that the file name begins with dirname\r
-    for (i = 0; (*ptr && i < dirlen); i++, ptr++)\r
-      if (*ptr != dirname[i])\r
-       break;\r
-\r
-    if (i != dirlen)\r
-      continue;\r
-\r
-    if (directories)\r
-    {\r
-      char *sep = strchr (ptr, '/');\r
-      if (sep == NULL)\r
-       continue;\r
-\r
-      i = sep-ptr;\r
-\r
-      // check for duplicates\r
-      for (lst_aux = files; lst_aux; lst_aux = g_slist_next (lst_aux))\r
-       if (strncmp ((char*)lst_aux->data, ptr, i) == 0)\r
-        {\r
-         found = TRUE;\r
-         break;\r
-       }\r
-\r
-      if (!found)\r
-      {\r
-       char *name = g_strndup (ptr, i+1);\r
-       name[i] = '\0';\r
-       files = g_slist_append (files, name);\r
-      }\r
-    }\r
-    else\r
-    {\r
-      // check extension\r
-      if ((ext != NULL) && (strstr (ptr, extension) == NULL))\r
-       continue;\r
-\r
-      // check for duplicates\r
-      for (lst_aux = files; lst_aux; lst_aux = g_slist_next (lst_aux))\r
-       if (strcmp ((char*)lst_aux->data, ptr) == 0)\r
-        {\r
-         found = TRUE;\r
-         break;\r
-       }\r
-\r
-      if (!found)\r
-       files = g_slist_append (files, g_strdup (ptr));\r
-    }\r
-  }\r
-  \r
-  for (i = 0; i < g_numDirs; i++)\r
-  {\r
-    strcpy (dirname, g_strDirs[i]);\r
-    strcat (dirname, dir);\r
-    g_strdown (dirname);\r
-       vfsFixDOSName (dirname);\r
-       vfsAddSlash (dirname);\r
-\r
-    diskdir = g_dir_open (dirname, 0, NULL);\r
-\r
-       if (diskdir != NULL)\r
-    {\r
-      while (1)\r
-      {\r
-        const char* name = g_dir_read_name(diskdir);\r
-        if(name == NULL)\r
-          break;\r
-\r
-        if (directories && (name[0] == '.'))\r
-          continue;\r
-\r
-        sprintf (filename, "%s%s", dirname, name);\r
-        stat (filename, &st);\r
-               Sys_Printf("vfs FileName: %s\n", filename);\r
-\r
-        if ((S_ISDIR (st.st_mode) != 0) != directories)\r
-          continue;\r
-\r
-        gboolean found = FALSE;\r
-\r
-        dirlist = g_strdup(name);\r
-\r
-        g_strdown (dirlist);\r
-\r
-        char *ptr_ext = strrchr (dirlist, '.');\r
-        if(ext == NULL\r
-          || (ext != NULL && ptr_ext != NULL && ptr_ext[0] != '\0' && strcmp (ptr_ext+1, extension) == 0))\r
-        {\r
-\r
-          // check for duplicates\r
-          for (lst_aux = files; lst_aux; lst_aux = g_slist_next (lst_aux))\r
-            if (strcmp ((char*)lst_aux->data, dirlist) == 0)\r
-            {\r
-              found = TRUE;\r
-              break;\r
-            }\r
-\r
-          if (!found)\r
-            files = g_slist_append (files, g_strdup (dirlist));\r
-        }\r
-\r
-        g_free(dirlist);\r
-      }\r
-      g_dir_close (diskdir);\r
-    }\r
-  }\r
-\r
-  return files;\r
-}\r
-\r
-/*!\r
-This behaves identically to -stricmp(a,b), except that ASCII chars\r
-[\]^`_ come AFTER alphabet chars instead of before. This is because\r
-it effectively converts all alphabet chars to uppercase before comparison,\r
-while stricmp converts them to lowercase.\r
-*/\r
-//!\todo Analyse the code in rtcw/q3 to see how it behaves.\r
-static int vfsPakSort (const void *a, const void *b)\r
-{\r
-       char    *s1, *s2;\r
-       int             c1, c2;\r
-\r
-       s1 = (char*)a;\r
-       s2 = (char*)b;\r
-\r
-       do {\r
-               c1 = *s1++;\r
-               c2 = *s2++;\r
-\r
-               if (c1 >= 'a' && c1 <= 'z')\r
-               {\r
-                       c1 -= ('a' - 'A');\r
-               }\r
-               if (c2 >= 'a' && c2 <= 'z')\r
-               {\r
-                       c2 -= ('a' - 'A');\r
-               }\r
-\r
-               if ( c1 == '\\' || c1 == ':' )\r
-               {\r
-                       c1 = '/';\r
-               }\r
-               if ( c2 == '\\' || c2 == ':' )\r
-               {\r
-                       c2 = '/';\r
-               }\r
-\r
-               // Arnout: note - sort pakfiles in reverse order. This ensures that\r
-               // later pakfiles override earlier ones. This because the vfs module\r
-               // returns a filehandle to the first file it can find (while it should\r
-               // return the filehandle to the file in the most overriding pakfile, the\r
-               // last one in the list that is).\r
-               if (c1 < c2)\r
-               {\r
-                       //return -1;            // strings not equal\r
-                       return 1;               // strings not equal\r
-               }\r
-               if (c1 > c2)\r
-               {\r
-                       //return 1;\r
-                       return -1;\r
-               }\r
-       } while (c1);\r
-\r
-       return 0;               // strings are equal\r
-}\r
-\r
-// =============================================================================\r
-// Global functions\r
-\r
-void vfsInitDirectory (const char *path)\r
-{\r
-  char filename[PATH_MAX];\r
-  //struct dirent *direntry;\r
-  GDir *dir;\r
-  //GSList *dirlistptr;\r
-  GSList *dirlist = NULL;\r
-\r
-  if (g_numDirs == (VFS_MAXDIRS-1))\r
-    return;\r
-\r
-  strcpy (g_strDirs[g_numDirs], path);\r
-  vfsFixDOSName (g_strDirs[g_numDirs]);\r
-  vfsAddSlash (g_strDirs[g_numDirs]);\r
-  g_numDirs++;\r
-\r
-  if (g_bUsePak)\r
-  {\r
-    dir = g_dir_open (path, 0, NULL);\r
-    if (dir != NULL)\r
-    {\r
-      g_FuncTable.m_pfnSysPrintf("vfs directory: %s\n", path);\r
-\r
-         for(;;)\r
-      {\r
-        const char* name = g_dir_read_name(dir);\r
-        if(name == NULL)\r
-          break;\r
-\r
-        char *ext = strrchr (name, '.');\r
-        if ((ext == NULL) || (strcasecmp (ext, ".pak") != 0))\r
-          continue;\r
-\r
-        char* direntry = g_strdup(name);\r
-               dirlist = g_slist_append (dirlist, direntry);\r
-      }\r
-\r
-      g_dir_close (dir);\r
-\r
-\r
-      // sort them\r
-      dirlist = g_slist_sort (dirlist, vfsPakSort);\r
-\r
-      // add the entries to the vfs and free the list\r
-      while (dirlist)\r
-      {\r
-        GSList *cur = dirlist;\r
-        char* name = (char*)cur->data;\r
-\r
-        sprintf (filename, "%s/%s", path, name);\r
-        vfsInitPakFile (filename);\r
-\r
-        g_free (name);\r
-        dirlist = g_slist_remove (cur, name);\r
-      }\r
-    } else\r
-          g_FuncTable.m_pfnSysFPrintf(SYS_WRN, "vfs directory not found: %s\n", path);\r
-\r
-  }\r
-}\r
-\r
-\r
-// frees all memory that we allocated\r
-void vfsShutdown ()\r
-{\r
-  while (g_unzFiles)\r
-  {\r
-    fclose ((FILE*)g_unzFiles->data);\r
-    g_unzFiles = g_slist_remove (g_unzFiles, g_unzFiles->data);\r
-  }\r
-\r
-  while (g_pakFiles)\r
-  {\r
-    g_free (g_pakFiles->data);\r
-    g_pakFiles = g_slist_remove (g_pakFiles, g_pakFiles->data);\r
-  }\r
-}\r
-\r
-GSList* vfsGetFileList (const char *dir, const char *ext)\r
-{\r
-  return vfsGetListInternal (dir, ext, false);\r
-}\r
-\r
-GSList* vfsGetDirList (const char *dir)\r
-{\r
-  return vfsGetListInternal (dir, NULL, true);\r
-}\r
-\r
-void vfsClearFileDirList (GSList **lst)\r
-{\r
-  while (*lst)\r
-  {\r
-    g_free ((*lst)->data);\r
-    *lst = g_slist_remove (*lst, (*lst)->data);\r
-  }\r
-}\r
-\r
-// return the number of files that match\r
-int vfsGetFileCount (const char *filename, int flag)\r
-{\r
-  int i, count = 0;\r
-  char fixed[NAME_MAX], tmp[NAME_MAX];\r
-  GSList *lst;\r
-\r
-  strcpy (fixed, filename);\r
-  vfsFixDOSName (fixed);\r
-  g_strdown (fixed);\r
-\r
-  for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))\r
-  {\r
-    VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;\r
-\r
-    if (strcmp (file->entry.filename, fixed) == 0)\r
-      count++;\r
-  }\r
-\r
-  for (i = 0; i < g_numDirs; i++)\r
-  {\r
-    strcpy (tmp, g_strDirs[i]);\r
-    strcat (tmp, fixed);\r
-    if (access (tmp, R_OK) == 0)\r
-      count++;\r
-  }\r
-\r
-  return count;\r
-}\r
-\r
-// NOTE: when loading a file, you have to allocate one extra byte and set it to \0\r
-int vfsLoadFile (const char *filename, void **bufferptr, int index)\r
-{\r
-  int i, count = 0;\r
-  char tmp[NAME_MAX], fixed[NAME_MAX];\r
-  GSList *lst;\r
-\r
-  *bufferptr = NULL;\r
-  strcpy (fixed, filename);\r
-  vfsFixDOSName (fixed);\r
-  g_strdown (fixed);\r
-\r
-  for (i = 0; i < g_numDirs; i++)\r
-  {\r
-    strcpy (tmp, g_strDirs[i]);\r
-    strcat (tmp, filename);\r
-    if (access (tmp, R_OK) == 0)\r
-    {\r
-      if (count == index)\r
-      {\r
-       long len;\r
-       FILE *f;\r
-\r
-       f = fopen (tmp, "rb");\r
-       if (f == NULL)\r
-         return -1;\r
-\r
-       fseek (f, 0, SEEK_END);\r
-       len = ftell (f);\r
-       rewind (f);\r
-\r
-       *bufferptr = malloc (len+1);\r
-       if (*bufferptr == NULL)\r
-         return -1;\r
-\r
-       fread (*bufferptr, 1, len, f);\r
-       fclose (f);\r
-\r
-        // we need to end the buffer with a 0\r
-        ((char*) (*bufferptr))[len] = 0;\r
-\r
-       return len;\r
-      }\r
-\r
-      count++;\r
-    }\r
-  }\r
-\r
-  for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))\r
-  {\r
-    VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;\r
-\r
-    if (strcmp (file->entry.filename, fixed) != 0)\r
-      continue;\r
-\r
-    if (count == index)\r
-    {\r
-      fseek (file->pak, file->entry.offset, SEEK_SET);\r
-\r
-      *bufferptr = malloc (file->entry.size+1);\r
-      // we need to end the buffer with a 0\r
-      ((char*) (*bufferptr))[file->entry.size] = 0;\r
-\r
-      return fread (*bufferptr, 1, file->entry.size, file->pak);\r
-    }\r
-\r
-    count++;\r
-  }\r
-\r
-  return -1;\r
-}\r
-\r
-void vfsFreeFile (void *p)\r
-{\r
-  g_free(p);\r
-}\r
-\r
-// open a full path file\r
-int vfsLoadFullPathFile (const char *filename, void **bufferptr)\r
-{\r
-  FILE *f;\r
-  long len;\r
-\r
-  f = fopen (filename, "rb");\r
-  if (f == NULL)\r
-    return -1;\r
-\r
-  fseek (f, 0, SEEK_END);\r
-  len = ftell (f);\r
-  rewind (f);\r
-\r
-  *bufferptr = g_malloc (len+1);\r
-  if (*bufferptr == NULL)\r
-    return -1;\r
-\r
-  fread (*bufferptr, 1, len, f);\r
-  fclose (f);\r
-\r
-  // we need to end the buffer with a 0\r
-  ((char*) (*bufferptr))[len] = 0;\r
-\r
-  return len;\r
-}\r
-\r
-void vfsCleanFileName(char *in)\r
-{\r
-  strlwr(in);\r
-  vfsFixDOSName(in);\r
-  int n = strlen(in);\r
-  if (in[n-1] == '/')\r
-    in[n-1] = '\0';\r
-}\r
-\r
-const char* vfsBasePromptPath()\r
-{\r
-#ifdef _WIN32\r
-  static char* path = "C:";\r
-#else\r
-  static char* path = "/";\r
-#endif\r
-  return path;\r
-}\r
-\r
-/*!\r
-\param shorten will try to match against the short version\r
-http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=144\r
-recent switch back to short path names in project settings has broken some stuff\r
-with shorten == true, we will convert in to short version before looking for root\r
-FIXME WAAA .. the stuff below is much more simple on linux .. add appropriate #ifdef\r
-*/\r
-char* vfsExtractRelativePath_short(const char *in, bool shorten)\r
-{\r
-  int i;\r
-  char l_in[PATH_MAX];\r
-  char check[PATH_MAX];\r
-  static char out[PATH_MAX];\r
-  out[0] = 0;\r
-\r
-#ifdef DBG_RLTPATH\r
-  Sys_Printf("vfsExtractRelativePath: %s\n", in);\r
-#endif\r
-\r
-#ifdef _WIN32\r
-  if (shorten)\r
-  {\r
-    // make it short\r
-    if (GetShortPathName(in, l_in, PATH_MAX) == 0)\r
-    {\r
-#ifdef DBG_RLTPATH\r
-      Sys_Printf("GetShortPathName failed\n");\r
-#endif\r
-      return NULL;\r
-    }\r
-  }\r
-  else\r
-  {\r
-    strcpy(l_in,in);\r
-  }\r
-  vfsCleanFileName(l_in);\r
-#else\r
-  strcpy(l_in, in);\r
-  vfsCleanFileName(l_in);\r
-#endif // ifdef WIN32\r
-\r
-\r
-#ifdef DBG_RLTPATH\r
-  Sys_Printf("cleaned path: %s\n", l_in);\r
-#endif\r
-\r
-  for (i = 0; i < g_numDirs; i++)\r
-  {\r
-    strcpy(check,g_strDirs[i]);\r
-    vfsCleanFileName(check);\r
-#ifdef DBG_RLTPATH\r
-    Sys_Printf("Matching against %s\n", check);\r
-#endif\r
-\r
-    // try to find a match\r
-    if (strstr(l_in, check))\r
-    {\r
-      strcpy(out,l_in+strlen(check)+1);\r
-      break;\r
-    }\r
-\r
-  }\r
-  if (out[0]!=0)\r
-  {\r
-#ifdef DBG_RLTPATH\r
-    Sys_Printf("vfsExtractRelativePath: success\n");\r
-#endif\r
-    return out;\r
-  }\r
-#ifdef DBG_RLTPATH\r
-  Sys_Printf("vfsExtractRelativePath: failed\n");\r
-#endif\r
-  return NULL;\r
-}\r
-\r
-// HYDRA: this now searches VFS/PAK files in addition to the filesystem\r
-// if FLAG is unspecified then ONLY dirs are searched.\r
-// PAK's are searched before DIRs to mimic engine behaviour\r
-// index is ignored when searching PAK files.\r
-// see ifilesystem.h\r
-char* vfsGetFullPath(const char *in, int index, int flag)\r
-{\r
-  int count = 0;\r
-  static char out[PATH_MAX];\r
-  char tmp[NAME_MAX];\r
-  int i;\r
-\r
-  if (flag & VFS_SEARCH_PAK)\r
-  {\r
-    char fixed[NAME_MAX];\r
-    GSList *lst;\r
-\r
-    strcpy (fixed, in);\r
-    vfsFixDOSName (fixed);\r
-    g_strdown (fixed);\r
-\r
-    for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))\r
-    {\r
-      VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;\r
-\r
-      char *ptr,*lastptr;\r
-      lastptr = file->name;\r
-\r
-      while (ptr = strchr(lastptr,'/'))\r
-        lastptr = ptr+1;\r
-\r
-      if (strcmp (lastptr, fixed) == 0)\r
-      {\r
-        strncpy(out,file->name,PATH_MAX);\r
-        return out;\r
-      }\r
-    }\r
-\r
-  }\r
-\r
-  if (!flag || (flag & VFS_SEARCH_DIR))\r
-  {\r
-  for (i = 0; i < g_numDirs; i++)\r
-  {\r
-    strcpy (tmp, g_strDirs[i]);\r
-    strcat (tmp, in);\r
-    if (access (tmp, R_OK) == 0)\r
-    {\r
-      if (count == index)\r
-      {\r
-        strcpy (out, tmp);\r
-        return out;\r
-      }\r
-      count++;\r
-    }\r
-  }\r
-  }\r
-  return NULL;\r
-}\r
-\r
-// FIXME TTimo: this and the above should be merged at some point\r
-char* vfsExtractRelativePath(const char *in)\r
-{\r
-  static char out[PATH_MAX];\r
-  unsigned int i, count;\r
-  char *chunk, *backup = NULL; // those point to out stuff\r
-  char *ret = vfsExtractRelativePath_short(in, false);\r
-  if (!ret)\r
-  {\r
-#ifdef DBG_RLTPATH\r
-    Sys_Printf("trying with a short version\n");\r
-#endif\r
-    ret = vfsExtractRelativePath_short(in, true);\r
-    if (ret)\r
-    {\r
-      // ok, but we have a relative short version now\r
-      // hack the long relative version out of here\r
-      count = 0;\r
-      for(i=0;i<strlen(ret);i++)\r
-      {\r
-        if (ret[i]=='/')\r
-          count++;\r
-      }\r
-      // this is the clean, not short version\r
-      strcpy(out, in);\r
-      vfsCleanFileName(out);\r
-      for(i=0;i<=count;i++)\r
-      {\r
-        chunk = strrchr(out, '/');\r
-        if (backup)\r
-          backup[0] = '/';\r
-        chunk[0] = '\0';\r
-        backup = chunk;\r
-      }\r
-      return chunk+1;\r
-    }\r
-  }\r
-  return ret;\r
-}\r
+/*
+   Copyright (c) 2001, Loki software, inc.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without modification,
+   are permitted provided that the following conditions are met:
+
+   Redistributions of source code must retain the above copyright notice, this list
+   of conditions and the following disclaimer.
+
+   Redistributions in binary form must reproduce the above copyright notice, this
+   list of conditions and the following disclaimer in the documentation and/or
+   other materials provided with the distribution.
+
+   Neither the name of Loki software nor the names of its contributors may be used
+   to endorse or promote products derived from this software without specific prior
+   written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+   DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//
+// Rules:
+//
+// - Directories should be searched in the following order: ~/.q3a/baseq3,
+//   install dir (/usr/local/games/quake3/baseq3) and cd_path (/mnt/cdrom/baseq3).
+//
+// - Pak files are searched first inside the directories.
+// - Case insensitive.
+// - Unix-style slashes (/)
+//
+// Leonardo Zide (leo@lokigames.com)
+//
+
+#include <glib.h>
+#include <stdio.h>
+#if defined __linux__ || defined ( __APPLE__ )
+       #include <dirent.h>
+       #include <unistd.h>
+       #define WINAPI
+#else
+       #include <wtypes.h>
+       #include <io.h>
+       #define R_OK 04
+       #define S_ISDIR( mode ) ( mode & _S_IFDIR )
+#endif
+
+#include "str.h"
+#include <stdlib.h>
+#include <sys/stat.h>
+#include "vfs.h"
+#include "vfspak.h"
+
+typedef struct
+{
+       char magic[4];       // Name of the new WAD format ("PACK")
+       gint32 diroffset;    // Position of WAD directory from start of file
+       gint32 dirsize;      // Number of entries * 0x40 (64 char)
+} pakheader_t;
+
+typedef struct
+{
+       char filename[0x38]; // Name of the file, Unix style, with extension, 50 chars, padded with '\0'.
+       gint32 offset;       // Position of the entry in PACK file
+       gint32 size;         // Size of the entry in PACK file
+} pakentry_t;
+
+typedef struct
+{
+       char*   name;
+       pakentry_t entry;
+       FILE *pak;
+} VFS_PAKFILE;
+
+// =============================================================================
+// Global variables
+
+static GSList* g_unzFiles;
+static GSList* g_pakFiles;
+static char g_strDirs[VFS_MAXDIRS][PATH_MAX];
+static int g_numDirs;
+static bool g_bUsePak = true;
+
+// =============================================================================
+// Static functions
+
+static void vfsAddSlash( char *str ){
+       int n = strlen( str );
+       if ( n > 0 ) {
+               if ( str[n - 1] != '\\' && str[n - 1] != '/' ) {
+                       strcat( str, "/" );
+               }
+       }
+}
+
+static void vfsFixDOSName( char *src ){
+       if ( src == NULL ) {
+               return;
+       }
+
+       while ( *src )
+       {
+               if ( *src == '\\' ) {
+                       *src = '/';
+               }
+               src++;
+       }
+}
+
+static void vfsInitPakFile( const char *filename ){
+       pakheader_t header;
+       FILE *f;
+       long i;
+
+       f = fopen( filename, "rb" );
+       if ( f == NULL ) {
+               return;
+       }
+
+       // read header
+       fread( header.magic, 1, 4, f );
+       fread( &header.diroffset, 1, 4, f );
+       fread( &header.dirsize, 1, 4, f );
+
+       // fix endianess
+       header.diroffset = GINT32_FROM_LE( header.diroffset );
+       header.dirsize = GINT32_FROM_LE( header.dirsize );
+
+       // check that the magic header
+       if ( strncmp( header.magic, "PACK", 4 ) ) {
+               fclose( f );
+               return;
+       }
+
+       g_FuncTable.m_pfnSysPrintf( "  pak file: %s\n", filename );
+
+       g_unzFiles = g_slist_append( g_unzFiles, f );
+       fseek( f, header.diroffset, SEEK_SET );
+
+       for ( i = 0; i < (long)( header.dirsize / sizeof( pakentry_t ) ); i++ )
+       {
+               VFS_PAKFILE* file;
+
+               file = (VFS_PAKFILE*)g_malloc( sizeof( VFS_PAKFILE ) );
+               g_pakFiles = g_slist_append( g_pakFiles, file );
+
+               fread( file->entry.filename, 1, sizeof( file->entry.filename ), f );
+               fread( &file->entry.offset, 1, sizeof( file->entry.offset ), f );
+               fread( &file->entry.size, 1, sizeof( file->entry.size ), f );
+               file->pak = f;
+
+               // fix endianess
+               file->entry.offset = GINT32_FROM_LE( file->entry.offset );
+               file->entry.size = GINT32_FROM_LE( file->entry.size );
+
+               // fix filename
+               vfsFixDOSName( file->entry.filename );
+               g_strdown( file->entry.filename );
+               //g_FuncTable.m_pfnSysPrintf("vfs file from pak: %s\n", file->entry.filename);
+       }
+}
+
+static GSList* vfsGetListInternal( const char *dir, const char *ext, bool directories ){
+       GSList *lst, *lst_aux, *files = NULL;
+       char dirname[NAME_MAX], extension[NAME_MAX], filename[NAME_MAX];
+       int dirlen;
+       char *ptr;
+       //struct dirent *dirlist;
+       char *dirlist;
+       struct stat st;
+       GDir *diskdir;
+       int i;
+
+       dirname[0] = '\0';
+       if ( dir != NULL ) {
+               strcat( dirname, dir );
+               g_strdown( dirname );
+               vfsFixDOSName( dirname );
+               vfsAddSlash( dirname );
+               Sys_Printf( "vfs dirname_1: %s\n", dirname );
+       }
+       //else
+       //  dirname[0] = '\0';
+       dirlen = strlen( dirname );
+
+       if ( ext != NULL ) {
+               strcpy( extension, ext );
+       }
+       else{
+               extension[0] = '\0';
+       }
+       g_strdown( extension );
+
+       for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
+       {
+               VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
+               gboolean found = FALSE;
+               ptr = file->entry.filename;
+
+               // check that the file name begins with dirname
+               for ( i = 0; ( *ptr && i < dirlen ); i++, ptr++ )
+                       if ( *ptr != dirname[i] ) {
+                               break;
+                       }
+
+               if ( i != dirlen ) {
+                       continue;
+               }
+
+               if ( directories ) {
+                       char *sep = strchr( ptr, '/' );
+                       if ( sep == NULL ) {
+                               continue;
+                       }
+
+                       i = sep - ptr;
+
+                       // check for duplicates
+                       for ( lst_aux = files; lst_aux; lst_aux = g_slist_next( lst_aux ) )
+                               if ( strncmp( (char*)lst_aux->data, ptr, i ) == 0 ) {
+                                       found = TRUE;
+                                       break;
+                               }
+
+                       if ( !found ) {
+                               char *name = g_strndup( ptr, i + 1 );
+                               name[i] = '\0';
+                               files = g_slist_append( files, name );
+                       }
+               }
+               else
+               {
+                       // check extension
+                       if ( ( ext != NULL ) && ( strstr( ptr, extension ) == NULL ) ) {
+                               continue;
+                       }
+
+                       // check for duplicates
+                       for ( lst_aux = files; lst_aux; lst_aux = g_slist_next( lst_aux ) )
+                               if ( strcmp( (char*)lst_aux->data, ptr ) == 0 ) {
+                                       found = TRUE;
+                                       break;
+                               }
+
+                       if ( !found ) {
+                               files = g_slist_append( files, g_strdup( ptr ) );
+                       }
+               }
+       }
+
+       for ( i = 0; i < g_numDirs; i++ )
+       {
+               strcpy( dirname, g_strDirs[i] );
+               strcat( dirname, dir );
+               g_strdown( dirname );
+               vfsFixDOSName( dirname );
+               vfsAddSlash( dirname );
+
+               diskdir = g_dir_open( dirname, 0, NULL );
+
+               if ( diskdir != NULL ) {
+                       while ( 1 )
+                       {
+                               const char* name = g_dir_read_name( diskdir );
+                               if ( name == NULL ) {
+                                       break;
+                               }
+
+                               if ( directories && ( name[0] == '.' ) ) {
+                                       continue;
+                               }
+
+                               sprintf( filename, "%s%s", dirname, name );
+                               stat( filename, &st );
+                               Sys_Printf( "vfs FileName: %s\n", filename );
+
+                               if ( ( S_ISDIR( st.st_mode ) != 0 ) != directories ) {
+                                       continue;
+                               }
+
+                               gboolean found = FALSE;
+
+                               dirlist = g_strdup( name );
+
+                               g_strdown( dirlist );
+
+                               char *ptr_ext = strrchr( dirlist, '.' );
+                               if ( ext == NULL
+                                        || ( ext != NULL && ptr_ext != NULL && ptr_ext[0] != '\0' && strcmp( ptr_ext + 1, extension ) == 0 ) ) {
+
+                                       // check for duplicates
+                                       for ( lst_aux = files; lst_aux; lst_aux = g_slist_next( lst_aux ) )
+                                               if ( strcmp( (char*)lst_aux->data, dirlist ) == 0 ) {
+                                                       found = TRUE;
+                                                       break;
+                                               }
+
+                                       if ( !found ) {
+                                               files = g_slist_append( files, g_strdup( dirlist ) );
+                                       }
+                               }
+
+                               g_free( dirlist );
+                       }
+                       g_dir_close( diskdir );
+               }
+       }
+
+       return files;
+}
+
+/*!
+   This behaves identically to -stricmp(a,b), except that ASCII chars
+   [\]^`_ come AFTER alphabet chars instead of before. This is because
+   it effectively converts all alphabet chars to uppercase before comparison,
+   while stricmp converts them to lowercase.
+ */
+//!\todo Analyse the code in rtcw/q3 to see how it behaves.
+static int vfsPakSort( const void *a, const void *b ){
+       char    *s1, *s2;
+       int c1, c2;
+
+       s1 = (char*)a;
+       s2 = (char*)b;
+
+       do {
+               c1 = *s1++;
+               c2 = *s2++;
+
+               if ( c1 >= 'a' && c1 <= 'z' ) {
+                       c1 -= ( 'a' - 'A' );
+               }
+               if ( c2 >= 'a' && c2 <= 'z' ) {
+                       c2 -= ( 'a' - 'A' );
+               }
+
+               if ( c1 == '\\' || c1 == ':' ) {
+                       c1 = '/';
+               }
+               if ( c2 == '\\' || c2 == ':' ) {
+                       c2 = '/';
+               }
+
+               // Arnout: note - sort pakfiles in reverse order. This ensures that
+               // later pakfiles override earlier ones. This because the vfs module
+               // returns a filehandle to the first file it can find (while it should
+               // return the filehandle to the file in the most overriding pakfile, the
+               // last one in the list that is).
+               if ( c1 < c2 ) {
+                       //return -1;            // strings not equal
+                       return 1;       // strings not equal
+               }
+               if ( c1 > c2 ) {
+                       //return 1;
+                       return -1;
+               }
+       } while ( c1 );
+
+       return 0;       // strings are equal
+}
+
+// =============================================================================
+// Global functions
+
+void vfsInitDirectory( const char *path ){
+       char filename[PATH_MAX];
+       //struct dirent *direntry;
+       GDir *dir;
+       GSList *dirlist = NULL;
+
+       if ( g_numDirs == ( VFS_MAXDIRS - 1 ) ) {
+               return;
+       }
+
+       strcpy( g_strDirs[g_numDirs], path );
+       vfsFixDOSName( g_strDirs[g_numDirs] );
+       vfsAddSlash( g_strDirs[g_numDirs] );
+       g_numDirs++;
+
+       if ( g_bUsePak ) {
+               dir = g_dir_open( path, 0, NULL );
+               if ( dir != NULL ) {
+                       g_FuncTable.m_pfnSysPrintf( "vfs directory: %s\n", path );
+
+                       for (;; )
+                       {
+                               const char* name = g_dir_read_name( dir );
+                               if ( name == NULL ) {
+                                       break;
+                               }
+
+                               const char *ext = strrchr( name, '.' );
+                               if ( ( ext == NULL ) || ( strcasecmp( ext, ".pak" ) != 0 ) ) {
+                                       continue;
+                               }
+
+                               char* direntry = g_strdup( name );
+                               dirlist = g_slist_append( dirlist, direntry );
+                       }
+
+                       g_dir_close( dir );
+
+
+                       // sort them
+                       dirlist = g_slist_sort( dirlist, vfsPakSort );
+
+                       // add the entries to the vfs and free the list
+                       while ( dirlist )
+                       {
+                               GSList *cur = dirlist;
+                               char* name = (char*)cur->data;
+
+                               sprintf( filename, "%s/%s", path, name );
+                               vfsInitPakFile( filename );
+
+                               g_free( name );
+                               dirlist = g_slist_remove( cur, name );
+                       }
+               }
+               else{
+                       g_FuncTable.m_pfnSysFPrintf( SYS_WRN, "vfs directory not found: %s\n", path );
+               }
+
+       }
+}
+
+
+// frees all memory that we allocated
+void vfsShutdown(){
+       while ( g_unzFiles )
+       {
+               fclose( (FILE*)g_unzFiles->data );
+               g_unzFiles = g_slist_remove( g_unzFiles, g_unzFiles->data );
+       }
+
+       while ( g_pakFiles )
+       {
+               g_free( g_pakFiles->data );
+               g_pakFiles = g_slist_remove( g_pakFiles, g_pakFiles->data );
+       }
+}
+
+GSList* vfsGetFileList( const char *dir, const char *ext ){
+       return vfsGetListInternal( dir, ext, false );
+}
+
+GSList* vfsGetDirList( const char *dir ){
+       return vfsGetListInternal( dir, NULL, true );
+}
+
+void vfsClearFileDirList( GSList **lst ){
+       while ( *lst )
+       {
+               g_free( ( *lst )->data );
+               *lst = g_slist_remove( *lst, ( *lst )->data );
+       }
+}
+
+// return the number of files that match
+int vfsGetFileCount( const char *filename, int flag ){
+       int i, count = 0;
+       char fixed[NAME_MAX], tmp[NAME_MAX];
+       GSList *lst;
+
+       strcpy( fixed, filename );
+       vfsFixDOSName( fixed );
+       g_strdown( fixed );
+
+       for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
+       {
+               VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
+
+               if ( strcmp( file->entry.filename, fixed ) == 0 ) {
+                       count++;
+               }
+       }
+
+       for ( i = 0; i < g_numDirs; i++ )
+       {
+               strcpy( tmp, g_strDirs[i] );
+               strcat( tmp, fixed );
+               if ( access( tmp, R_OK ) == 0 ) {
+                       count++;
+               }
+       }
+
+       return count;
+}
+
+// NOTE: when loading a file, you have to allocate one extra byte and set it to \0
+int vfsLoadFile( const char *filename, void **bufferptr, int index ){
+       int i, count = 0;
+       char tmp[NAME_MAX], fixed[NAME_MAX];
+       GSList *lst;
+
+       *bufferptr = NULL;
+       strcpy( fixed, filename );
+       vfsFixDOSName( fixed );
+       g_strdown( fixed );
+
+       for ( i = 0; i < g_numDirs; i++ )
+       {
+               strcpy( tmp, g_strDirs[i] );
+               strcat( tmp, filename );
+               if ( access( tmp, R_OK ) == 0 ) {
+                       if ( count == index ) {
+                               long len;
+                               FILE *f;
+
+                               f = fopen( tmp, "rb" );
+                               if ( f == NULL ) {
+                                       return -1;
+                               }
+
+                               fseek( f, 0, SEEK_END );
+                               len = ftell( f );
+                               rewind( f );
+
+                               *bufferptr = malloc( len + 1 );
+                               if ( *bufferptr == NULL ) {
+                                       return -1;
+                               }
+
+                               fread( *bufferptr, 1, len, f );
+                               fclose( f );
+
+                               // we need to end the buffer with a 0
+                               ( (char*) ( *bufferptr ) )[len] = 0;
+
+                               return len;
+                       }
+
+                       count++;
+               }
+       }
+
+       for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
+       {
+               VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
+
+               if ( strcmp( file->entry.filename, fixed ) != 0 ) {
+                       continue;
+               }
+
+               if ( count == index ) {
+                       fseek( file->pak, file->entry.offset, SEEK_SET );
+
+                       *bufferptr = malloc( file->entry.size + 1 );
+                       // we need to end the buffer with a 0
+                       ( (char*) ( *bufferptr ) )[file->entry.size] = 0;
+
+                       return fread( *bufferptr, 1, file->entry.size, file->pak );
+               }
+
+               count++;
+       }
+
+       return -1;
+}
+
+void vfsFreeFile( void *p ){
+       g_free( p );
+}
+
+// open a full path file
+int vfsLoadFullPathFile( const char *filename, void **bufferptr ){
+       FILE *f;
+       long len;
+
+       f = fopen( filename, "rb" );
+       if ( f == NULL ) {
+               return -1;
+       }
+
+       fseek( f, 0, SEEK_END );
+       len = ftell( f );
+       rewind( f );
+
+       *bufferptr = g_malloc( len + 1 );
+       if ( *bufferptr == NULL ) {
+               return -1;
+       }
+
+       fread( *bufferptr, 1, len, f );
+       fclose( f );
+
+       // we need to end the buffer with a 0
+       ( (char*) ( *bufferptr ) )[len] = 0;
+
+       return len;
+}
+
+void vfsCleanFileName( char *in ){
+       strlwr( in );
+       vfsFixDOSName( in );
+       int n = strlen( in );
+       if ( in[n - 1] == '/' ) {
+               in[n - 1] = '\0';
+       }
+}
+
+const char* vfsBasePromptPath(){
+#ifdef _WIN32
+       static const char* path = "C:";
+#else
+       static const char* path = "/";
+#endif
+       return path;
+}
+
+/*!
+   \param shorten will try to match against the short version
+   recent switch back to short path names in project settings has broken some stuff
+   with shorten == true, we will convert in to short version before looking for root
+   FIXME WAAA .. the stuff below is much more simple on linux .. add appropriate #ifdef
+ */
+char* vfsExtractRelativePath_short( const char *in, bool shorten ){
+       int i;
+       char l_in[PATH_MAX];
+       char check[PATH_MAX];
+       static char out[PATH_MAX];
+       out[0] = 0;
+
+#ifdef DBG_RLTPATH
+       Sys_Printf( "vfsExtractRelativePath: %s\n", in );
+#endif
+
+#ifdef _WIN32
+       if ( shorten ) {
+               // make it short
+               if ( GetShortPathName( in, l_in, PATH_MAX ) == 0 ) {
+#ifdef DBG_RLTPATH
+                       Sys_Printf( "GetShortPathName failed\n" );
+#endif
+                       return NULL;
+               }
+       }
+       else
+       {
+               strcpy( l_in,in );
+       }
+       vfsCleanFileName( l_in );
+#else
+       strcpy( l_in, in );
+       vfsCleanFileName( l_in );
+#endif // ifdef WIN32
+
+
+#ifdef DBG_RLTPATH
+       Sys_Printf( "cleaned path: %s\n", l_in );
+#endif
+
+       for ( i = 0; i < g_numDirs; i++ )
+       {
+               strcpy( check,g_strDirs[i] );
+               vfsCleanFileName( check );
+#ifdef DBG_RLTPATH
+               Sys_Printf( "Matching against %s\n", check );
+#endif
+
+               // try to find a match
+               if ( strstr( l_in, check ) ) {
+                       strcpy( out,l_in + strlen( check ) + 1 );
+                       break;
+               }
+
+       }
+       if ( out[0] != 0 ) {
+#ifdef DBG_RLTPATH
+               Sys_Printf( "vfsExtractRelativePath: success\n" );
+#endif
+               return out;
+       }
+#ifdef DBG_RLTPATH
+       Sys_Printf( "vfsExtractRelativePath: failed\n" );
+#endif
+       return NULL;
+}
+
+// HYDRA: this now searches VFS/PAK files in addition to the filesystem
+// if FLAG is unspecified then ONLY dirs are searched.
+// PAK's are searched before DIRs to mimic engine behaviour
+// index is ignored when searching PAK files.
+// see ifilesystem.h
+char* vfsGetFullPath( const char *in, int index, int flag ){
+       int count = 0;
+       static char out[PATH_MAX];
+       char tmp[NAME_MAX];
+       int i;
+
+       if ( flag & VFS_SEARCH_PAK ) {
+               char fixed[NAME_MAX];
+               GSList *lst;
+
+               strcpy( fixed, in );
+               vfsFixDOSName( fixed );
+               g_strdown( fixed );
+
+               for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
+               {
+                       VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
+
+                       char *ptr,*lastptr;
+                       lastptr = file->name;
+
+                       while ( ( ptr = strchr( lastptr,'/' ) ) != NULL )
+                               lastptr = ptr + 1;
+
+                       if ( strcmp( lastptr, fixed ) == 0 ) {
+                               strncpy( out,file->name,PATH_MAX );
+                               return out;
+                       }
+               }
+
+       }
+
+       if ( !flag || ( flag & VFS_SEARCH_DIR ) ) {
+               for ( i = 0; i < g_numDirs; i++ )
+               {
+                       strcpy( tmp, g_strDirs[i] );
+                       strcat( tmp, in );
+                       if ( access( tmp, R_OK ) == 0 ) {
+                               if ( count == index ) {
+                                       strcpy( out, tmp );
+                                       return out;
+                               }
+                               count++;
+                       }
+               }
+       }
+       return NULL;
+}
+
+// FIXME TTimo: this and the above should be merged at some point
+char* vfsExtractRelativePath( const char *in ){
+       static char out[PATH_MAX];
+       unsigned int i, count;
+       char *chunk, *backup = NULL; // those point to out stuff
+       char *ret = vfsExtractRelativePath_short( in, false );
+       if ( !ret ) {
+#ifdef DBG_RLTPATH
+               Sys_Printf( "trying with a short version\n" );
+#endif
+               ret = vfsExtractRelativePath_short( in, true );
+               if ( ret ) {
+                       // ok, but we have a relative short version now
+                       // hack the long relative version out of here
+                       count = 0;
+                       for ( i = 0; i < strlen( ret ); i++ )
+                       {
+                               if ( ret[i] == '/' ) {
+                                       count++;
+                               }
+                       }
+                       // this is the clean, not short version
+                       strcpy( out, in );
+                       vfsCleanFileName( out );
+                       for ( i = 0; i <= count; i++ )
+                       {
+                               chunk = strrchr( out, '/' );
+                               if ( backup ) {
+                                       backup[0] = '/';
+                               }
+                               chunk[0] = '\0';
+                               backup = chunk;
+                       }
+                       return chunk + 1;
+               }
+       }
+       return ret;
+}