]> icculus.org git repositories - taylor/freespace2.git/blob - src/cfile/cfilesystem.cpp
Filesystem mods, actually reads some data files now
[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.2  2002/05/28 06:28:20  theoddone33
15  * Filesystem mods, actually reads some data files now
16  *
17  * Revision 1.1.1.1  2002/05/03 03:28:08  root
18  * Initial import.
19  *
20  * 
21  * 6     9/08/99 10:01p Dave
22  * Make sure game won't run in a drive's root directory. Make sure
23  * standalone routes suqad war messages properly to the host.
24  * 
25  * 5     9/03/99 1:31a Dave
26  * CD checking by act. Added support to play 2 cutscenes in a row
27  * seamlessly. Fixed super low level cfile bug related to files in the
28  * root directory of a CD. Added cheat code to set campaign mission # in
29  * main hall.
30  * 
31  * 4     2/22/99 10:31p Andsager
32  * Get rid of unneeded includes.
33  * 
34  * 3     10/13/98 9:19a Andsager
35  * Add localization support to cfile.  Optional parameter with cfopen that
36  * looks for localized files.
37  * 
38  * 2     10/07/98 10:52a Dave
39  * Initial checkin.
40  * 
41  * 1     10/07/98 10:48a Dave
42  * 
43  * 14    8/31/98 2:06p Dave
44  * Make cfile sort the ordering or vp files. Added support/checks for
45  * recognizing "mission disk" players.
46  * 
47  * 13    6/23/98 4:18p Hoffoss
48  * Fixed some bugs with AC release build.
49  * 
50  * 12    5/20/98 10:46p John
51  * Added code that doesn't include duplicate filenames in any file list
52  * functions.
53  * 
54  * 11    5/14/98 2:14p Lawrance2
55  * Use filespec filtering for packfiles
56  * 
57  * 10    5/03/98 11:53a John
58  * Fixed filename case mangling.
59  * 
60  * 9     5/02/98 11:06p Allender
61  * correctly deal with pack pathnames
62  * 
63  * 8     5/01/98 11:41a Allender
64  * Fixed bug with mission saving in Fred.
65  * 
66  * 7     5/01/98 10:21a John
67  * Added code to find all pack files in all trees.   Added code to create
68  * any directories that we write to.
69  * 
70  * 6     4/30/98 10:21p John
71  * Added code to cleanup cfilesystem
72  * 
73  * 5     4/30/98 10:18p John
74  * added source safe header
75  *
76  * $NoKeywords: $
77  */
78
79 #include <stdlib.h>
80 #include <string.h>
81 #include <stdio.h>
82 #include <errno.h>
83 #ifndef PLAT_UNIX
84 #include <io.h>
85 #include <direct.h>
86 #include <windows.h>
87 #include <winbase.h>            /* needed for memory mapping of file functions */
88 #else
89 #include <sys/types.h>
90 #include <dirent.h>
91 #include <fnmatch.h>
92 #endif
93
94 #include "pstypes.h"
95 //#include "outwnd.h"
96 //#include "vecmat.h"
97 //#include "timer.h"
98 #include "cfile.h"
99 #include "cfilesystem.h"
100 #include "localize.h"
101
102
103 #define CF_ROOTTYPE_PATH 0
104 #define CF_ROOTTYPE_PACK 1
105
106 //  Created by:
107 //    specifying hard drive tree
108 //    searching for pack files on hard drive            // Found by searching all known paths
109 //    specifying cd-rom tree
110 //    searching for pack files on CD-rom tree
111 typedef struct cf_root {
112         char                            path[CF_MAX_PATHNAME_LENGTH];           // Contains something like c:\projects\freespace or c:\projects\freespace\freespace.vp
113         int                             roottype;                                                               // CF_ROOTTYPE_PATH  = Path, CF_ROOTTYPE_PACK =Pack file
114 } cf_root;
115
116 // convenient type for sorting (see cf_build_pack_list())
117 typedef struct cf_root_sort { 
118         char                            path[CF_MAX_PATHNAME_LENGTH];
119         int                             roottype;
120         int                             cf_type;
121 } cf_root_sort;
122
123 #define CF_NUM_ROOTS_PER_BLOCK   32
124 #define CF_MAX_ROOT_BLOCKS                      256                             // Can store 32*256 = 8192 Roots
125 #define CF_MAX_ROOTS                                    (CF_NUM_ROOTS_PER_BLOCK * CF_MAX_ROOT_BLOCKS)
126
127 typedef struct cf_root_block {
128         cf_root                 roots[CF_NUM_ROOTS_PER_BLOCK];
129 } cf_root_block;
130
131 static int Num_roots = 0;
132 static cf_root_block  *Root_blocks[CF_MAX_ROOT_BLOCKS];
133   
134
135 // Created by searching all roots in order.   This means Files is then sorted by precedence.
136 typedef struct cf_file {
137         char            name_ext[CF_MAX_FILENAME_LENGTH];               // Filename and extension
138         int             root_index;                                                                             // Where in Roots this is located
139         int             pathtype_index;                                                         // Where in Paths this is located
140         time_t  write_time;                                                                             // When it was last written
141         int             size;                                                                                           // How big it is in bytes
142         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.
143 } cf_file;
144
145 #define CF_NUM_FILES_PER_BLOCK   256
146 #define CF_MAX_FILE_BLOCKS                      128                                             // Can store 256*128 = 32768 files
147
148 typedef struct cf_file_block {
149         cf_file                                         files[CF_NUM_FILES_PER_BLOCK];
150 } cf_file_block;
151
152 static int Num_files = 0;
153 static cf_file_block  *File_blocks[CF_MAX_FILE_BLOCKS];
154
155
156 // Return a pointer to to file 'index'.
157 cf_file *cf_get_file(int index)
158 {
159         int block = index / CF_NUM_FILES_PER_BLOCK;
160         int offset = index % CF_NUM_FILES_PER_BLOCK;
161
162         return &File_blocks[block]->files[offset];
163 }
164
165 // Create a new file and return a pointer to it.
166 cf_file *cf_create_file()
167 {
168         int block = Num_files / CF_NUM_FILES_PER_BLOCK;
169         int offset = Num_files % CF_NUM_FILES_PER_BLOCK;
170         
171         if ( File_blocks[block] == NULL )       {
172                 File_blocks[block] = (cf_file_block *)malloc( sizeof(cf_file_block) );
173                 Assert( File_blocks[block] != NULL);
174         }
175
176         Num_files++;
177
178         return &File_blocks[block]->files[offset];
179 }
180
181 extern int cfile_inited;
182
183 // Create a new root and return a pointer to it.  The structure is assumed unitialized.
184 cf_root *cf_get_root(int n)
185 {
186         int block = n / CF_NUM_ROOTS_PER_BLOCK;
187         int offset = n % CF_NUM_ROOTS_PER_BLOCK;
188
189         if (!cfile_inited)
190                 return NULL;
191
192         return &Root_blocks[block]->roots[offset];
193 }
194
195
196 // Create a new root and return a pointer to it.  The structure is assumed unitialized.
197 cf_root *cf_create_root()
198 {
199         int block = Num_roots / CF_NUM_ROOTS_PER_BLOCK;
200         int offset = Num_roots % CF_NUM_ROOTS_PER_BLOCK;
201         
202         if ( Root_blocks[block] == NULL )       {
203                 Root_blocks[block] = (cf_root_block *)malloc( sizeof(cf_root_block) );
204                 Assert(Root_blocks[block] != NULL);
205         }
206
207         Num_roots++;
208
209         return &Root_blocks[block]->roots[offset];
210 }
211
212 // return the # of packfiles which exist
213 int cf_get_packfile_count(cf_root *root)
214 {
215         char filespec[MAX_PATH_LEN];
216         int i;
217         int packfile_count;
218
219         // count up how many packfiles we're gonna have
220         packfile_count = 0;
221         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
222 #ifdef PLAT_UNIX
223                 strcpy( filespec, root->path );
224
225                 if(strlen(Pathtypes[i].path)){
226                         strcat( filespec, Pathtypes[i].path );
227                         strcat( filespec, "/" );
228                 }
229
230                 DIR *dirp;
231                 struct dirent *dir;
232
233                 dirp = opendir (filespec);
234                 if ( dirp ) {
235                         while ((dir = readdir (dirp)) != NULL)
236                         {
237                                 if (!fnmatch ("*.vp", dir->d_name, 0))
238                                         packfile_count++;
239                         }
240                 }
241 #else
242                 strcpy( filespec, root->path );
243
244                 if(strlen(Pathtypes[i].path)){
245                         strcat( filespec, Pathtypes[i].path );
246                         strcat( filespec, "\\" );
247                 }
248
249                 strcat( filespec, "*.vp" );
250
251                 int find_handle;
252                 _finddata_t find;
253                 
254                 find_handle = _findfirst( filespec, &find );
255
256                 if (find_handle != -1) {
257                         do {
258                                 if (!(find.attrib & _A_SUBDIR)) {
259                                         packfile_count++;
260                                 }
261
262                         } while (!_findnext(find_handle, &find));
263
264                         _findclose( find_handle );
265                 }       
266 #endif
267         }       
268
269         return packfile_count;
270 }
271
272 // packfile sort function
273 int cf_packfile_sort_func(const void *elem1, const void *elem2)
274 {
275         cf_root_sort *r1, *r2;
276         r1 = (cf_root_sort*)elem1;
277         r2 = (cf_root_sort*)elem2;
278
279         // if the 2 directory types are the same, do a string compare
280         if(r1->cf_type == r2->cf_type){
281                 return stricmp(r1->path, r2->path);
282         }
283
284         // otherwise return them in order of CF_TYPE_* precedence
285         return (r1->cf_type < r2->cf_type) ? -1 : 1;
286 }
287
288 // Go through a root and look for pack files
289 void cf_build_pack_list( cf_root *root )
290 {
291         char filespec[MAX_PATH_LEN];
292         int i;
293         cf_root_sort *temp_roots_sort, *rptr_sort;
294         int temp_root_count, root_index;
295
296         // determine how many packfiles there are
297         temp_root_count = cf_get_packfile_count(root);
298         if(temp_root_count <= 0){
299                 return;
300         }
301
302         // allocate a temporary array of temporary roots so we can easily sort them
303         temp_roots_sort = (cf_root_sort*)malloc(sizeof(cf_root_sort) * temp_root_count);
304         if(temp_roots_sort == NULL){
305                 Int3();
306                 return;
307         }
308
309         // now just setup all the root info
310         root_index = 0;
311         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
312
313 #ifdef PLAT_UNIX
314                 strcpy( filespec, root->path );
315
316                 if(strlen(Pathtypes[i].path)){
317                         strcat( filespec, Pathtypes[i].path );          
318                         strcat( filespec, "/" );
319                 }
320                 fprintf (stderr, "DDOI: searching |%s|\n", filespec);
321
322                 DIR *dirp;
323                 struct dirent *dir;
324
325                 dirp = opendir (filespec);
326                 if ( dirp ) {
327                         while ((dir = readdir (dirp)) != NULL)
328                         {
329                                 if (!fnmatch ("*.vp", dir->d_name, 0))
330                                 {
331                                         Assert(root_index < temp_root_count);
332
333                                         // get a temp pointer
334                                         rptr_sort = &temp_roots_sort[root_index++];
335
336                                         // fill in all the proper info
337                                         strcpy(rptr_sort->path, root->path);
338
339                                         if(strlen(Pathtypes[i].path)){
340                                                 strcat(rptr_sort->path, Pathtypes[i].path );                                    
341                                                 strcat(rptr_sort->path, "/");
342                                         }
343
344                                         strcat(rptr_sort->path, dir->d_name );
345                                         rptr_sort->roottype = CF_ROOTTYPE_PACK;
346                                         rptr_sort->cf_type = i;
347                                 }
348                         }
349                 }
350 #else
351                 strcpy( filespec, root->path );
352
353                 if(strlen(Pathtypes[i].path)){
354                         strcat( filespec, Pathtypes[i].path );          
355                         strcat( filespec, "\\" );
356                 }
357                 strcat( filespec, "*.vp" );
358                 int find_handle;
359                 _finddata_t find;
360                 
361                 find_handle = _findfirst( filespec, &find );
362
363                 if (find_handle != -1) {
364                         do {
365                                 // add the new item
366                                 if (!(find.attrib & _A_SUBDIR)) {                                       
367                                         Assert(root_index < temp_root_count);
368
369                                         // get a temp pointer
370                                         rptr_sort = &temp_roots_sort[root_index++];
371
372                                         // fill in all the proper info
373                                         strcpy(rptr_sort->path, root->path);
374
375                                         if(strlen(Pathtypes[i].path)){
376                                                 strcat(rptr_sort->path, Pathtypes[i].path );                                    
377                                                 strcat(rptr_sort->path, "\\");
378                                         }
379
380                                         strcat(rptr_sort->path, find.name );
381                                         rptr_sort->roottype = CF_ROOTTYPE_PACK;
382                                         rptr_sort->cf_type = i;
383                                 }
384
385                         } while (!_findnext(find_handle, &find));
386
387                         _findclose( find_handle );
388                 }       
389 #endif
390         }       
391
392         // these should always be the same
393         Assert(root_index == temp_root_count);
394
395         // sort tht roots
396         qsort(temp_roots_sort,  temp_root_count, sizeof(cf_root_sort), cf_packfile_sort_func);
397
398         // now insert them all into the real root list properly
399         cf_root *new_root;
400         for(i=0; i<temp_root_count; i++){               
401                 new_root = cf_create_root();
402                 strcpy( new_root->path, root->path );
403
404                 // mwa -- 4/2/98 put in the next 2 lines because the path name needs to be there
405                 // to find the files.
406                 strcpy(new_root->path, temp_roots_sort[i].path);                
407                 new_root->roottype = CF_ROOTTYPE_PACK;          
408         }
409
410         // free up the temp list
411         free(temp_roots_sort);
412 }
413
414
415 void cf_build_root_list(char *cdrom_dir)
416 {
417         Num_roots = 0;
418
419         cf_root *root;
420
421    //======================================================
422         // First, check the current directory.
423         // strcpy( root->path, "d:\\projects\\freespace\\" );
424
425         root = cf_create_root();
426
427         if ( !_getcwd(root->path, CF_MAX_PATHNAME_LENGTH ) ) {
428                 Error(LOCATION, "Can't get current working directory -- %d", errno );
429         }
430
431         // do we already have a slash? as in the case of a root directory install
432 #ifdef PLAT_UNIX
433         if(strlen(root->path) && (root->path[strlen(root->path)-1] != '/')){
434                 strcat(root->path, "/");                // put trailing backslash on for easier path construction
435 #else
436         if(strlen(root->path) && (root->path[strlen(root->path)-1] != '\\')){
437                 strcat(root->path, "\\");               // put trailing backslash on for easier path construction
438 #endif
439         }
440         root->roottype = CF_ROOTTYPE_PATH;
441
442    //======================================================
443         // Next, check any VP files under the current directory.
444         cf_build_pack_list(root);
445
446
447    //======================================================
448         // Check the real CD if one...
449         if ( cdrom_dir && strlen(cdrom_dir) )   {
450                 root = cf_create_root();
451                 strcpy( root->path, cdrom_dir );
452                 root->roottype = CF_ROOTTYPE_PATH;
453
454                 //======================================================
455                 // Next, check any VP files in the CD-ROM directory.
456                 cf_build_pack_list(root);
457
458         }
459
460 }
461
462 // Given a lower case list of file extensions 
463 // separated by spaces, return zero if ext is
464 // not in the list.
465 int is_ext_in_list( char *ext_list, char *ext )
466 {
467         char tmp_ext[128];
468
469         strncpy( tmp_ext, ext, 127 );
470         strlwr(tmp_ext);
471         if ( strstr(ext_list, tmp_ext ))        {
472                 return 1;
473         }       
474
475         return 0;
476 }
477
478 void cf_search_root_path(int root_index)
479 {
480         int i;
481
482         cf_root *root = cf_get_root(root_index);
483
484         mprintf(( "Searching root '%s'\n", root->path ));
485
486         char search_path[CF_MAX_PATHNAME_LENGTH];
487
488         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
489
490 #ifdef STUB_FUNCTION
491                 DIR *dirp;
492                 struct dirent *dir;
493
494                 strcpy( search_path, root->path );
495
496                 if(strlen(Pathtypes[i].path)){
497                         strcat( search_path, Pathtypes[i].path );
498                         strcat( search_path, "/" );
499                 } 
500
501                 fprintf (stderr, "DDOI: we're searching |%s|\n", search_path);
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