]> icculus.org git repositories - taylor/freespace2.git/blob - src/cfile/cfilesystem.cpp
add function to check if a packfile exists
[taylor/freespace2.git] / src / cfile / cfilesystem.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/CFile/CfileSystem.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Functions to keep track of and find files that can exist
16  * on the harddrive, cd-rom, or in a pack file on either of those.
17  * This keeps a list of all the files in packfiles or on CD-rom
18  * and when you need a file you call one function which then searches
19  * all those locations, inherently enforcing precedence orders.
20  *
21  * $Log$
22  * Revision 1.11  2004/07/04 11:27:29  taylor
23  * cleanup CFILE code a little, warning fixes, remove redundant dir checks, amd64 support
24  *
25  * Revision 1.10  2004/06/11 00:29:22  tigital
26  * byte-swapping changes for bigendian systems
27  *
28  * Revision 1.9  2003/05/27 03:03:11  taylor
29  * fix second root (gamedir) searching
30  *
31  * Revision 1.8  2003/02/20 17:41:07  theoddone33
32  * Userdir patch from Taylor Richards
33  *
34  * Revision 1.7  2002/06/22 23:57:39  relnev
35  * remove writable strings.
36  *
37  * fix compile for intel compiler.
38  *
39  * Revision 1.6  2002/06/09 04:41:15  relnev
40  * added copyright header
41  *
42  * Revision 1.5  2002/06/05 04:03:32  relnev
43  * finished cfilesystem.
44  *
45  * removed some old code.
46  *
47  * fixed mouse save off-by-one.
48  *
49  * sound cleanups.
50  *
51  * Revision 1.4  2002/05/28 17:26:57  theoddone33
52  * Fill in some timer and palette setting stubs.  Still no display
53  *
54  * Revision 1.3  2002/05/28 06:45:38  theoddone33
55  * Cleanup some stuff
56  *
57  * Revision 1.2  2002/05/28 06:28:20  theoddone33
58  * Filesystem mods, actually reads some data files now
59  *
60  * Revision 1.1.1.1  2002/05/03 03:28:08  root
61  * Initial import.
62  *
63  * 
64  * 6     9/08/99 10:01p Dave
65  * Make sure game won't run in a drive's root directory. Make sure
66  * standalone routes suqad war messages properly to the host.
67  * 
68  * 5     9/03/99 1:31a Dave
69  * CD checking by act. Added support to play 2 cutscenes in a row
70  * seamlessly. Fixed super low level cfile bug related to files in the
71  * root directory of a CD. Added cheat code to set campaign mission # in
72  * main hall.
73  * 
74  * 4     2/22/99 10:31p Andsager
75  * Get rid of unneeded includes.
76  * 
77  * 3     10/13/98 9:19a Andsager
78  * Add localization support to cfile.  Optional parameter with cfopen that
79  * looks for localized files.
80  * 
81  * 2     10/07/98 10:52a Dave
82  * Initial checkin.
83  * 
84  * 1     10/07/98 10:48a Dave
85  * 
86  * 14    8/31/98 2:06p Dave
87  * Make cfile sort the ordering or vp files. Added support/checks for
88  * recognizing "mission disk" players.
89  * 
90  * 13    6/23/98 4:18p Hoffoss
91  * Fixed some bugs with AC release build.
92  * 
93  * 12    5/20/98 10:46p John
94  * Added code that doesn't include duplicate filenames in any file list
95  * functions.
96  * 
97  * 11    5/14/98 2:14p Lawrance2
98  * Use filespec filtering for packfiles
99  * 
100  * 10    5/03/98 11:53a John
101  * Fixed filename case mangling.
102  * 
103  * 9     5/02/98 11:06p Allender
104  * correctly deal with pack pathnames
105  * 
106  * 8     5/01/98 11:41a Allender
107  * Fixed bug with mission saving in Fred.
108  * 
109  * 7     5/01/98 10:21a John
110  * Added code to find all pack files in all trees.   Added code to create
111  * any directories that we write to.
112  * 
113  * 6     4/30/98 10:21p John
114  * Added code to cleanup cfilesystem
115  * 
116  * 5     4/30/98 10:18p John
117  * added source safe header
118  *
119  * $NoKeywords: $
120  */
121
122 #include <stdlib.h>
123 #include <string.h>
124 #include <stdio.h>
125 #include <errno.h>
126 #ifndef PLAT_UNIX
127 #include <io.h>
128 #include <direct.h>
129 #include <windows.h>
130 #include <winbase.h>            /* needed for memory mapping of file functions */
131 #else
132 #include <sys/types.h>
133 #include <dirent.h>
134 #include <fnmatch.h>
135 #include <sys/stat.h>
136 #include <unistd.h>
137 #endif
138
139 #include "pstypes.h"
140 //#include "outwnd.h"
141 //#include "vecmat.h"
142 //#include "timer.h"
143 #include "cfile.h"
144 #include "cfilesystem.h"
145 #include "localize.h"
146
147
148 #define CF_ROOTTYPE_PATH 0
149 #define CF_ROOTTYPE_PACK 1
150
151 //  Created by:
152 //    specifying hard drive tree
153 //    searching for pack files on hard drive            // Found by searching all known paths
154 //    specifying cd-rom tree
155 //    searching for pack files on CD-rom tree
156 typedef struct cf_root {
157         char                            path[CF_MAX_PATHNAME_LENGTH];           // Contains something like c:\projects\freespace or c:\projects\freespace\freespace.vp
158         int                             roottype;                                                               // CF_ROOTTYPE_PATH  = Path, CF_ROOTTYPE_PACK =Pack file
159 } cf_root;
160
161 // convenient type for sorting (see cf_build_pack_list())
162 typedef struct cf_root_sort { 
163         char                            path[CF_MAX_PATHNAME_LENGTH];
164         int                             roottype;
165         int                             cf_type;
166 } cf_root_sort;
167
168 #define CF_NUM_ROOTS_PER_BLOCK   32
169 #define CF_MAX_ROOT_BLOCKS                      256                             // Can store 32*256 = 8192 Roots
170 #define CF_MAX_ROOTS                                    (CF_NUM_ROOTS_PER_BLOCK * CF_MAX_ROOT_BLOCKS)
171
172 typedef struct cf_root_block {
173         cf_root                 roots[CF_NUM_ROOTS_PER_BLOCK];
174 } cf_root_block;
175
176 static int Num_roots = 0;
177 static cf_root_block  *Root_blocks[CF_MAX_ROOT_BLOCKS];
178   
179
180 // Created by searching all roots in order.   This means Files is then sorted by precedence.
181 typedef struct cf_file {
182         char            name_ext[CF_MAX_FILENAME_LENGTH];               // Filename and extension
183         int             root_index;                                                                             // Where in Roots this is located
184         int             pathtype_index;                                                         // Where in Paths this is located
185         time_t  write_time;                                                                             // When it was last written
186         int             size;                                                                                           // How big it is in bytes
187         int             pack_offset;                                                                    // For pack files, where it is at.   0 if not in a pack file.  This can be used to tell if in a pack file.
188 } cf_file;
189
190 #define CF_NUM_FILES_PER_BLOCK   256
191 #define CF_MAX_FILE_BLOCKS                      128                                             // Can store 256*128 = 32768 files
192
193 typedef struct cf_file_block {
194         cf_file                                         files[CF_NUM_FILES_PER_BLOCK];
195 } cf_file_block;
196
197 static int Num_files = 0;
198 static cf_file_block  *File_blocks[CF_MAX_FILE_BLOCKS];
199
200
201 // Return a pointer to to file 'index'.
202 cf_file *cf_get_file(int index)
203 {
204         int block = index / CF_NUM_FILES_PER_BLOCK;
205         int offset = index % CF_NUM_FILES_PER_BLOCK;
206
207         return &File_blocks[block]->files[offset];
208 }
209
210 // Create a new file and return a pointer to it.
211 cf_file *cf_create_file()
212 {
213         int block = Num_files / CF_NUM_FILES_PER_BLOCK;
214         int offset = Num_files % CF_NUM_FILES_PER_BLOCK;
215         
216         if ( File_blocks[block] == NULL )       {
217                 File_blocks[block] = (cf_file_block *)malloc( sizeof(cf_file_block) );
218                 SDL_assert( File_blocks[block] != NULL);
219         }
220
221         Num_files++;
222
223         return &File_blocks[block]->files[offset];
224 }
225
226 extern int cfile_inited;
227
228 // Create a new root and return a pointer to it.  The structure is assumed unitialized.
229 cf_root *cf_get_root(int n)
230 {
231         int block = n / CF_NUM_ROOTS_PER_BLOCK;
232         int offset = n % CF_NUM_ROOTS_PER_BLOCK;
233
234         if (!cfile_inited)
235                 return NULL;
236
237         return &Root_blocks[block]->roots[offset];
238 }
239
240
241 // Create a new root and return a pointer to it.  The structure is assumed unitialized.
242 cf_root *cf_create_root()
243 {
244         int block = Num_roots / CF_NUM_ROOTS_PER_BLOCK;
245         int offset = Num_roots % CF_NUM_ROOTS_PER_BLOCK;
246         
247         if ( Root_blocks[block] == NULL )       {
248                 Root_blocks[block] = (cf_root_block *)malloc( sizeof(cf_root_block) );
249                 SDL_assert(Root_blocks[block] != NULL);
250         }
251
252         Num_roots++;
253
254         return &Root_blocks[block]->roots[offset];
255 }
256
257 // return the # of packfiles which exist
258 int cf_get_packfile_count(cf_root *root)
259 {
260         char filespec[MAX_PATH_LEN];
261         int i;
262         int packfile_count;
263
264         // count up how many packfiles we're gonna have
265         packfile_count = 0;
266         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
267                 SDL_strlcpy( filespec, root->path, sizeof(filespec) );
268
269                 if(strlen(Pathtypes[i].path)){
270                         SDL_strlcat( filespec, Pathtypes[i].path, sizeof(filespec) );
271                         SDL_strlcat( filespec, DIR_SEPARATOR_STR, sizeof(filespec) );
272                 }
273
274 #ifdef PLAT_UNIX
275                 DIR *dirp;
276                 struct dirent *dir;
277
278                 dirp = opendir (filespec);
279                 if ( dirp ) {
280                         while ((dir = readdir (dirp)) != NULL)
281                         {
282                                 if (!fnmatch ("*.vp", dir->d_name, 0))
283                                         packfile_count++;
284                         }
285                         closedir(dirp);
286                 }
287 #else
288                 SDL_strlcat( filespec, "*.vp", sizeof(filespec) );
289
290                 int find_handle;
291                 _finddata_t find;
292                 
293                 find_handle = _findfirst( filespec, &find );
294
295                 if (find_handle != -1) {
296                         do {
297                                 if (!(find.attrib & _A_SUBDIR)) {
298                                         packfile_count++;
299                                 }
300
301                         } while (!_findnext(find_handle, &find));
302
303                         _findclose( find_handle );
304                 }       
305 #endif
306         }       
307
308         return packfile_count;
309 }
310
311 // packfile sort function
312 int cf_packfile_sort_func(const void *elem1, const void *elem2)
313 {
314         cf_root_sort *r1, *r2;
315         r1 = (cf_root_sort*)elem1;
316         r2 = (cf_root_sort*)elem2;
317
318         // if the 2 directory types are the same, do a string compare
319         if(r1->cf_type == r2->cf_type){
320                 return SDL_strcasecmp(r1->path, r2->path);
321         }
322
323         // otherwise return them in order of CF_TYPE_* precedence
324         return (r1->cf_type < r2->cf_type) ? -1 : 1;
325 }
326
327 // Go through a root and look for pack files
328 void cf_build_pack_list( cf_root *root )
329 {
330         char filespec[MAX_PATH_LEN];
331         int i;
332         cf_root_sort *temp_roots_sort, *rptr_sort;
333         int temp_root_count, root_index;
334
335         // determine how many packfiles there are
336         temp_root_count = cf_get_packfile_count(root);
337         if(temp_root_count <= 0){
338                 return;
339         }
340
341         // allocate a temporary array of temporary roots so we can easily sort them
342         temp_roots_sort = (cf_root_sort*)malloc(sizeof(cf_root_sort) * temp_root_count);
343         if(temp_roots_sort == NULL){
344                 Int3();
345                 return;
346         }
347
348         // now just setup all the root info
349         root_index = 0;
350         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
351                 SDL_strlcpy( filespec, root->path, sizeof(filespec) );
352
353                 if(strlen(Pathtypes[i].path)){
354                         SDL_strlcat( filespec, Pathtypes[i].path, sizeof(filespec) );
355                         SDL_strlcat( filespec, DIR_SEPARATOR_STR, sizeof(filespec) );
356                 }
357
358 #ifdef PLAT_UNIX
359                 DIR *dirp;
360                 struct dirent *dir;
361
362                 dirp = opendir (filespec);
363                 if ( dirp ) {
364                         while ((dir = readdir (dirp)) != NULL)
365                         {
366                                 if (!fnmatch ("*.vp", dir->d_name, 0))
367                                 {
368                                         SDL_assert(root_index < temp_root_count);
369
370                                         char fn[MAX_PATH_LEN];
371                                         SDL_snprintf(fn, sizeof(fn), "%s/%s", filespec, dir->d_name);
372                                                         
373                                         struct stat buf;
374                                         if (stat(fn, &buf) == -1) {
375                                                 continue;
376                                         }
377                                         
378                                         if (!S_ISREG(buf.st_mode)) {
379                                                 continue;
380                                         }
381                                         
382                                         // get a temp pointer
383                                         rptr_sort = &temp_roots_sort[root_index++];
384
385                                         // fill in all the proper info
386                                         SDL_strlcpy(rptr_sort->path, root->path, sizeof(rptr_sort->path));
387
388                                         if(strlen(Pathtypes[i].path)){
389                                                 SDL_strlcat(rptr_sort->path, Pathtypes[i].path, sizeof(rptr_sort->path));
390                                                 SDL_strlcat(rptr_sort->path, "/", sizeof(rptr_sort->path));
391                                         }
392
393                                         SDL_strlcat(rptr_sort->path, dir->d_name, sizeof(rptr_sort->path));
394                                         rptr_sort->roottype = CF_ROOTTYPE_PACK;
395                                         rptr_sort->cf_type = i;
396                                 }
397                         }
398                         closedir(dirp);
399                 }
400 #else
401                 SDL_strlcat( filespec, "*.vp", sizeof(filespec) );
402
403                 int find_handle;
404                 _finddata_t find;
405                 
406                 find_handle = _findfirst( filespec, &find );
407
408                 if (find_handle != -1) {
409                         do {
410                                 // add the new item
411                                 if (!(find.attrib & _A_SUBDIR)) {                                       
412                                         SDL_assert(root_index < temp_root_count);
413
414                                         // get a temp pointer
415                                         rptr_sort = &temp_roots_sort[root_index++];
416
417                                         // fill in all the proper info
418                                         SDL_strlcpy(rptr_sort->path, root->path, sizeof(rptr_sort->path));
419
420                                         if(strlen(Pathtypes[i].path)){
421                                                 SDL_strlcat(rptr_sort->path, Pathtypes[i].path, sizeof(rptr_sort->path) );
422                                                 SDL_strlcat(rptr_sort->path, "\\", sizeof(rptr_sort->path));
423                                         }
424
425                                         SDL_strlcat(rptr_sort->path, find.name, sizeof(rptr_sort->path) );
426                                         rptr_sort->roottype = CF_ROOTTYPE_PACK;
427                                         rptr_sort->cf_type = i;
428                                 }
429
430                         } while (!_findnext(find_handle, &find));
431
432                         _findclose( find_handle );
433                 }       
434 #endif
435         }       
436
437         // these should always be the same
438         SDL_assert(root_index == temp_root_count);
439
440         // sort tht roots
441         qsort(temp_roots_sort,  temp_root_count, sizeof(cf_root_sort), cf_packfile_sort_func);
442
443         // now insert them all into the real root list properly
444         cf_root *new_root;
445         for(i=0; i<temp_root_count; i++){               
446                 new_root = cf_create_root();
447                 SDL_strlcpy( new_root->path, root->path, sizeof(new_root->path) );
448
449                 // mwa -- 4/2/98 put in the next 2 lines because the path name needs to be there
450                 // to find the files.
451                 SDL_strlcpy(new_root->path, temp_roots_sort[i].path, sizeof(new_root->path));
452                 new_root->roottype = CF_ROOTTYPE_PACK;          
453         }
454
455         // free up the temp list
456         free(temp_roots_sort);
457 }
458
459
460 void cf_build_root_list(const char *extras_dir)
461 {
462         Num_roots = 0;
463
464         cf_root *root;
465
466         // ================================================================
467         // have user's writable directory as default for loading and saving files
468         root = cf_create_root();
469         SDL_strlcpy( root->path, Cfile_user_dir, sizeof(root->path) );
470
471         // do we already have a slash? as in the case of a root directory install
472         if(strlen(root->path) && (root->path[strlen(root->path)-1] != DIR_SEPARATOR_CHAR)){
473                 SDL_strlcat(root->path, DIR_SEPARATOR_STR, sizeof(root->path));         // put trailing backslash on for easier path construction
474         }
475         root->roottype = CF_ROOTTYPE_PATH;
476
477         //======================================================
478         // then check any VP files under the directory.
479         cf_build_pack_list(root);
480
481         //======================================================
482         // Next, use the executable's directory for game data
483         root = cf_create_root();
484         SDL_strlcpy( root->path, Cfile_root_dir, sizeof(root->path) );
485
486         // do we already have a slash? as in the case of a root directory install
487         if(strlen(root->path) && (root->path[strlen(root->path)-1] != DIR_SEPARATOR_CHAR)){
488                 SDL_strlcat(root->path, DIR_SEPARATOR_STR, sizeof(root->path));         // put trailing backslash on for easier path construction
489         }
490         root->roottype = CF_ROOTTYPE_PATH;
491
492         //======================================================
493         // then check any VP files under the current directory.
494         cf_build_pack_list(root);
495
496
497         //======================================================
498         // Check the real CD if one...
499         if ( extras_dir && strlen(extras_dir) ) {
500                 root = cf_create_root();
501                 SDL_strlcpy( root->path, extras_dir, sizeof(root->path) );
502                 root->roottype = CF_ROOTTYPE_PATH;
503
504                 //======================================================
505                 // Next, check any VP files in the CD-ROM directory.
506                 cf_build_pack_list(root);
507         }
508 }
509
510 // Given a lower case list of file extensions 
511 // separated by spaces, return zero if ext is
512 // not in the list.
513 int is_ext_in_list( const char *ext_list, char *ext )
514 {
515         char tmp_ext[128];
516
517         SDL_strlcpy( tmp_ext, ext, sizeof(tmp_ext) );
518         SDL_strlwr(tmp_ext);
519         if ( strstr(ext_list, tmp_ext ))        {
520                 return 1;
521         }       
522
523         return 0;
524 }
525
526 void cf_search_root_path(int root_index)
527 {
528         int i;
529
530         cf_root *root = cf_get_root(root_index);
531
532         mprintf(( "Searching root '%s'\n", root->path ));
533
534         char search_path[CF_MAX_PATHNAME_LENGTH];
535
536         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
537
538                 SDL_strlcpy( search_path, root->path, sizeof(search_path) );
539
540                 if(strlen(Pathtypes[i].path)){
541                         SDL_strlcat( search_path, Pathtypes[i].path, sizeof(search_path) );
542                         SDL_strlcat( search_path, DIR_SEPARATOR_STR, sizeof(search_path) );
543                 }
544
545 #ifdef PLAT_UNIX
546                 DIR *dirp;
547                 struct dirent *dir;
548
549                 // only get pilots from the primary root as there could be nasty
550                 // permission issues otherwise
551                 if ( (root_index > 0) && ((Pathtypes[i].index == CF_TYPE_SINGLE_PLAYERS)
552                                         || (Pathtypes[i].index == CF_TYPE_MULTI_PLAYERS)) ) {
553                         break;
554                 }
555
556                 dirp = opendir (search_path);
557                 if ( dirp ) {
558                         while ((dir = readdir (dirp)) != NULL)
559                         {
560                                 if (!fnmatch ("*.*", dir->d_name, 0))
561                                 {
562                                         char fn[MAX_PATH_LEN];
563                                         SDL_snprintf(fn, MAX_PATH_LEN, "%s/%s", search_path, dir->d_name);
564                                                         
565                                         struct stat buf;
566                                         if (stat(fn, &buf) == -1) {
567                                                 continue;
568                                         }
569                                         
570                                         if (!S_ISREG(buf.st_mode)) {
571                                                 continue;
572                                         }
573                                         
574                                         char *ext = SDL_strchr( dir->d_name, '.' );
575                                         if ( ext )      {
576                                                 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) )   {
577                                                         // Found a file!!!!
578                                                         cf_file *file = cf_create_file();
579
580                                                         SDL_strlcpy( file->name_ext, dir->d_name, sizeof(file->name_ext) );
581                                                         file->root_index = root_index;
582                                                         file->pathtype_index = i;
583
584
585                                                         file->write_time = buf.st_mtime;
586                                                         file->size = buf.st_size;
587
588                                                         file->pack_offset = 0;                  // Mark as a non-packed file
589
590                                                         //mprintf(( "Found file '%s'\n", file->name_ext ));
591                                                 }
592                                         }
593                                 }
594                         }
595                         closedir(dirp);
596                 }
597 #else
598                 SDL_strlcat( search_path, "*.*", sizeof(search_path) );
599
600                 int find_handle;
601                 _finddata_t find;
602                 
603                 find_handle = _findfirst( search_path, &find );
604
605                 if (find_handle != -1) {
606                         do {
607                                 if (!(find.attrib & _A_SUBDIR)) {
608
609                                         char *ext = SDL_strchr( find.name, '.' );
610                                         if ( ext )      {
611                                                 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) )   {
612                                                         // Found a file!!!!
613                                                         cf_file *file = cf_create_file();
614
615                                                         SDL_strlcpy( file->name_ext, find.name, sizeof(file->name_ext) );
616                                                         file->root_index = root_index;
617                                                         file->pathtype_index = i;
618                                                         file->write_time = find.time_write;
619                                                         file->size = find.size;
620                                                         file->pack_offset = 0;                  // Mark as a non-packed file
621
622                                                         //mprintf(( "Found file '%s'\n", file->name_ext ));
623
624                                                 }
625                                         }
626
627                                 }
628
629                         } while (!_findnext(find_handle, &find));
630
631                         _findclose( find_handle );
632                 }
633 #endif
634
635         }
636 }
637
638
639 typedef struct VP_FILE_HEADER {
640         char id[4];
641         int version;
642         int index_offset;
643         int num_files;
644 } VP_FILE_HEADER;
645
646 typedef struct VP_FILE {
647         int     offset;
648         int     size;
649         char    filename[32];
650         fs_time_t write_time;
651 } VP_FILE;
652
653 void cf_search_root_pack(int root_index)
654 {
655         int i;
656
657         cf_root *root = cf_get_root(root_index);
658
659         //mprintf(( "Searching root pack '%s'\n", root->path ));
660
661         // Open data
662                 
663         FILE *fp = fopen( root->path, "rb" );
664         // Read the file header
665         if (!fp) {
666                 return;
667         }
668
669         VP_FILE_HEADER VP_header;
670
671         SDL_assert( sizeof(VP_header) == 16 );
672         fread(&VP_header, 1, sizeof(VP_header), fp);
673
674     VP_header.version = INTEL_INT( VP_header.version);
675     VP_header.index_offset = INTEL_INT( VP_header.index_offset);
676     VP_header.num_files = INTEL_INT( VP_header.num_files);
677         
678         // Read index info
679         fseek(fp, VP_header.index_offset, SEEK_SET);
680
681         char search_path[CF_MAX_PATHNAME_LENGTH];
682
683         SDL_strlcpy( search_path, "", sizeof(search_path) );
684         
685         // Go through all the files
686         for (i=0; i<VP_header.num_files; i++ )  {
687                 VP_FILE find;
688
689                 fread( &find, sizeof(VP_FILE), 1, fp );
690
691         find.offset = INTEL_INT( find.offset );
692         find.size = INTEL_INT( find.size );
693                 find.write_time = INTEL_INT(find.write_time);
694
695                 if ( find.size == 0 )   {
696                         if ( !SDL_strcasecmp( find.filename, ".." ))    {
697                                 int l = strlen(search_path);
698                                 char *p = &search_path[l-1];
699                                 while( (p > search_path) && (*p != DIR_SEPARATOR_CHAR) )        {
700                                         p--;
701                                 }
702                                 *p = 0;
703                         } else {
704                                 if ( strlen(search_path)        )       {
705                                         SDL_strlcat( search_path,       DIR_SEPARATOR_STR, sizeof(search_path) );
706                                 }
707                                 SDL_strlcat( search_path, find.filename, sizeof(search_path) );
708                         }
709
710                         //mprintf(( "Current dir = '%s'\n", search_path ));
711                 } else {
712         
713                         int j;
714                         for (j=CF_TYPE_ROOT; j<CF_MAX_PATH_TYPES; j++ ) {
715
716                                 if ( !SDL_strcasecmp( search_path, Pathtypes[j].path )) {
717
718                                         char *ext = SDL_strchr( find.filename, '.' );
719                                         if ( ext )      {
720                                                 if ( is_ext_in_list( Pathtypes[j].extensions, ext ) )   {
721                                                         // Found a file!!!!
722                                                         cf_file *file = cf_create_file();
723                                                         
724                                                         SDL_strlcpy( file->name_ext, find.filename, sizeof(file->name_ext) );
725                                                         file->root_index = root_index;
726                                                         file->pathtype_index = j;
727                                                         file->write_time = find.write_time;
728                                                         file->size = find.size;
729                                                         file->pack_offset = find.offset;                        // Mark as a non-packed file
730
731                                                         //mprintf(( "Found pack file '%s'\n", file->name_ext ));
732                                                 }
733                                         }
734                                         
735
736                                 }
737                         }
738
739                 }
740         }
741         fclose(fp);
742 }
743
744
745 void cf_build_file_list()
746 {
747         int i;
748
749         Num_files = 0;
750
751         // For each root, find all files...
752         for (i=1; i<Num_roots; i++ )    {
753                 cf_root *root = cf_get_root(i);
754                 if ( root->roottype == CF_ROOTTYPE_PATH )       {
755                         cf_search_root_path(i);
756                 } else if ( root->roottype == CF_ROOTTYPE_PACK )        {
757                         cf_search_root_pack(i);
758                 }
759         }
760
761 }
762
763
764 void cf_build_secondary_filelist(const char *extras_dir)
765 {
766         int i;
767
768         // Assume no files
769         Num_roots = 0;
770         Num_files = 0;
771
772         // Init the path types
773         for (i=0; i<CF_MAX_PATH_TYPES; i++ )    {
774                 SDL_assert( Pathtypes[i].index == i );
775 #if 0 /* they are already lowercased -- SBF */
776                 if ( Pathtypes[i].extensions )  {
777                         SDL_strlwr(Pathtypes[i].extensions);
778                 }
779 #endif          
780         }
781         
782         // Init the root blocks
783         for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ )   {
784                 Root_blocks[i] = NULL;
785         }
786
787         // Init the file blocks 
788         for (i=0; i<CF_MAX_FILE_BLOCKS; i++ )   {
789                 File_blocks[i] = NULL;
790         }
791
792         mprintf(( "Building file index...\n" ));
793         
794         // build the list of searchable roots
795         cf_build_root_list(extras_dir);
796
797         // build the list of files themselves
798         cf_build_file_list();
799
800         mprintf(( "Found %d roots and %d files.\n", Num_roots, Num_files ));
801 }
802
803 void cf_free_secondary_filelist()
804 {
805         int i;
806
807         // Free the root blocks
808         for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ )   {
809                 if ( Root_blocks[i] )   {
810                         free( Root_blocks[i] );
811                         Root_blocks[i] = NULL;
812                 }
813         }
814         Num_roots = 0;
815
816         // Init the file blocks 
817         for (i=0; i<CF_MAX_FILE_BLOCKS; i++ )   {
818                 if ( File_blocks[i] )   {
819                         free( File_blocks[i] );
820                         File_blocks[i] = NULL;
821                 }
822         }
823         Num_files = 0;
824 }
825
826 // Searches for a file.   Follows all rules and precedence and searches
827 // CD's and pack files.
828 // Input:  filespace   - Filename & extension
829 //         pathtype    - See CF_TYPE_ defines in CFILE.H
830 // Output: pack_filename - Absolute path and filename of this file.   Could be a packfile or the actual file.
831 //         size        - File size
832 //         offset      - Offset into pack file.  0 if not a packfile.
833 // Returns: If not found returns 0.
834 int cf_find_file_location( const char *filespec, int pathtype, char *pack_filename, int *size, int *offset, bool localize )
835 {
836         int i;
837
838         SDL_assert(filespec && strlen(filespec));
839
840         // see if we have something other than just a filename
841         // our current rules say that any file that specifies a direct
842         // path will try to be opened on that path.  If that open
843         // fails, then we will open the file based on the extension
844         // of the file
845
846 #ifdef PLAT_UNIX
847         const char *toks = "/";
848 #else
849         const char *toks = "/\\:";
850 #endif
851
852         // NOTE: full path should also include localization, if so desired
853         if ( strpbrk(filespec, toks) ) {                // do we have a full path already?
854                 FILE *fp = fopen(filespec, "rb" );
855                 if (fp) {
856                         if ( size ) *size = filelength(fileno(fp));
857                         if ( offset ) *offset = 0;
858                         if ( pack_filename ) {
859                                 SDL_strlcpy( pack_filename, filespec, MAX_PATH_LEN );
860                         }                               
861                         fclose(fp);
862                         return 1;               
863                 }
864
865                 return 0;               // If they give a full path, fail if not found.
866         }
867
868         // Search the hard drive for files first.
869         int num_search_dirs = 0;
870         int search_order[CF_MAX_PATH_TYPES];
871
872         if ( CF_TYPE_SPECIFIED(pathtype) )      {
873                 search_order[num_search_dirs++] = pathtype;
874         }
875
876         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++)  {
877                 if ( i != pathtype )    {
878                         search_order[num_search_dirs++] = i;
879                 }
880         }
881
882         for (i=0; i<num_search_dirs; i++ )      {
883                 char longname[MAX_PATH_LEN];
884
885                 cf_create_default_path_string( longname, search_order[i], filespec, localize );
886
887                 FILE *fp = fopen(longname, "rb" );
888                 if (fp) {
889                         if ( size ) *size = filelength(fileno(fp));
890                         if ( offset ) *offset = 0;
891                         if ( pack_filename ) {
892                                 SDL_strlcpy( pack_filename, longname, MAX_PATH_LEN );
893                         }                               
894                         fclose(fp);
895                         return 1;               
896                 }
897         } 
898
899         // Search the pak files and CD-ROM.
900
901                 for (i=0; i<Num_files; i++ )    {
902                         cf_file * f = cf_get_file(i);
903
904                         // only search paths we're supposed to...
905                         if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index)  )    {
906                                 continue;
907                         }
908
909                         if (localize) {
910                                 // create localized filespec
911                                 char loc_filespec[MAX_PATH_LEN];
912                                 SDL_strlcpy(loc_filespec, filespec, sizeof(loc_filespec));
913                                 lcl_add_dir_to_path_with_filename(loc_filespec, sizeof(loc_filespec));
914                         
915                                 if ( !SDL_strcasecmp(loc_filespec, f->name_ext) )       {
916                                         if ( size ) *size = f->size;
917                                         if ( offset ) *offset = f->pack_offset;
918                                         if ( pack_filename ) {
919                                                 cf_root * r = cf_get_root(f->root_index);
920
921                                                 SDL_strlcpy( pack_filename, r->path, MAX_PATH_LEN );
922                                                 if ( f->pack_offset < 1 )       {
923                                                         SDL_strlcat( pack_filename, Pathtypes[f->pathtype_index].path, MAX_PATH_LEN );
924                                                         SDL_strlcat( pack_filename, DIR_SEPARATOR_STR, MAX_PATH_LEN );
925                                                         SDL_strlcat( pack_filename, f->name_ext, MAX_PATH_LEN );
926                                                 }
927                                         }                               
928                                         return 1;               
929                                 }
930                         }
931
932                         // file either not localized or localized version not found
933                         if ( !SDL_strcasecmp(filespec, f->name_ext) )   {
934                                 if ( size ) *size = f->size;
935                                 if ( offset ) *offset = f->pack_offset;
936                                 if ( pack_filename ) {
937                                         cf_root * r = cf_get_root(f->root_index);
938
939                                         SDL_strlcpy( pack_filename, r->path, MAX_PATH_LEN );
940                                         if ( f->pack_offset < 1 )       {
941
942                                                 if(strlen(Pathtypes[f->pathtype_index].path)){
943                                                         SDL_strlcat( pack_filename, Pathtypes[f->pathtype_index].path, MAX_PATH_LEN );
944                                                         SDL_strlcat( pack_filename, DIR_SEPARATOR_STR, MAX_PATH_LEN );
945                                                 }
946
947                                                 SDL_strlcat( pack_filename, f->name_ext, MAX_PATH_LEN );
948                                         }
949                                 }                               
950                                 return 1;               
951                         }
952                 }
953         
954         return 0;
955 }
956
957
958 // Returns true if filename matches filespec, else zero if not
959 int cf_matches_spec(const char *filespec, const char *filename)
960 {
961         const char *src_ext, *dst_ext;
962
963         src_ext = SDL_strchr(filespec, '.');
964         if (!src_ext)
965                 return 1;
966         if (*src_ext == '*')
967                 return 1;
968
969         dst_ext = SDL_strchr(filename, '.');
970         if (!dst_ext)
971                 return 1;
972         
973         return !SDL_strcasecmp(dst_ext, src_ext);
974 }
975
976 int (*Get_file_list_filter)(const char *filename) = NULL;
977 int Skip_packfile_search = 0;
978
979 int cf_file_already_in_list( int num_files, char **list, char *filename )
980 {
981         int i;
982
983         char name_no_extension[MAX_PATH_LEN];
984
985         SDL_strlcpy(name_no_extension, filename, sizeof(name_no_extension));
986         char *p = SDL_strchr( name_no_extension, '.' );
987         if ( p ) *p = 0;
988
989         for (i=0; i<num_files; i++ )    {
990                 if ( !SDL_strcasecmp(list[i], name_no_extension ) )     {
991                         // Match found!
992                         return 1;
993                 }
994         }
995         // Not found
996         return 0;
997 }
998
999 // An alternative cf_get_file_list(), dynamic list version.
1000 // This one has a 'type', which is a CF_TYPE_* value.  Because this specifies the directory
1001 // location, 'filter' only needs to be the filter itself, with no path information.
1002 // See above descriptions of cf_get_file_list() for more information about how it all works.
1003 int cf_get_file_list( int max, char **list, int pathtype, const char *filter, int sort, file_list_info *info )
1004 {
1005         char *ptr;
1006         int i, l, num_files = 0, own_flag = 0;
1007
1008         if (max < 1) {
1009                 Get_file_list_filter = NULL;
1010                 return 0;
1011         }
1012
1013         SDL_assert(list);
1014
1015         if (!info && (sort == CF_SORT_TIME)) {
1016                 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1017                 own_flag = 1;
1018         }
1019
1020         char filespec[MAX_PATH_LEN];
1021
1022 #ifdef PLAT_UNIX
1023         cf_create_default_path_string( filespec, pathtype, NULL );
1024
1025         DIR *dirp;
1026         struct dirent *dir;
1027
1028         dirp = opendir (filespec);
1029         if ( dirp ) {
1030                 while ((dir = readdir (dirp)) != NULL)
1031                 {
1032                         if (num_files >= max)
1033                                 break;
1034
1035                         if (fnmatch(filter, dir->d_name, 0) != 0)
1036                                 continue;
1037
1038                         char fn[MAX_PATH_LEN];
1039                         SDL_snprintf(fn, MAX_PATH_LEN, "%s/%s", filespec, dir->d_name);
1040
1041                         struct stat buf;
1042                         if (stat(fn, &buf) == -1) {
1043                                 continue;
1044                         }
1045
1046                         if (!S_ISREG(buf.st_mode)) {
1047                                 continue;
1048                         }
1049
1050                         if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1051                                 ptr = strrchr(dir->d_name, '.');
1052                                 if (ptr)
1053                                         l = ptr - dir->d_name;
1054                                 else
1055                                         l = strlen(dir->d_name);
1056
1057                                 list[num_files] = (char *)malloc(l + 1);
1058                                 SDL_strlcpy(list[num_files], dir->d_name, l+1);
1059                                 if (info)
1060                                         info[num_files].write_time = buf.st_mtime;
1061
1062                                 num_files++;
1063                         }
1064                 }
1065
1066                 closedir(dirp);
1067         }
1068 #else
1069         cf_create_default_path_string( filespec, pathtype, filter );
1070
1071         int find_handle;
1072         _finddata_t find;
1073
1074         find_handle = _findfirst( filespec, &find );
1075         if (find_handle != -1) {
1076                 do {
1077                         if (num_files >= max)
1078                                 break;
1079
1080                         if (!(find.attrib & _A_SUBDIR)) {
1081                                 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1082                                         ptr = strrchr(find.name, '.');
1083                                         if (ptr)
1084                                                 l = ptr - find.name;
1085                                         else
1086                                                 l = strlen(find.name);
1087
1088                                         list[num_files] = (char *)malloc(l + 1);
1089                                         SDL_strlcpy(list[num_files], find.name, l+1);
1090                                         if (info)
1091                                                 info[num_files].write_time = find.time_write;
1092
1093                                         num_files++;
1094                                 }
1095                         }
1096
1097                 } while (!_findnext(find_handle, &find));
1098
1099                 _findclose( find_handle );
1100         }
1101 #endif
1102
1103         // Search all the packfiles and CD.
1104         if ( !Skip_packfile_search )    {
1105                 for (i=0; i<Num_files; i++ )    {
1106                         cf_file * f = cf_get_file(i);
1107
1108                         // only search paths we're supposed to...
1109                         if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index)  )    {
1110                                 continue;
1111                         }
1112
1113                         if (num_files >= max)
1114                                 break;
1115
1116                         if ( !cf_matches_spec( filter,f->name_ext))     {
1117                                 continue;
1118                         }
1119
1120                         if ( cf_file_already_in_list(num_files,list,f->name_ext))       {
1121                                 continue;
1122                         }
1123
1124                         if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1125
1126                                 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1127
1128                                         ptr = strrchr(f->name_ext, '.');
1129                                         if (ptr)
1130                                                 l = ptr - f->name_ext;
1131                                         else
1132                                                 l = strlen(f->name_ext);
1133
1134                                         list[num_files] = (char *)malloc(l + 1);
1135                                         SDL_strlcpy(list[num_files], f->name_ext, l+1);
1136
1137                                 if (info)       {
1138                                         info[num_files].write_time = f->write_time;
1139                                 }
1140
1141                                 num_files++;
1142                         }
1143
1144                 }
1145         }
1146
1147
1148         if (sort != CF_SORT_NONE)       {
1149                 cf_sort_filenames( num_files, list, sort, info );
1150         }
1151
1152         if (own_flag)   {
1153                 free(info);
1154         }
1155
1156         Get_file_list_filter = NULL;
1157         return num_files;
1158 }
1159
1160 int cf_file_already_in_list_preallocated( int num_files, char arr[][MAX_FILENAME_LEN], const char *filename )
1161 {
1162         int i;
1163
1164         char name_no_extension[MAX_PATH_LEN];
1165
1166         SDL_strlcpy(name_no_extension, filename, sizeof(name_no_extension));
1167         char *p = SDL_strchr( name_no_extension, '.' );
1168         if ( p ) *p = 0;
1169
1170         for (i=0; i<num_files; i++ )    {
1171                 if ( !SDL_strcasecmp(arr[i], name_no_extension ) )      {
1172                         // Match found!
1173                         return 1;
1174                 }
1175         }
1176         // Not found
1177         return 0;
1178 }
1179
1180 // An alternative cf_get_file_list(), fixed array version.
1181 // This one has a 'type', which is a CF_TYPE_* value.  Because this specifies the directory
1182 // location, 'filter' only needs to be the filter itself, with no path information.
1183 // See above descriptions of cf_get_file_list() for more information about how it all works.
1184 int cf_get_file_list_preallocated( int max, char arr[][MAX_FILENAME_LEN], char **list, int pathtype, const char *filter, int sort, file_list_info *info )
1185 {
1186         int i, num_files = 0, own_flag = 0;
1187
1188         if (max < 1) {
1189                 Get_file_list_filter = NULL;
1190                 return 0;
1191         }
1192
1193         if (list) {
1194                 for (i=0; i<max; i++)   {
1195                         list[i] = arr[i];
1196                 }
1197         } else {
1198                 sort = CF_SORT_NONE;  // sorting of array directly not supported.  Sorting done on list only
1199         }
1200
1201         if (!info && (sort == CF_SORT_TIME)) {
1202                 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1203                 own_flag = 1;
1204         }
1205
1206         char filespec[MAX_PATH_LEN];
1207
1208         // Search the default directories
1209 #ifdef PLAT_UNIX
1210         cf_create_default_path_string( filespec, pathtype, NULL );
1211         
1212         DIR *dirp;
1213         struct dirent *dir;
1214
1215         dirp = opendir (filespec);
1216         if ( dirp ) {
1217                 while ((dir = readdir (dirp)) != NULL)
1218                 {
1219                         if (num_files >= max)
1220                                 break;
1221
1222                         if (fnmatch(filter, dir->d_name, 0) != 0)
1223                                 continue;
1224
1225                         char fn[MAX_PATH_LEN];
1226                         SDL_snprintf(fn, MAX_PATH_LEN, "%s/%s", filespec, dir->d_name);
1227
1228                         struct stat buf;
1229                         if (stat(fn, &buf) == -1) {
1230                                 continue;
1231                         }
1232
1233                         if (!S_ISREG(buf.st_mode)) {
1234                                 continue;
1235                         }
1236
1237                         if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1238
1239                                 SDL_strlcpy(arr[num_files], dir->d_name, MAX_FILENAME_LEN);
1240                                 char *ptr = strrchr(arr[num_files], '.');
1241                                 if ( ptr ) {
1242                                         *ptr = 0;
1243                                 }
1244
1245                                 if (info)       {
1246                                         info[num_files].write_time = buf.st_mtime;
1247                                 }
1248
1249                                 num_files++;
1250                         }
1251                 }
1252                 closedir(dirp);
1253         }
1254 #else
1255         cf_create_default_path_string( filespec, pathtype, filter );
1256         
1257         int find_handle;
1258         _finddata_t find;
1259         
1260         find_handle = _findfirst( filespec, &find );
1261         if (find_handle != -1) {
1262                 do {
1263                         if (num_files >= max)
1264                                 break;
1265
1266                         if (!(find.attrib & _A_SUBDIR)) {
1267
1268                                 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1269
1270                                         SDL_strlcpy(arr[num_files], find.name, MAX_FILENAME_LEN);
1271                                         char *ptr = strrchr(arr[num_files], '.');
1272                                         if ( ptr ) {
1273                                                 *ptr = 0;
1274                                         }
1275
1276                                         if (info)       {
1277                                                 info[num_files].write_time = find.time_write;
1278                                         }
1279
1280                                         num_files++;
1281                                 }
1282                         }
1283
1284                 } while (!_findnext(find_handle, &find));
1285
1286                 _findclose( find_handle );
1287         }
1288 #endif
1289                 
1290
1291         // Search all the packfiles and CD.
1292         if ( !Skip_packfile_search )    {
1293                 for (i=0; i<Num_files; i++ )    {
1294                         cf_file * f = cf_get_file(i);
1295
1296                         // only search paths we're supposed to...
1297                         if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index)  )    {
1298                                 continue;
1299                         }
1300
1301                         if (num_files >= max)
1302                                 break;
1303
1304                         if ( !cf_matches_spec( filter,f->name_ext))     {
1305                                 continue;
1306                         }
1307
1308                         if ( cf_file_already_in_list_preallocated( num_files, arr, f->name_ext ))       {
1309                                 continue;
1310                         }
1311
1312                         if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1313
1314                                 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1315
1316                                 SDL_strlcpy(arr[num_files], f->name_ext, MAX_FILENAME_LEN);
1317                                 char *ptr = strrchr(arr[num_files], '.');
1318                                 if ( ptr ) {
1319                                         *ptr = 0;
1320                                 }
1321
1322                                 if (info)       {
1323                                         info[num_files].write_time = f->write_time;
1324                                 }
1325
1326                                 num_files++;
1327                         }
1328
1329                 }
1330         }
1331
1332         if (sort != CF_SORT_NONE) {
1333                 SDL_assert(list);
1334                 cf_sort_filenames( num_files, list, sort, info );
1335         }
1336
1337         if (own_flag)   {
1338                 free(info);
1339         }
1340
1341         Get_file_list_filter = NULL;
1342         return num_files;
1343 }
1344
1345 // Returns the default storage path for files given a 
1346 // particular pathtype.   In other words, the path to 
1347 // the unpacked, non-cd'd, stored on hard drive path.
1348 // If filename isn't null it will also tack the filename
1349 // on the end, creating a completely valid filename.
1350 // Input:   pathtype  - CF_TYPE_??
1351 //          filename  - optional, if set, tacks the filename onto end of path.
1352 // Output:  path      - Fully qualified pathname.
1353 void cf_create_default_path_string( char *path, int pathtype, const char *filename, bool localize )
1354 {
1355 #ifdef PLAT_UNIX
1356         const char *toks = "/";
1357 #else
1358         const char *toks = "/\\:";
1359 #endif
1360
1361         if ( filename && strpbrk(filename, toks) ) {
1362                 // Already has full path
1363                 SDL_strlcpy( path, filename, MAX_PATH_LEN );
1364         } else {
1365                 if ( cfile_init_paths() ) {
1366                         SDL_strlcpy(path, (filename) ? filename : "", MAX_PATH_LEN);
1367                         return;
1368                 }
1369
1370                 SDL_assert(CF_TYPE_SPECIFIED(pathtype));
1371
1372                 SDL_strlcpy(path, Cfile_user_dir, MAX_PATH_LEN);
1373                 SDL_strlcat(path, Pathtypes[pathtype].path, MAX_PATH_LEN);
1374
1375                 // Don't add slash for root directory
1376                 if (Pathtypes[pathtype].path[0] != '\0') {
1377                         SDL_strlcat(path, DIR_SEPARATOR_STR, MAX_PATH_LEN);
1378                 }
1379
1380                 // add filename
1381                 if (filename) {
1382                         SDL_strlcat(path, filename, MAX_PATH_LEN);
1383
1384                         // localize filename
1385                         if (localize) {
1386                                 // create copy of path
1387                                 char temp_path[MAX_PATH_LEN];
1388                                 SDL_strlcpy(temp_path, path, sizeof(temp_path));
1389
1390                                 // localize the path
1391                                 lcl_add_dir_to_path_with_filename(path, MAX_PATH_LEN);
1392
1393                                 // verify localized path
1394                                 FILE *fp = fopen(path, "rb");
1395                                 if (fp) {
1396                                         fclose(fp);
1397                                 } else {
1398                                         SDL_strlcpy(path, temp_path, sizeof(temp_path));
1399                                 }
1400                         }
1401                 }
1402         }
1403 }
1404
1405 // returns true if packfile has been indexed by CFILE (case-insensitive search)
1406 bool cf_has_packfile(const char *fn)
1407 {
1408         cf_root *root;
1409         char vp_name[CF_MAX_PATHNAME_LENGTH];
1410         char *p;
1411         int i;
1412
1413         if (fn == NULL) {
1414                 return false;
1415         }
1416
1417         if ( !SDL_strlen(fn) || (SDL_strlen(fn) < 4) ) {
1418                 return false;
1419         }
1420
1421         // add ".vp" if needed
1422         SDL_strlcpy(vp_name, fn, CF_MAX_PATHNAME_LENGTH);
1423
1424         p = &vp_name[SDL_strlen(vp_name)-3];
1425
1426         if ( SDL_strcasecmp(p, ".vp") ) {
1427                 SDL_strlcat(vp_name, ".vp", CF_MAX_PATHNAME_LENGTH);
1428         }
1429
1430         // now see if we have the packfile
1431         for (i = 0; i < Num_roots; i++) {
1432                 root = cf_get_root(i);
1433
1434                 if (root->roottype != CF_ROOTTYPE_PACK) {
1435                         continue;
1436                 }
1437
1438                 p = SDL_strrchr(root->path, DIR_SEPARATOR_CHAR);
1439
1440                 if (p) {
1441                         p++;
1442
1443                         if ( !SDL_strcasecmp(p, vp_name) ) {
1444                                 return true;
1445                         }
1446                 }
1447         }
1448
1449         return false;
1450 }