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