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