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