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