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