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