]> git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/vfspak/vfs.cpp
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / plugins / vfspak / vfs.cpp
1 /*\r
2 Copyright (c) 2001, Loki software, inc.\r
3 All rights reserved.\r
4 \r
5 Redistribution and use in source and binary forms, with or without modification, \r
6 are permitted provided that the following conditions are met:\r
7 \r
8 Redistributions of source code must retain the above copyright notice, this list \r
9 of conditions and the following disclaimer.\r
10 \r
11 Redistributions in binary form must reproduce the above copyright notice, this\r
12 list of conditions and the following disclaimer in the documentation and/or\r
13 other materials provided with the distribution.\r
14 \r
15 Neither the name of Loki software nor the names of its contributors may be used \r
16 to endorse or promote products derived from this software without specific prior \r
17 written permission. \r
18 \r
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' \r
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE \r
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \r
22 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY \r
23 DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES \r
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \r
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON \r
26 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT \r
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS \r
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \r
29 */\r
30 \r
31 //\r
32 // Rules:\r
33 //\r
34 // - Directories should be searched in the following order: ~/.q3a/baseq3,\r
35 //   install dir (/usr/local/games/quake3/baseq3) and cd_path (/mnt/cdrom/baseq3).\r
36 //\r
37 // - Pak files are searched first inside the directories.\r
38 // - Case insensitive.\r
39 // - Unix-style slashes (/)\r
40 //\r
41 // Leonardo Zide (leo@lokigames.com)\r
42 //\r
43 \r
44 #include <glib.h>\r
45 #include <stdio.h>\r
46 #if defined __linux__ || defined (__APPLE__)\r
47         #include <dirent.h>\r
48         #include <unistd.h>\r
49         #define WINAPI\r
50 #else\r
51         #include <wtypes.h>\r
52         #include <io.h>\r
53         #define R_OK 04\r
54         #define S_ISDIR(mode) (mode & _S_IFDIR)\r
55 #endif\r
56 \r
57 #include "str.h"\r
58 #include <stdlib.h>\r
59 #include <sys/stat.h>\r
60 #include "vfs.h"\r
61 #include "vfspak.h"\r
62 \r
63 typedef struct\r
64 {\r
65   char magic[4];         // Name of the new WAD format ("PACK")\r
66   gint32 diroffset;      // Position of WAD directory from start of file\r
67   gint32 dirsize;        // Number of entries * 0x40 (64 char)\r
68 } pakheader_t;\r
69 \r
70 typedef struct\r
71 {\r
72   char filename[0x38];   // Name of the file, Unix style, with extension, 50 chars, padded with '\0'.\r
73   gint32 offset;         // Position of the entry in PACK file\r
74   gint32 size;           // Size of the entry in PACK file\r
75 } pakentry_t;\r
76 \r
77 typedef struct\r
78 {\r
79   char*   name;\r
80   pakentry_t entry;\r
81   FILE *pak;\r
82 } VFS_PAKFILE;\r
83 \r
84 // =============================================================================\r
85 // Global variables\r
86 \r
87 static GSList* g_unzFiles;\r
88 static GSList* g_pakFiles;\r
89 static char    g_strDirs[VFS_MAXDIRS][PATH_MAX];\r
90 static int     g_numDirs;\r
91 static bool    g_bUsePak = true;\r
92 \r
93 // =============================================================================\r
94 // Static functions\r
95 \r
96 static void vfsAddSlash (char *str)\r
97 {\r
98   int n = strlen (str);\r
99   if (n > 0)\r
100   {\r
101     if (str[n-1] != '\\' && str[n-1] != '/')\r
102       strcat (str, "/");\r
103   }\r
104 }\r
105 \r
106 static void vfsFixDOSName (char *src)\r
107 {\r
108   if (src == NULL)\r
109     return;\r
110 \r
111   while (*src)\r
112   {\r
113     if (*src == '\\')\r
114       *src = '/';\r
115     src++;\r
116   }\r
117 }\r
118 \r
119 static void vfsInitPakFile (const char *filename)\r
120 {\r
121   pakheader_t header;\r
122   FILE *f;\r
123   long i;\r
124 \r
125   f = fopen (filename, "rb");\r
126   if (f == NULL)\r
127     return;\r
128 \r
129   // read header\r
130   fread (header.magic, 1, 4, f);\r
131   fread (&header.diroffset, 1, 4, f);\r
132   fread (&header.dirsize, 1, 4, f);\r
133 \r
134   // fix endianess\r
135   header.diroffset = GINT32_FROM_LE (header.diroffset);\r
136   header.dirsize = GINT32_FROM_LE (header.dirsize);\r
137 \r
138   // check that the magic header\r
139   if (strncmp (header.magic, "PACK", 4))\r
140   {\r
141     fclose (f);\r
142     return;\r
143   }\r
144 \r
145   g_FuncTable.m_pfnSysPrintf("  pak file: %s\n", filename);\r
146 \r
147   g_unzFiles = g_slist_append (g_unzFiles, f);\r
148   fseek (f, header.diroffset, SEEK_SET);\r
149 \r
150   for (i = 0; i < (long)(header.dirsize/sizeof (pakentry_t)); i++)\r
151   {\r
152     VFS_PAKFILE* file;\r
153 \r
154     file = (VFS_PAKFILE*)g_malloc (sizeof (VFS_PAKFILE));\r
155     g_pakFiles = g_slist_append (g_pakFiles, file);\r
156 \r
157     fread (file->entry.filename, 1, sizeof (file->entry.filename), f);\r
158     fread (&file->entry.offset, 1, sizeof (file->entry.offset), f);\r
159     fread (&file->entry.size, 1, sizeof (file->entry.size), f);\r
160     file->pak = f;\r
161 \r
162     // fix endianess\r
163     file->entry.offset = GINT32_FROM_LE (file->entry.offset);\r
164     file->entry.size = GINT32_FROM_LE (file->entry.size);\r
165 \r
166     // fix filename\r
167     vfsFixDOSName (file->entry.filename);\r
168     g_strdown (file->entry.filename);\r
169     //g_FuncTable.m_pfnSysPrintf("vfs file from pak: %s\n", file->entry.filename);\r
170   }\r
171 }\r
172 \r
173 static GSList* vfsGetListInternal (const char *dir, const char *ext, bool directories)\r
174 {\r
175   GSList *lst, *lst_aux, *files = NULL;\r
176   char dirname[NAME_MAX], extension[NAME_MAX], filename[NAME_MAX];\r
177   int dirlen;\r
178   char *ptr;\r
179   //struct dirent *dirlist;\r
180   char *dirlist;\r
181   struct stat st;\r
182   GDir *diskdir;\r
183   int i;\r
184 \r
185   dirname[0] = '\0';\r
186   if (dir != NULL)\r
187   {\r
188     strcat (dirname, dir);\r
189         g_strdown (dirname);\r
190         vfsFixDOSName (dirname);\r
191         vfsAddSlash (dirname);\r
192         Sys_Printf("vfs dirname_1: %s\n", dirname);\r
193   }\r
194   //else\r
195   //  dirname[0] = '\0';\r
196   dirlen = strlen (dirname);\r
197 \r
198   if (ext != NULL)\r
199     strcpy (extension, ext);\r
200   else\r
201     extension[0] = '\0';\r
202   g_strdown (extension);\r
203 \r
204   for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))\r
205   {\r
206     VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;\r
207     gboolean found = FALSE;\r
208     ptr = file->entry.filename;\r
209 \r
210     // check that the file name begins with dirname\r
211     for (i = 0; (*ptr && i < dirlen); i++, ptr++)\r
212       if (*ptr != dirname[i])\r
213         break;\r
214 \r
215     if (i != dirlen)\r
216       continue;\r
217 \r
218     if (directories)\r
219     {\r
220       char *sep = strchr (ptr, '/');\r
221       if (sep == NULL)\r
222         continue;\r
223 \r
224       i = sep-ptr;\r
225 \r
226       // check for duplicates\r
227       for (lst_aux = files; lst_aux; lst_aux = g_slist_next (lst_aux))\r
228         if (strncmp ((char*)lst_aux->data, ptr, i) == 0)\r
229         {\r
230           found = TRUE;\r
231           break;\r
232         }\r
233 \r
234       if (!found)\r
235       {\r
236         char *name = g_strndup (ptr, i+1);\r
237         name[i] = '\0';\r
238         files = g_slist_append (files, name);\r
239       }\r
240     }\r
241     else\r
242     {\r
243       // check extension\r
244       if ((ext != NULL) && (strstr (ptr, extension) == NULL))\r
245         continue;\r
246 \r
247       // check for duplicates\r
248       for (lst_aux = files; lst_aux; lst_aux = g_slist_next (lst_aux))\r
249         if (strcmp ((char*)lst_aux->data, ptr) == 0)\r
250         {\r
251           found = TRUE;\r
252           break;\r
253         }\r
254 \r
255       if (!found)\r
256         files = g_slist_append (files, g_strdup (ptr));\r
257     }\r
258   }\r
259   \r
260   for (i = 0; i < g_numDirs; i++)\r
261   {\r
262     strcpy (dirname, g_strDirs[i]);\r
263     strcat (dirname, dir);\r
264     g_strdown (dirname);\r
265         vfsFixDOSName (dirname);\r
266         vfsAddSlash (dirname);\r
267 \r
268     diskdir = g_dir_open (dirname, 0, NULL);\r
269 \r
270         if (diskdir != NULL)\r
271     {\r
272       while (1)\r
273       {\r
274         const char* name = g_dir_read_name(diskdir);\r
275         if(name == NULL)\r
276           break;\r
277 \r
278         if (directories && (name[0] == '.'))\r
279           continue;\r
280 \r
281         sprintf (filename, "%s%s", dirname, name);\r
282         stat (filename, &st);\r
283                 Sys_Printf("vfs FileName: %s\n", filename);\r
284 \r
285         if ((S_ISDIR (st.st_mode) != 0) != directories)\r
286           continue;\r
287 \r
288         gboolean found = FALSE;\r
289 \r
290         dirlist = g_strdup(name);\r
291 \r
292         g_strdown (dirlist);\r
293 \r
294         char *ptr_ext = strrchr (dirlist, '.');\r
295         if(ext == NULL\r
296           || (ext != NULL && ptr_ext != NULL && ptr_ext[0] != '\0' && strcmp (ptr_ext+1, extension) == 0))\r
297         {\r
298 \r
299           // check for duplicates\r
300           for (lst_aux = files; lst_aux; lst_aux = g_slist_next (lst_aux))\r
301             if (strcmp ((char*)lst_aux->data, dirlist) == 0)\r
302             {\r
303               found = TRUE;\r
304               break;\r
305             }\r
306 \r
307           if (!found)\r
308             files = g_slist_append (files, g_strdup (dirlist));\r
309         }\r
310 \r
311         g_free(dirlist);\r
312       }\r
313       g_dir_close (diskdir);\r
314     }\r
315   }\r
316 \r
317   return files;\r
318 }\r
319 \r
320 /*!\r
321 This behaves identically to -stricmp(a,b), except that ASCII chars\r
322 [\]^`_ come AFTER alphabet chars instead of before. This is because\r
323 it effectively converts all alphabet chars to uppercase before comparison,\r
324 while stricmp converts them to lowercase.\r
325 */\r
326 //!\todo Analyse the code in rtcw/q3 to see how it behaves.\r
327 static int vfsPakSort (const void *a, const void *b)\r
328 {\r
329         char    *s1, *s2;\r
330         int             c1, c2;\r
331 \r
332         s1 = (char*)a;\r
333         s2 = (char*)b;\r
334 \r
335         do {\r
336                 c1 = *s1++;\r
337                 c2 = *s2++;\r
338 \r
339                 if (c1 >= 'a' && c1 <= 'z')\r
340                 {\r
341                         c1 -= ('a' - 'A');\r
342                 }\r
343                 if (c2 >= 'a' && c2 <= 'z')\r
344                 {\r
345                         c2 -= ('a' - 'A');\r
346                 }\r
347 \r
348                 if ( c1 == '\\' || c1 == ':' )\r
349                 {\r
350                         c1 = '/';\r
351                 }\r
352                 if ( c2 == '\\' || c2 == ':' )\r
353                 {\r
354                         c2 = '/';\r
355                 }\r
356 \r
357                 // Arnout: note - sort pakfiles in reverse order. This ensures that\r
358                 // later pakfiles override earlier ones. This because the vfs module\r
359                 // returns a filehandle to the first file it can find (while it should\r
360                 // return the filehandle to the file in the most overriding pakfile, the\r
361                 // last one in the list that is).\r
362                 if (c1 < c2)\r
363                 {\r
364                         //return -1;            // strings not equal\r
365                         return 1;               // strings not equal\r
366                 }\r
367                 if (c1 > c2)\r
368                 {\r
369                         //return 1;\r
370                         return -1;\r
371                 }\r
372         } while (c1);\r
373 \r
374         return 0;               // strings are equal\r
375 }\r
376 \r
377 // =============================================================================\r
378 // Global functions\r
379 \r
380 void vfsInitDirectory (const char *path)\r
381 {\r
382   char filename[PATH_MAX];\r
383   //struct dirent *direntry;\r
384   GDir *dir;\r
385   //GSList *dirlistptr;\r
386   GSList *dirlist = NULL;\r
387 \r
388   if (g_numDirs == (VFS_MAXDIRS-1))\r
389     return;\r
390 \r
391   strcpy (g_strDirs[g_numDirs], path);\r
392   vfsFixDOSName (g_strDirs[g_numDirs]);\r
393   vfsAddSlash (g_strDirs[g_numDirs]);\r
394   g_numDirs++;\r
395 \r
396   if (g_bUsePak)\r
397   {\r
398     dir = g_dir_open (path, 0, NULL);\r
399     if (dir != NULL)\r
400     {\r
401       g_FuncTable.m_pfnSysPrintf("vfs directory: %s\n", path);\r
402 \r
403           for(;;)\r
404       {\r
405         const char* name = g_dir_read_name(dir);\r
406         if(name == NULL)\r
407           break;\r
408 \r
409         char *ext = strrchr (name, '.');\r
410         if ((ext == NULL) || (strcasecmp (ext, ".pak") != 0))\r
411           continue;\r
412 \r
413         char* direntry = g_strdup(name);\r
414                 dirlist = g_slist_append (dirlist, direntry);\r
415       }\r
416 \r
417       g_dir_close (dir);\r
418 \r
419 \r
420       // sort them\r
421       dirlist = g_slist_sort (dirlist, vfsPakSort);\r
422 \r
423       // add the entries to the vfs and free the list\r
424       while (dirlist)\r
425       {\r
426         GSList *cur = dirlist;\r
427         char* name = (char*)cur->data;\r
428 \r
429         sprintf (filename, "%s/%s", path, name);\r
430         vfsInitPakFile (filename);\r
431 \r
432         g_free (name);\r
433         dirlist = g_slist_remove (cur, name);\r
434       }\r
435     } else\r
436           g_FuncTable.m_pfnSysFPrintf(SYS_WRN, "vfs directory not found: %s\n", path);\r
437 \r
438   }\r
439 }\r
440 \r
441 \r
442 // frees all memory that we allocated\r
443 void vfsShutdown ()\r
444 {\r
445   while (g_unzFiles)\r
446   {\r
447     fclose ((FILE*)g_unzFiles->data);\r
448     g_unzFiles = g_slist_remove (g_unzFiles, g_unzFiles->data);\r
449   }\r
450 \r
451   while (g_pakFiles)\r
452   {\r
453     g_free (g_pakFiles->data);\r
454     g_pakFiles = g_slist_remove (g_pakFiles, g_pakFiles->data);\r
455   }\r
456 }\r
457 \r
458 GSList* vfsGetFileList (const char *dir, const char *ext)\r
459 {\r
460   return vfsGetListInternal (dir, ext, false);\r
461 }\r
462 \r
463 GSList* vfsGetDirList (const char *dir)\r
464 {\r
465   return vfsGetListInternal (dir, NULL, true);\r
466 }\r
467 \r
468 void vfsClearFileDirList (GSList **lst)\r
469 {\r
470   while (*lst)\r
471   {\r
472     g_free ((*lst)->data);\r
473     *lst = g_slist_remove (*lst, (*lst)->data);\r
474   }\r
475 }\r
476 \r
477 // return the number of files that match\r
478 int vfsGetFileCount (const char *filename, int flag)\r
479 {\r
480   int i, count = 0;\r
481   char fixed[NAME_MAX], tmp[NAME_MAX];\r
482   GSList *lst;\r
483 \r
484   strcpy (fixed, filename);\r
485   vfsFixDOSName (fixed);\r
486   g_strdown (fixed);\r
487 \r
488   for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))\r
489   {\r
490     VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;\r
491 \r
492     if (strcmp (file->entry.filename, fixed) == 0)\r
493       count++;\r
494   }\r
495 \r
496   for (i = 0; i < g_numDirs; i++)\r
497   {\r
498     strcpy (tmp, g_strDirs[i]);\r
499     strcat (tmp, fixed);\r
500     if (access (tmp, R_OK) == 0)\r
501       count++;\r
502   }\r
503 \r
504   return count;\r
505 }\r
506 \r
507 // NOTE: when loading a file, you have to allocate one extra byte and set it to \0\r
508 int vfsLoadFile (const char *filename, void **bufferptr, int index)\r
509 {\r
510   int i, count = 0;\r
511   char tmp[NAME_MAX], fixed[NAME_MAX];\r
512   GSList *lst;\r
513 \r
514   *bufferptr = NULL;\r
515   strcpy (fixed, filename);\r
516   vfsFixDOSName (fixed);\r
517   g_strdown (fixed);\r
518 \r
519   for (i = 0; i < g_numDirs; i++)\r
520   {\r
521     strcpy (tmp, g_strDirs[i]);\r
522     strcat (tmp, filename);\r
523     if (access (tmp, R_OK) == 0)\r
524     {\r
525       if (count == index)\r
526       {\r
527         long len;\r
528         FILE *f;\r
529 \r
530         f = fopen (tmp, "rb");\r
531         if (f == NULL)\r
532           return -1;\r
533 \r
534         fseek (f, 0, SEEK_END);\r
535         len = ftell (f);\r
536         rewind (f);\r
537 \r
538         *bufferptr = malloc (len+1);\r
539         if (*bufferptr == NULL)\r
540           return -1;\r
541 \r
542         fread (*bufferptr, 1, len, f);\r
543         fclose (f);\r
544 \r
545         // we need to end the buffer with a 0\r
546         ((char*) (*bufferptr))[len] = 0;\r
547 \r
548         return len;\r
549       }\r
550 \r
551       count++;\r
552     }\r
553   }\r
554 \r
555   for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))\r
556   {\r
557     VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;\r
558 \r
559     if (strcmp (file->entry.filename, fixed) != 0)\r
560       continue;\r
561 \r
562     if (count == index)\r
563     {\r
564       fseek (file->pak, file->entry.offset, SEEK_SET);\r
565 \r
566       *bufferptr = malloc (file->entry.size+1);\r
567       // we need to end the buffer with a 0\r
568       ((char*) (*bufferptr))[file->entry.size] = 0;\r
569 \r
570       return fread (*bufferptr, 1, file->entry.size, file->pak);\r
571     }\r
572 \r
573     count++;\r
574   }\r
575 \r
576   return -1;\r
577 }\r
578 \r
579 void vfsFreeFile (void *p)\r
580 {\r
581   g_free(p);\r
582 }\r
583 \r
584 // open a full path file\r
585 int vfsLoadFullPathFile (const char *filename, void **bufferptr)\r
586 {\r
587   FILE *f;\r
588   long len;\r
589 \r
590   f = fopen (filename, "rb");\r
591   if (f == NULL)\r
592     return -1;\r
593 \r
594   fseek (f, 0, SEEK_END);\r
595   len = ftell (f);\r
596   rewind (f);\r
597 \r
598   *bufferptr = g_malloc (len+1);\r
599   if (*bufferptr == NULL)\r
600     return -1;\r
601 \r
602   fread (*bufferptr, 1, len, f);\r
603   fclose (f);\r
604 \r
605   // we need to end the buffer with a 0\r
606   ((char*) (*bufferptr))[len] = 0;\r
607 \r
608   return len;\r
609 }\r
610 \r
611 void vfsCleanFileName(char *in)\r
612 {\r
613   strlwr(in);\r
614   vfsFixDOSName(in);\r
615   int n = strlen(in);\r
616   if (in[n-1] == '/')\r
617     in[n-1] = '\0';\r
618 }\r
619 \r
620 const char* vfsBasePromptPath()\r
621 {\r
622 #ifdef _WIN32\r
623   static char* path = "C:";\r
624 #else\r
625   static char* path = "/";\r
626 #endif\r
627   return path;\r
628 }\r
629 \r
630 /*!\r
631 \param shorten will try to match against the short version\r
632 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=144\r
633 recent switch back to short path names in project settings has broken some stuff\r
634 with shorten == true, we will convert in to short version before looking for root\r
635 FIXME WAAA .. the stuff below is much more simple on linux .. add appropriate #ifdef\r
636 */\r
637 char* vfsExtractRelativePath_short(const char *in, bool shorten)\r
638 {\r
639   int i;\r
640   char l_in[PATH_MAX];\r
641   char check[PATH_MAX];\r
642   static char out[PATH_MAX];\r
643   out[0] = 0;\r
644 \r
645 #ifdef DBG_RLTPATH\r
646   Sys_Printf("vfsExtractRelativePath: %s\n", in);\r
647 #endif\r
648 \r
649 #ifdef _WIN32\r
650   if (shorten)\r
651   {\r
652     // make it short\r
653     if (GetShortPathName(in, l_in, PATH_MAX) == 0)\r
654     {\r
655 #ifdef DBG_RLTPATH\r
656       Sys_Printf("GetShortPathName failed\n");\r
657 #endif\r
658       return NULL;\r
659     }\r
660   }\r
661   else\r
662   {\r
663     strcpy(l_in,in);\r
664   }\r
665   vfsCleanFileName(l_in);\r
666 #else\r
667   strcpy(l_in, in);\r
668   vfsCleanFileName(l_in);\r
669 #endif // ifdef WIN32\r
670 \r
671 \r
672 #ifdef DBG_RLTPATH\r
673   Sys_Printf("cleaned path: %s\n", l_in);\r
674 #endif\r
675 \r
676   for (i = 0; i < g_numDirs; i++)\r
677   {\r
678     strcpy(check,g_strDirs[i]);\r
679     vfsCleanFileName(check);\r
680 #ifdef DBG_RLTPATH\r
681     Sys_Printf("Matching against %s\n", check);\r
682 #endif\r
683 \r
684     // try to find a match\r
685     if (strstr(l_in, check))\r
686     {\r
687       strcpy(out,l_in+strlen(check)+1);\r
688       break;\r
689     }\r
690 \r
691   }\r
692   if (out[0]!=0)\r
693   {\r
694 #ifdef DBG_RLTPATH\r
695     Sys_Printf("vfsExtractRelativePath: success\n");\r
696 #endif\r
697     return out;\r
698   }\r
699 #ifdef DBG_RLTPATH\r
700   Sys_Printf("vfsExtractRelativePath: failed\n");\r
701 #endif\r
702   return NULL;\r
703 }\r
704 \r
705 // HYDRA: this now searches VFS/PAK files in addition to the filesystem\r
706 // if FLAG is unspecified then ONLY dirs are searched.\r
707 // PAK's are searched before DIRs to mimic engine behaviour\r
708 // index is ignored when searching PAK files.\r
709 // see ifilesystem.h\r
710 char* vfsGetFullPath(const char *in, int index, int flag)\r
711 {\r
712   int count = 0;\r
713   static char out[PATH_MAX];\r
714   char tmp[NAME_MAX];\r
715   int i;\r
716 \r
717   if (flag & VFS_SEARCH_PAK)\r
718   {\r
719     char fixed[NAME_MAX];\r
720     GSList *lst;\r
721 \r
722     strcpy (fixed, in);\r
723     vfsFixDOSName (fixed);\r
724     g_strdown (fixed);\r
725 \r
726     for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))\r
727     {\r
728       VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;\r
729 \r
730       char *ptr,*lastptr;\r
731       lastptr = file->name;\r
732 \r
733       while (ptr = strchr(lastptr,'/'))\r
734         lastptr = ptr+1;\r
735 \r
736       if (strcmp (lastptr, fixed) == 0)\r
737       {\r
738         strncpy(out,file->name,PATH_MAX);\r
739         return out;\r
740       }\r
741     }\r
742 \r
743   }\r
744 \r
745   if (!flag || (flag & VFS_SEARCH_DIR))\r
746   {\r
747   for (i = 0; i < g_numDirs; i++)\r
748   {\r
749     strcpy (tmp, g_strDirs[i]);\r
750     strcat (tmp, in);\r
751     if (access (tmp, R_OK) == 0)\r
752     {\r
753       if (count == index)\r
754       {\r
755         strcpy (out, tmp);\r
756         return out;\r
757       }\r
758       count++;\r
759     }\r
760   }\r
761   }\r
762   return NULL;\r
763 }\r
764 \r
765 // FIXME TTimo: this and the above should be merged at some point\r
766 char* vfsExtractRelativePath(const char *in)\r
767 {\r
768   static char out[PATH_MAX];\r
769   unsigned int i, count;\r
770   char *chunk, *backup = NULL; // those point to out stuff\r
771   char *ret = vfsExtractRelativePath_short(in, false);\r
772   if (!ret)\r
773   {\r
774 #ifdef DBG_RLTPATH\r
775     Sys_Printf("trying with a short version\n");\r
776 #endif\r
777     ret = vfsExtractRelativePath_short(in, true);\r
778     if (ret)\r
779     {\r
780       // ok, but we have a relative short version now\r
781       // hack the long relative version out of here\r
782       count = 0;\r
783       for(i=0;i<strlen(ret);i++)\r
784       {\r
785         if (ret[i]=='/')\r
786           count++;\r
787       }\r
788       // this is the clean, not short version\r
789       strcpy(out, in);\r
790       vfsCleanFileName(out);\r
791       for(i=0;i<=count;i++)\r
792       {\r
793         chunk = strrchr(out, '/');\r
794         if (backup)\r
795           backup[0] = '/';\r
796         chunk[0] = '\0';\r
797         backup = chunk;\r
798       }\r
799       return chunk+1;\r
800     }\r
801   }\r
802   return ret;\r
803 }\r