]> icculus.org git repositories - taylor/freespace2.git/blob - src/cfile/cfilesystem.cpp
Initial revision
[taylor/freespace2.git] / src / cfile / cfilesystem.cpp
1 /*
2  * $Logfile: /Freespace2/code/CFile/CfileSystem.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Functions to keep track of and find files that can exist
8  * on the harddrive, cd-rom, or in a pack file on either of those.
9  * This keeps a list of all the files in packfiles or on CD-rom
10  * and when you need a file you call one function which then searches
11  * all those locations, inherently enforcing precedence orders.
12  *
13  * $Log$
14  * Revision 1.1  2002/05/03 03:28:08  root
15  * Initial revision
16  *
17  * 
18  * 6     9/08/99 10:01p Dave
19  * Make sure game won't run in a drive's root directory. Make sure
20  * standalone routes suqad war messages properly to the host.
21  * 
22  * 5     9/03/99 1:31a Dave
23  * CD checking by act. Added support to play 2 cutscenes in a row
24  * seamlessly. Fixed super low level cfile bug related to files in the
25  * root directory of a CD. Added cheat code to set campaign mission # in
26  * main hall.
27  * 
28  * 4     2/22/99 10:31p Andsager
29  * Get rid of unneeded includes.
30  * 
31  * 3     10/13/98 9:19a Andsager
32  * Add localization support to cfile.  Optional parameter with cfopen that
33  * looks for localized files.
34  * 
35  * 2     10/07/98 10:52a Dave
36  * Initial checkin.
37  * 
38  * 1     10/07/98 10:48a Dave
39  * 
40  * 14    8/31/98 2:06p Dave
41  * Make cfile sort the ordering or vp files. Added support/checks for
42  * recognizing "mission disk" players.
43  * 
44  * 13    6/23/98 4:18p Hoffoss
45  * Fixed some bugs with AC release build.
46  * 
47  * 12    5/20/98 10:46p John
48  * Added code that doesn't include duplicate filenames in any file list
49  * functions.
50  * 
51  * 11    5/14/98 2:14p Lawrance2
52  * Use filespec filtering for packfiles
53  * 
54  * 10    5/03/98 11:53a John
55  * Fixed filename case mangling.
56  * 
57  * 9     5/02/98 11:06p Allender
58  * correctly deal with pack pathnames
59  * 
60  * 8     5/01/98 11:41a Allender
61  * Fixed bug with mission saving in Fred.
62  * 
63  * 7     5/01/98 10:21a John
64  * Added code to find all pack files in all trees.   Added code to create
65  * any directories that we write to.
66  * 
67  * 6     4/30/98 10:21p John
68  * Added code to cleanup cfilesystem
69  * 
70  * 5     4/30/98 10:18p John
71  * added source safe header
72  *
73  * $NoKeywords: $
74  */
75
76 #include <stdlib.h>
77 #include <string.h>
78 #include <stdio.h>
79 #include <errno.h>
80 #ifndef PLAT_UNIX
81 #include <io.h>
82 #include <direct.h>
83 #include <windows.h>
84 #include <winbase.h>            /* needed for memory mapping of file functions */
85 #endif
86
87 #include "pstypes.h"
88 //#include "outwnd.h"
89 //#include "vecmat.h"
90 //#include "timer.h"
91 #include "cfile.h"
92 #include "cfilesystem.h"
93 #include "localize.h"
94
95
96 #define CF_ROOTTYPE_PATH 0
97 #define CF_ROOTTYPE_PACK 1
98
99 //  Created by:
100 //    specifying hard drive tree
101 //    searching for pack files on hard drive            // Found by searching all known paths
102 //    specifying cd-rom tree
103 //    searching for pack files on CD-rom tree
104 typedef struct cf_root {
105         char                            path[CF_MAX_PATHNAME_LENGTH];           // Contains something like c:\projects\freespace or c:\projects\freespace\freespace.vp
106         int                             roottype;                                                               // CF_ROOTTYPE_PATH  = Path, CF_ROOTTYPE_PACK =Pack file
107 } cf_root;
108
109 // convenient type for sorting (see cf_build_pack_list())
110 typedef struct cf_root_sort { 
111         char                            path[CF_MAX_PATHNAME_LENGTH];
112         int                             roottype;
113         int                             cf_type;
114 } cf_root_sort;
115
116 #define CF_NUM_ROOTS_PER_BLOCK   32
117 #define CF_MAX_ROOT_BLOCKS                      256                             // Can store 32*256 = 8192 Roots
118 #define CF_MAX_ROOTS                                    (CF_NUM_ROOTS_PER_BLOCK * CF_MAX_ROOT_BLOCKS)
119
120 typedef struct cf_root_block {
121         cf_root                 roots[CF_NUM_ROOTS_PER_BLOCK];
122 } cf_root_block;
123
124 static int Num_roots = 0;
125 static cf_root_block  *Root_blocks[CF_MAX_ROOT_BLOCKS];
126   
127
128 // Created by searching all roots in order.   This means Files is then sorted by precedence.
129 typedef struct cf_file {
130         char            name_ext[CF_MAX_FILENAME_LENGTH];               // Filename and extension
131         int             root_index;                                                                             // Where in Roots this is located
132         int             pathtype_index;                                                         // Where in Paths this is located
133         time_t  write_time;                                                                             // When it was last written
134         int             size;                                                                                           // How big it is in bytes
135         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.
136 } cf_file;
137
138 #define CF_NUM_FILES_PER_BLOCK   256
139 #define CF_MAX_FILE_BLOCKS                      128                                             // Can store 256*128 = 32768 files
140
141 typedef struct cf_file_block {
142         cf_file                                         files[CF_NUM_FILES_PER_BLOCK];
143 } cf_file_block;
144
145 static int Num_files = 0;
146 static cf_file_block  *File_blocks[CF_MAX_FILE_BLOCKS];
147
148
149 // Return a pointer to to file 'index'.
150 cf_file *cf_get_file(int index)
151 {
152         int block = index / CF_NUM_FILES_PER_BLOCK;
153         int offset = index % CF_NUM_FILES_PER_BLOCK;
154
155         return &File_blocks[block]->files[offset];
156 }
157
158 // Create a new file and return a pointer to it.
159 cf_file *cf_create_file()
160 {
161         int block = Num_files / CF_NUM_FILES_PER_BLOCK;
162         int offset = Num_files % CF_NUM_FILES_PER_BLOCK;
163         
164         if ( File_blocks[block] == NULL )       {
165                 File_blocks[block] = (cf_file_block *)malloc( sizeof(cf_file_block) );
166                 Assert( File_blocks[block] != NULL);
167         }
168
169         Num_files++;
170
171         return &File_blocks[block]->files[offset];
172 }
173
174 extern int cfile_inited;
175
176 // Create a new root and return a pointer to it.  The structure is assumed unitialized.
177 cf_root *cf_get_root(int n)
178 {
179         int block = n / CF_NUM_ROOTS_PER_BLOCK;
180         int offset = n % CF_NUM_ROOTS_PER_BLOCK;
181
182         if (!cfile_inited)
183                 return NULL;
184
185         return &Root_blocks[block]->roots[offset];
186 }
187
188
189 // Create a new root and return a pointer to it.  The structure is assumed unitialized.
190 cf_root *cf_create_root()
191 {
192         int block = Num_roots / CF_NUM_ROOTS_PER_BLOCK;
193         int offset = Num_roots % CF_NUM_ROOTS_PER_BLOCK;
194         
195         if ( Root_blocks[block] == NULL )       {
196                 Root_blocks[block] = (cf_root_block *)malloc( sizeof(cf_root_block) );
197                 Assert(Root_blocks[block] != NULL);
198         }
199
200         Num_roots++;
201
202         return &Root_blocks[block]->roots[offset];
203 }
204
205 // return the # of packfiles which exist
206 int cf_get_packfile_count(cf_root *root)
207 {
208         char filespec[MAX_PATH_LEN];
209         int i;
210         int packfile_count;
211
212         // count up how many packfiles we're gonna have
213         packfile_count = 0;
214         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
215                 strcpy( filespec, root->path );
216
217                 if(strlen(Pathtypes[i].path)){
218                         strcat( filespec, Pathtypes[i].path );
219                         strcat( filespec, "\\" );
220                 }
221
222                 strcat( filespec, "*.vp" );
223
224 #ifdef PLAT_UNIX
225                 STUB_FUNCTION;
226 #else
227                 int find_handle;
228                 _finddata_t find;
229                 
230                 find_handle = _findfirst( filespec, &find );
231
232                 if (find_handle != -1) {
233                         do {
234                                 if (!(find.attrib & _A_SUBDIR)) {
235                                         packfile_count++;
236                                 }
237
238                         } while (!_findnext(find_handle, &find));
239
240                         _findclose( find_handle );
241                 }       
242 #endif
243         }       
244
245         return packfile_count;
246 }
247
248 // packfile sort function
249 int cf_packfile_sort_func(const void *elem1, const void *elem2)
250 {
251         cf_root_sort *r1, *r2;
252         r1 = (cf_root_sort*)elem1;
253         r2 = (cf_root_sort*)elem2;
254
255         // if the 2 directory types are the same, do a string compare
256         if(r1->cf_type == r2->cf_type){
257                 return stricmp(r1->path, r2->path);
258         }
259
260         // otherwise return them in order of CF_TYPE_* precedence
261         return (r1->cf_type < r2->cf_type) ? -1 : 1;
262 }
263
264 // Go through a root and look for pack files
265 void cf_build_pack_list( cf_root *root )
266 {
267         char filespec[MAX_PATH_LEN];
268         int i;
269         cf_root_sort *temp_roots_sort, *rptr_sort;
270         int temp_root_count, root_index;
271
272         // determine how many packfiles there are
273         temp_root_count = cf_get_packfile_count(root);
274         if(temp_root_count <= 0){
275                 return;
276         }
277
278         // allocate a temporary array of temporary roots so we can easily sort them
279         temp_roots_sort = (cf_root_sort*)malloc(sizeof(cf_root_sort) * temp_root_count);
280         if(temp_roots_sort == NULL){
281                 Int3();
282                 return;
283         }
284
285         // now just setup all the root info
286         root_index = 0;
287         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
288                 strcpy( filespec, root->path );
289
290                 if(strlen(Pathtypes[i].path)){
291                         strcat( filespec, Pathtypes[i].path );          
292                         strcat( filespec, "\\" );
293                 }
294                 strcat( filespec, "*.vp" );
295
296 #ifdef PLAT_UNIX
297                 STUB_FUNCTION;
298 #else
299                 int find_handle;
300                 _finddata_t find;
301                 
302                 find_handle = _findfirst( filespec, &find );
303
304                 if (find_handle != -1) {
305                         do {
306                                 // add the new item
307                                 if (!(find.attrib & _A_SUBDIR)) {                                       
308                                         Assert(root_index < temp_root_count);
309
310                                         // get a temp pointer
311                                         rptr_sort = &temp_roots_sort[root_index++];
312
313                                         // fill in all the proper info
314                                         strcpy(rptr_sort->path, root->path);
315
316                                         if(strlen(Pathtypes[i].path)){
317                                                 strcat(rptr_sort->path, Pathtypes[i].path );                                    
318                                                 strcat(rptr_sort->path, "\\");
319                                         }
320
321                                         strcat(rptr_sort->path, find.name );
322                                         rptr_sort->roottype = CF_ROOTTYPE_PACK;
323                                         rptr_sort->cf_type = i;
324                                 }
325
326                         } while (!_findnext(find_handle, &find));
327
328                         _findclose( find_handle );
329                 }       
330 #endif
331         }       
332
333         // these should always be the same
334         Assert(root_index == temp_root_count);
335
336         // sort tht roots
337         qsort(temp_roots_sort,  temp_root_count, sizeof(cf_root_sort), cf_packfile_sort_func);
338
339         // now insert them all into the real root list properly
340         cf_root *new_root;
341         for(i=0; i<temp_root_count; i++){               
342                 new_root = cf_create_root();
343                 strcpy( new_root->path, root->path );
344
345                 // mwa -- 4/2/98 put in the next 2 lines because the path name needs to be there
346                 // to find the files.
347                 strcpy(new_root->path, temp_roots_sort[i].path);                
348                 new_root->roottype = CF_ROOTTYPE_PACK;          
349         }
350
351         // free up the temp list
352         free(temp_roots_sort);
353 }
354
355
356 void cf_build_root_list(char *cdrom_dir)
357 {
358         Num_roots = 0;
359
360         cf_root *root;
361
362    //======================================================
363         // First, check the current directory.
364         // strcpy( root->path, "d:\\projects\\freespace\\" );
365
366         root = cf_create_root();
367
368         if ( !_getcwd(root->path, CF_MAX_PATHNAME_LENGTH ) ) {
369                 Error(LOCATION, "Can't get current working directory -- %d", errno );
370         }
371
372         // do we already have a slash? as in the case of a root directory install
373         if(strlen(root->path) && (root->path[strlen(root->path)-1] != '\\')){
374                 strcat(root->path, "\\");               // put trailing backslash on for easier path construction
375         }
376         root->roottype = CF_ROOTTYPE_PATH;
377
378    //======================================================
379         // Next, check any VP files under the current directory.
380         cf_build_pack_list(root);
381
382
383    //======================================================
384         // Check the real CD if one...
385         if ( cdrom_dir && strlen(cdrom_dir) )   {
386                 root = cf_create_root();
387                 strcpy( root->path, cdrom_dir );
388                 root->roottype = CF_ROOTTYPE_PATH;
389
390                 //======================================================
391                 // Next, check any VP files in the CD-ROM directory.
392                 cf_build_pack_list(root);
393
394         }
395
396 }
397
398 // Given a lower case list of file extensions 
399 // separated by spaces, return zero if ext is
400 // not in the list.
401 int is_ext_in_list( char *ext_list, char *ext )
402 {
403         char tmp_ext[128];
404
405         strncpy( tmp_ext, ext, 127 );
406         strlwr(tmp_ext);
407         if ( strstr(ext_list, tmp_ext ))        {
408                 return 1;
409         }       
410
411         return 0;
412 }
413
414 void cf_search_root_path(int root_index)
415 {
416         int i;
417
418         cf_root *root = cf_get_root(root_index);
419
420         mprintf(( "Searching root '%s'\n", root->path ));
421
422         char search_path[CF_MAX_PATHNAME_LENGTH];
423
424         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
425
426                 strcpy( search_path, root->path );
427
428                 if(strlen(Pathtypes[i].path)){
429                         strcat( search_path, Pathtypes[i].path );
430                         strcat( search_path, "\\" );
431                 } 
432
433                 strcat( search_path, "*.*" );
434
435 #ifdef STUB_FUNCTION
436                 STUB_FUNCTION;
437 #else
438                 int find_handle;
439                 _finddata_t find;
440                 
441                 find_handle = _findfirst( search_path, &find );
442
443                 if (find_handle != -1) {
444                         do {
445                                 if (!(find.attrib & _A_SUBDIR)) {
446
447                                         char *ext = strchr( find.name, '.' );
448                                         if ( ext )      {
449                                                 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) )   {
450                                                         // Found a file!!!!
451                                                         cf_file *file = cf_create_file();
452
453                                                         strcpy( file->name_ext, find.name );
454                                                         file->root_index = root_index;
455                                                         file->pathtype_index = i;
456                                                         file->write_time = find.time_write;
457                                                         file->size = find.size;
458                                                         file->pack_offset = 0;                  // Mark as a non-packed file
459
460                                                         //mprintf(( "Found file '%s'\n", file->name_ext ));
461
462                                                 }
463                                         }
464
465                                 }
466
467                         } while (!_findnext(find_handle, &find));
468
469                         _findclose( find_handle );
470                 }
471 #endif
472
473         }
474 }
475
476
477 typedef struct VP_FILE_HEADER {
478         char id[4];
479         int version;
480         int index_offset;
481         int num_files;
482 } VP_FILE_HEADER;
483
484 typedef struct VP_FILE {
485         int     offset;
486         int     size;
487         char    filename[32];
488         time_t write_time;
489 } VP_FILE;
490
491 void cf_search_root_pack(int root_index)
492 {
493         int i;
494
495         cf_root *root = cf_get_root(root_index);
496
497         //mprintf(( "Searching root pack '%s'\n", root->path ));
498
499         // Open data
500                 
501         FILE *fp = fopen( root->path, "rb" );
502         // Read the file header
503         if (!fp) {
504                 return;
505         }
506
507         VP_FILE_HEADER VP_header;
508
509         Assert( sizeof(VP_header) == 16 );
510         fread(&VP_header, 1, sizeof(VP_header), fp);
511
512         // Read index info
513         fseek(fp, VP_header.index_offset, SEEK_SET);
514
515         char search_path[CF_MAX_PATHNAME_LENGTH];
516
517         strcpy( search_path, "" );
518         
519         // Go through all the files
520         for (i=0; i<VP_header.num_files; i++ )  {
521                 VP_FILE find;
522
523                 fread( &find, sizeof(VP_FILE), 1, fp );
524
525                 if ( find.size == 0 )   {
526                         if ( !stricmp( find.filename, ".." ))   {
527                                 int l = strlen(search_path);
528                                 char *p = &search_path[l-1];
529                                 while( (p > search_path) && (*p != '\\') )      {
530                                         p--;
531                                 }
532                                 *p = 0;
533                         } else {
534                                 if ( strlen(search_path)        )       {
535                                         strcat( search_path,    "\\" );
536                                 }
537                                 strcat( search_path, find.filename );
538                         }
539
540                         //mprintf(( "Current dir = '%s'\n", search_path ));
541                 } else {
542         
543                         int j;
544                         for (j=CF_TYPE_ROOT; j<CF_MAX_PATH_TYPES; j++ ) {
545
546                                 if ( !stricmp( search_path, Pathtypes[j].path ))        {
547
548                                         char *ext = strchr( find.filename, '.' );
549                                         if ( ext )      {
550                                                 if ( is_ext_in_list( Pathtypes[j].extensions, ext ) )   {
551                                                         // Found a file!!!!
552                                                         cf_file *file = cf_create_file();
553                                                         
554                                                         strcpy( file->name_ext, find.filename );
555                                                         file->root_index = root_index;
556                                                         file->pathtype_index = j;
557                                                         file->write_time = find.write_time;
558                                                         file->size = find.size;
559                                                         file->pack_offset = find.offset;                        // Mark as a non-packed file
560
561                                                         //mprintf(( "Found pack file '%s'\n", file->name_ext ));
562                                                 }
563                                         }
564                                         
565
566                                 }
567                         }
568
569                 }
570         }
571         fclose(fp);
572 }
573
574
575 void cf_build_file_list()
576 {
577         int i;
578
579         Num_files = 0;
580
581         // For each root, find all files...
582         for (i=1; i<Num_roots; i++ )    {
583                 cf_root *root = cf_get_root(i);
584                 if ( root->roottype == CF_ROOTTYPE_PATH )       {
585                         cf_search_root_path(i);
586                 } else if ( root->roottype == CF_ROOTTYPE_PACK )        {
587                         cf_search_root_pack(i);
588                 }
589         }
590
591 }
592
593
594 void cf_build_secondary_filelist(char *cdrom_dir)
595 {
596         int i;
597
598         // Assume no files
599         Num_roots = 0;
600         Num_files = 0;
601
602         // Init the path types
603         for (i=0; i<CF_MAX_PATH_TYPES; i++ )    {
604                 Assert( Pathtypes[i].index == i );
605                 if ( Pathtypes[i].extensions )  {
606                         strlwr(Pathtypes[i].extensions);
607                 }
608         }
609         
610         // Init the root blocks
611         for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ )   {
612                 Root_blocks[i] = NULL;
613         }
614
615         // Init the file blocks 
616         for (i=0; i<CF_MAX_FILE_BLOCKS; i++ )   {
617                 File_blocks[i] = NULL;
618         }
619
620         mprintf(( "Building file index...\n" ));
621         
622         // build the list of searchable roots
623         cf_build_root_list(cdrom_dir);  
624
625         // build the list of files themselves
626         cf_build_file_list();
627
628         mprintf(( "Found %d roots and %d files.\n", Num_roots, Num_files ));
629 }
630
631 void cf_free_secondary_filelist()
632 {
633         int i;
634
635         // Free the root blocks
636         for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ )   {
637                 if ( Root_blocks[i] )   {
638                         free( Root_blocks[i] );
639                         Root_blocks[i] = NULL;
640                 }
641         }
642         Num_roots = 0;
643
644         // Init the file blocks 
645         for (i=0; i<CF_MAX_FILE_BLOCKS; i++ )   {
646                 if ( File_blocks[i] )   {
647                         free( File_blocks[i] );
648                         File_blocks[i] = NULL;
649                 }
650         }
651         Num_files = 0;
652 }
653
654 // Searches for a file.   Follows all rules and precedence and searches
655 // CD's and pack files.
656 // Input:  filespace   - Filename & extension
657 //         pathtype    - See CF_TYPE_ defines in CFILE.H
658 // Output: pack_filename - Absolute path and filename of this file.   Could be a packfile or the actual file.
659 //         size        - File size
660 //         offset      - Offset into pack file.  0 if not a packfile.
661 // Returns: If not found returns 0.
662 int cf_find_file_location( char *filespec, int pathtype, char *pack_filename, int *size, int *offset, bool localize )
663 {
664         int i;
665
666         Assert(filespec && strlen(filespec));
667
668         // see if we have something other than just a filename
669         // our current rules say that any file that specifies a direct
670         // path will try to be opened on that path.  If that open
671         // fails, then we will open the file based on the extension
672         // of the file
673
674         // NOTE: full path should also include localization, if so desired
675         if ( strpbrk(filespec,"/\\:")  ) {              // do we have a full path already?
676                 FILE *fp = fopen(filespec, "rb" );
677                 if (fp) {
678                         if ( size ) *size = filelength(fileno(fp));
679                         if ( offset ) *offset = 0;
680                         if ( pack_filename ) {
681                                 strcpy( pack_filename, filespec );
682                         }                               
683                         fclose(fp);
684                         return 1;               
685                 }
686
687                 return 0;               // If they give a full path, fail if not found.
688         }
689
690         // Search the hard drive for files first.
691         int num_search_dirs = 0;
692         int search_order[CF_MAX_PATH_TYPES];
693
694         if ( CF_TYPE_SPECIFIED(pathtype) )      {
695                 search_order[num_search_dirs++] = pathtype;
696         }
697
698         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++)  {
699                 if ( i != pathtype )    {
700                         search_order[num_search_dirs++] = i;
701                 }
702         }
703
704         for (i=0; i<num_search_dirs; i++ )      {
705                 char longname[MAX_PATH_LEN];
706
707                 cf_create_default_path_string( longname, search_order[i], filespec, localize );
708
709                 FILE *fp = fopen(longname, "rb" );
710                 if (fp) {
711                         if ( size ) *size = filelength(fileno(fp));
712                         if ( offset ) *offset = 0;
713                         if ( pack_filename ) {
714                                 strcpy( pack_filename, longname );
715                         }                               
716                         fclose(fp);
717                         return 1;               
718                 }
719         } 
720
721         // Search the pak files and CD-ROM.
722
723                 for (i=0; i<Num_files; i++ )    {
724                         cf_file * f = cf_get_file(i);
725
726                         // only search paths we're supposed to...
727                         if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index)  )    {
728                                 continue;
729                         }
730
731                         if (localize) {
732                                 // create localized filespec
733                                 char temp[MAX_PATH_LEN];
734                                 strcpy(temp, filespec);
735                                 lcl_add_dir_to_path_with_filename(filespec);
736                         
737                                 if ( !stricmp(filespec, f->name_ext) )  {
738                                         if ( size ) *size = f->size;
739                                         if ( offset ) *offset = f->pack_offset;
740                                         if ( pack_filename ) {
741                                                 cf_root * r = cf_get_root(f->root_index);
742
743                                                 strcpy( pack_filename, r->path );
744                                                 if ( f->pack_offset < 1 )       {
745                                                         strcat( pack_filename, Pathtypes[f->pathtype_index].path );
746                                                         strcat( pack_filename, "\\" );
747                                                         strcat( pack_filename, f->name_ext );
748                                                 }
749                                         }                               
750                                         return 1;               
751                                 }
752                                 // restore original filespec
753                                 strcpy(filespec, temp);
754                         }
755
756                         // file either not localized or localized version not found
757                         if ( !stricmp(filespec, f->name_ext) )  {
758                                 if ( size ) *size = f->size;
759                                 if ( offset ) *offset = f->pack_offset;
760                                 if ( pack_filename ) {
761                                         cf_root * r = cf_get_root(f->root_index);
762
763                                         strcpy( pack_filename, r->path );
764                                         if ( f->pack_offset < 1 )       {
765
766                                                 if(strlen(Pathtypes[f->pathtype_index].path)){
767                                                         strcat( pack_filename, Pathtypes[f->pathtype_index].path );
768                                                         strcat( pack_filename, "\\" );
769                                                 }
770
771                                                 strcat( pack_filename, f->name_ext );
772                                         }
773                                 }                               
774                                 return 1;               
775                         }
776                 }
777         
778         return 0;
779 }
780
781
782 // Returns true if filename matches filespec, else zero if not
783 int cf_matches_spec(char *filespec, char *filename)
784 {
785         char *src_ext, *dst_ext;
786
787         src_ext = strchr(filespec, '.');
788         if (!src_ext)
789                 return 1;
790         if (*src_ext == '*')
791                 return 1;
792
793         dst_ext = strchr(filename, '.');
794         if (!dst_ext)
795                 return 1;
796         
797         return !stricmp(dst_ext, src_ext);
798 }
799
800 int (*Get_file_list_filter)(char *filename) = NULL;
801 int Skip_packfile_search = 0;
802
803 int cf_file_already_in_list( int num_files, char **list, char *filename )
804 {
805         int i;
806
807         char name_no_extension[MAX_PATH_LEN];
808
809         strcpy(name_no_extension, filename );
810         char *p = strchr( name_no_extension, '.' );
811         if ( p ) *p = 0;
812
813         for (i=0; i<num_files; i++ )    {
814                 if ( !stricmp(list[i], name_no_extension ) )    {
815                         // Match found!
816                         return 1;
817                 }
818         }
819         // Not found
820         return 0;
821 }
822
823 // An alternative cf_get_file_list(), dynamic list version.
824 // This one has a 'type', which is a CF_TYPE_* value.  Because this specifies the directory
825 // location, 'filter' only needs to be the filter itself, with no path information.
826 // See above descriptions of cf_get_file_list() for more information about how it all works.
827 int cf_get_file_list( int max, char **list, int pathtype, char *filter, int sort, file_list_info *info )
828 {
829         char *ptr;
830         int i, l, find_handle, num_files = 0, own_flag = 0;
831 #ifndef PLAT_UNIX
832         _finddata_t find;
833 #endif
834
835         if (max < 1) {
836                 Get_file_list_filter = NULL;
837                 return 0;
838         }
839
840         Assert(list);
841
842         if (!info && (sort == CF_SORT_TIME)) {
843                 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
844                 own_flag = 1;
845         }
846
847         char filespec[MAX_PATH_LEN];
848
849         cf_create_default_path_string( filespec, pathtype, filter );
850
851 #ifdef PLAT_UNIX
852         STUB_FUNCTION;
853 #else
854         find_handle = _findfirst( filespec, &find );
855         if (find_handle != -1) {
856                 do {
857                         if (num_files >= max)
858                                 break;
859
860                         if (!(find.attrib & _A_SUBDIR)) {
861                                 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
862                                         ptr = strrchr(find.name, '.');
863                                         if (ptr)
864                                                 l = ptr - find.name;
865                                         else
866                                                 l = strlen(find.name);
867
868                                         list[num_files] = (char *)malloc(l + 1);
869                                         strncpy(list[num_files], find.name, l);
870                                         list[num_files][l] = 0;
871                                         if (info)
872                                                 info[num_files].write_time = find.time_write;
873
874                                         num_files++;
875                                 }
876                         }
877
878                 } while (!_findnext(find_handle, &find));
879
880                 _findclose( find_handle );
881         }
882 #endif
883
884
885         // Search all the packfiles and CD.
886         if ( !Skip_packfile_search )    {
887                 for (i=0; i<Num_files; i++ )    {
888                         cf_file * f = cf_get_file(i);
889
890                         // only search paths we're supposed to...
891                         if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index)  )    {
892                                 continue;
893                         }
894
895                         if (num_files >= max)
896                                 break;
897
898                         if ( !cf_matches_spec( filter,f->name_ext))     {
899                                 continue;
900                         }
901
902                         if ( cf_file_already_in_list(num_files,list,f->name_ext))       {
903                                 continue;
904                         }
905
906                         if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
907
908                                 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
909
910                                         ptr = strrchr(f->name_ext, '.');
911                                         if (ptr)
912                                                 l = ptr - f->name_ext;
913                                         else
914                                                 l = strlen(f->name_ext);
915
916                                         list[num_files] = (char *)malloc(l + 1);
917                                         strncpy(list[num_files], f->name_ext, l);
918                                         list[num_files][l] = 0;
919
920                                 if (info)       {
921                                         info[num_files].write_time = f->write_time;
922                                 }
923
924                                 num_files++;
925                         }
926
927                 }
928         }
929
930
931         if (sort != CF_SORT_NONE)       {
932                 cf_sort_filenames( num_files, list, sort, info );
933         }
934
935         if (own_flag)   {
936                 free(info);
937         }
938
939         Get_file_list_filter = NULL;
940         return num_files;
941 }
942
943 int cf_file_already_in_list_preallocated( int num_files, char arr[][MAX_FILENAME_LEN], char *filename )
944 {
945         int i;
946
947         char name_no_extension[MAX_PATH_LEN];
948
949         strcpy(name_no_extension, filename );
950         char *p = strchr( name_no_extension, '.' );
951         if ( p ) *p = 0;
952
953         for (i=0; i<num_files; i++ )    {
954                 if ( !stricmp(arr[i], name_no_extension ) )     {
955                         // Match found!
956                         return 1;
957                 }
958         }
959         // Not found
960         return 0;
961 }
962
963 // An alternative cf_get_file_list(), fixed array version.
964 // This one has a 'type', which is a CF_TYPE_* value.  Because this specifies the directory
965 // location, 'filter' only needs to be the filter itself, with no path information.
966 // See above descriptions of cf_get_file_list() for more information about how it all works.
967 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 )
968 {
969         int i, num_files = 0, own_flag = 0;
970
971         if (max < 1) {
972                 Get_file_list_filter = NULL;
973                 return 0;
974         }
975
976         if (list) {
977                 for (i=0; i<max; i++)   {
978                         list[i] = arr[i];
979                 }
980         } else {
981                 sort = CF_SORT_NONE;  // sorting of array directly not supported.  Sorting done on list only
982         }
983
984         if (!info && (sort == CF_SORT_TIME)) {
985                 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
986                 own_flag = 1;
987         }
988
989         char filespec[MAX_PATH_LEN];
990
991         cf_create_default_path_string( filespec, pathtype, filter );
992
993         // Search the default directories
994 #ifdef PLAT_UNIX
995         STUB_FUNCTION;
996 #else
997         int find_handle;
998         _finddata_t find;
999         
1000         find_handle = _findfirst( filespec, &find );
1001         if (find_handle != -1) {
1002                 do {
1003                         if (num_files >= max)
1004                                 break;
1005
1006                         if (!(find.attrib & _A_SUBDIR)) {
1007
1008                                 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1009
1010                                         strncpy(arr[num_files], find.name, MAX_FILENAME_LEN - 1 );
1011                                         char *ptr = strrchr(arr[num_files], '.');
1012                                         if ( ptr ) {
1013                                                 *ptr = 0;
1014                                         }
1015
1016                                         if (info)       {
1017                                                 info[num_files].write_time = find.time_write;
1018                                         }
1019
1020                                         num_files++;
1021                                 }
1022                         }
1023
1024                 } while (!_findnext(find_handle, &find));
1025
1026                 _findclose( find_handle );
1027         }
1028 #endif
1029                 
1030
1031         // Search all the packfiles and CD.
1032         if ( !Skip_packfile_search )    {
1033                 for (i=0; i<Num_files; i++ )    {
1034                         cf_file * f = cf_get_file(i);
1035
1036                         // only search paths we're supposed to...
1037                         if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index)  )    {
1038                                 continue;
1039                         }
1040
1041                         if (num_files >= max)
1042                                 break;
1043
1044                         if ( !cf_matches_spec( filter,f->name_ext))     {
1045                                 continue;
1046                         }
1047
1048                         if ( cf_file_already_in_list_preallocated( num_files, arr, f->name_ext ))       {
1049                                 continue;
1050                         }
1051
1052                         if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1053
1054                                 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1055
1056                                 strncpy(arr[num_files], f->name_ext, MAX_FILENAME_LEN - 1 );
1057                                 char *ptr = strrchr(arr[num_files], '.');
1058                                 if ( ptr ) {
1059                                         *ptr = 0;
1060                                 }
1061
1062                                 if (info)       {
1063                                         info[num_files].write_time = f->write_time;
1064                                 }
1065
1066                                 num_files++;
1067                         }
1068
1069                 }
1070         }
1071
1072         if (sort != CF_SORT_NONE) {
1073                 Assert(list);
1074                 cf_sort_filenames( num_files, list, sort, info );
1075         }
1076
1077         if (own_flag)   {
1078                 free(info);
1079         }
1080
1081         Get_file_list_filter = NULL;
1082         return num_files;
1083 }
1084
1085 // Returns the default storage path for files given a 
1086 // particular pathtype.   In other words, the path to 
1087 // the unpacked, non-cd'd, stored on hard drive path.
1088 // If filename isn't null it will also tack the filename
1089 // on the end, creating a completely valid filename.
1090 // Input:   pathtype  - CF_TYPE_??
1091 //          filename  - optional, if set, tacks the filename onto end of path.
1092 // Output:  path      - Fully qualified pathname.
1093 void cf_create_default_path_string( char *path, int pathtype, char *filename, bool localize )
1094 {
1095         if ( filename && strpbrk(filename,"/\\:")  ) {  
1096                 // Already has full path
1097                 strcpy( path, filename );
1098
1099         } else {
1100                 cf_root *root = cf_get_root(0);
1101
1102                 if (!root) {
1103                         strcpy(path, filename);
1104                         return;
1105                 }
1106
1107                 Assert(CF_TYPE_SPECIFIED(pathtype));
1108
1109                 strcpy(path, root->path);
1110                 strcat(path, Pathtypes[pathtype].path);
1111
1112                 // Don't add slash for root directory
1113                 if (Pathtypes[pathtype].path[0] != '\0') {
1114                         strcat(path, "\\");
1115                 }
1116
1117                 // add filename
1118                 if (filename) {
1119                         strcat(path, filename);
1120
1121                         // localize filename
1122                         if (localize) {
1123                                 // create copy of path
1124                                 char temp_path[MAX_PATH_LEN];
1125                                 strcpy(temp_path, path);
1126
1127                                 // localize the path
1128                                 lcl_add_dir_to_path_with_filename(path);
1129
1130                                 // verify localized path
1131                                 FILE *fp = fopen(path, "rb");
1132                                 if (fp) {
1133                                         fclose(fp);
1134                                 } else {
1135                                         strcpy(path, temp_path);
1136                                 }
1137                         }
1138                 }
1139         }
1140 }
1141