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