2 * $Logfile: /Freespace2/code/CFile/CfileSystem.cpp $
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.
14 * Revision 1.5 2002/06/05 04:03:32 relnev
15 * finished cfilesystem.
17 * removed some old code.
19 * fixed mouse save off-by-one.
23 * Revision 1.4 2002/05/28 17:26:57 theoddone33
24 * Fill in some timer and palette setting stubs. Still no display
26 * Revision 1.3 2002/05/28 06:45:38 theoddone33
29 * Revision 1.2 2002/05/28 06:28:20 theoddone33
30 * Filesystem mods, actually reads some data files now
32 * Revision 1.1.1.1 2002/05/03 03:28:08 root
36 * 6 9/08/99 10:01p Dave
37 * Make sure game won't run in a drive's root directory. Make sure
38 * standalone routes suqad war messages properly to the host.
40 * 5 9/03/99 1:31a Dave
41 * CD checking by act. Added support to play 2 cutscenes in a row
42 * seamlessly. Fixed super low level cfile bug related to files in the
43 * root directory of a CD. Added cheat code to set campaign mission # in
46 * 4 2/22/99 10:31p Andsager
47 * Get rid of unneeded includes.
49 * 3 10/13/98 9:19a Andsager
50 * Add localization support to cfile. Optional parameter with cfopen that
51 * looks for localized files.
53 * 2 10/07/98 10:52a Dave
56 * 1 10/07/98 10:48a Dave
58 * 14 8/31/98 2:06p Dave
59 * Make cfile sort the ordering or vp files. Added support/checks for
60 * recognizing "mission disk" players.
62 * 13 6/23/98 4:18p Hoffoss
63 * Fixed some bugs with AC release build.
65 * 12 5/20/98 10:46p John
66 * Added code that doesn't include duplicate filenames in any file list
69 * 11 5/14/98 2:14p Lawrance2
70 * Use filespec filtering for packfiles
72 * 10 5/03/98 11:53a John
73 * Fixed filename case mangling.
75 * 9 5/02/98 11:06p Allender
76 * correctly deal with pack pathnames
78 * 8 5/01/98 11:41a Allender
79 * Fixed bug with mission saving in Fred.
81 * 7 5/01/98 10:21a John
82 * Added code to find all pack files in all trees. Added code to create
83 * any directories that we write to.
85 * 6 4/30/98 10:21p John
86 * Added code to cleanup cfilesystem
88 * 5 4/30/98 10:18p John
89 * added source safe header
102 #include <winbase.h> /* needed for memory mapping of file functions */
104 #include <sys/types.h>
107 #include <sys/stat.h>
112 //#include "outwnd.h"
113 //#include "vecmat.h"
116 #include "cfilesystem.h"
117 #include "localize.h"
120 #define CF_ROOTTYPE_PATH 0
121 #define CF_ROOTTYPE_PACK 1
124 // specifying hard drive tree
125 // searching for pack files on hard drive // Found by searching all known paths
126 // specifying cd-rom tree
127 // searching for pack files on CD-rom tree
128 typedef struct cf_root {
129 char path[CF_MAX_PATHNAME_LENGTH]; // Contains something like c:\projects\freespace or c:\projects\freespace\freespace.vp
130 int roottype; // CF_ROOTTYPE_PATH = Path, CF_ROOTTYPE_PACK =Pack file
133 // convenient type for sorting (see cf_build_pack_list())
134 typedef struct cf_root_sort {
135 char path[CF_MAX_PATHNAME_LENGTH];
140 #define CF_NUM_ROOTS_PER_BLOCK 32
141 #define CF_MAX_ROOT_BLOCKS 256 // Can store 32*256 = 8192 Roots
142 #define CF_MAX_ROOTS (CF_NUM_ROOTS_PER_BLOCK * CF_MAX_ROOT_BLOCKS)
144 typedef struct cf_root_block {
145 cf_root roots[CF_NUM_ROOTS_PER_BLOCK];
148 static int Num_roots = 0;
149 static cf_root_block *Root_blocks[CF_MAX_ROOT_BLOCKS];
152 // Created by searching all roots in order. This means Files is then sorted by precedence.
153 typedef struct cf_file {
154 char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension
155 int root_index; // Where in Roots this is located
156 int pathtype_index; // Where in Paths this is located
157 time_t write_time; // When it was last written
158 int size; // How big it is in bytes
159 int pack_offset; // For pack files, where it is at. 0 if not in a pack file. This can be used to tell if in a pack file.
162 #define CF_NUM_FILES_PER_BLOCK 256
163 #define CF_MAX_FILE_BLOCKS 128 // Can store 256*128 = 32768 files
165 typedef struct cf_file_block {
166 cf_file files[CF_NUM_FILES_PER_BLOCK];
169 static int Num_files = 0;
170 static cf_file_block *File_blocks[CF_MAX_FILE_BLOCKS];
173 // Return a pointer to to file 'index'.
174 cf_file *cf_get_file(int index)
176 int block = index / CF_NUM_FILES_PER_BLOCK;
177 int offset = index % CF_NUM_FILES_PER_BLOCK;
179 return &File_blocks[block]->files[offset];
182 // Create a new file and return a pointer to it.
183 cf_file *cf_create_file()
185 int block = Num_files / CF_NUM_FILES_PER_BLOCK;
186 int offset = Num_files % CF_NUM_FILES_PER_BLOCK;
188 if ( File_blocks[block] == NULL ) {
189 File_blocks[block] = (cf_file_block *)malloc( sizeof(cf_file_block) );
190 Assert( File_blocks[block] != NULL);
195 return &File_blocks[block]->files[offset];
198 extern int cfile_inited;
200 // Create a new root and return a pointer to it. The structure is assumed unitialized.
201 cf_root *cf_get_root(int n)
203 int block = n / CF_NUM_ROOTS_PER_BLOCK;
204 int offset = n % CF_NUM_ROOTS_PER_BLOCK;
209 return &Root_blocks[block]->roots[offset];
213 // Create a new root and return a pointer to it. The structure is assumed unitialized.
214 cf_root *cf_create_root()
216 int block = Num_roots / CF_NUM_ROOTS_PER_BLOCK;
217 int offset = Num_roots % CF_NUM_ROOTS_PER_BLOCK;
219 if ( Root_blocks[block] == NULL ) {
220 Root_blocks[block] = (cf_root_block *)malloc( sizeof(cf_root_block) );
221 Assert(Root_blocks[block] != NULL);
226 return &Root_blocks[block]->roots[offset];
229 // return the # of packfiles which exist
230 int cf_get_packfile_count(cf_root *root)
232 char filespec[MAX_PATH_LEN];
236 // count up how many packfiles we're gonna have
238 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
240 strcpy( filespec, root->path );
242 if(strlen(Pathtypes[i].path)){
243 strcat( filespec, Pathtypes[i].path );
244 strcat( filespec, "/" );
250 dirp = opendir (filespec);
252 while ((dir = readdir (dirp)) != NULL)
254 if (!fnmatch ("*.vp", dir->d_name, 0))
260 strcpy( filespec, root->path );
262 if(strlen(Pathtypes[i].path)){
263 strcat( filespec, Pathtypes[i].path );
264 strcat( filespec, "\\" );
267 strcat( filespec, "*.vp" );
272 find_handle = _findfirst( filespec, &find );
274 if (find_handle != -1) {
276 if (!(find.attrib & _A_SUBDIR)) {
280 } while (!_findnext(find_handle, &find));
282 _findclose( find_handle );
287 return packfile_count;
290 // packfile sort function
291 int cf_packfile_sort_func(const void *elem1, const void *elem2)
293 cf_root_sort *r1, *r2;
294 r1 = (cf_root_sort*)elem1;
295 r2 = (cf_root_sort*)elem2;
297 // if the 2 directory types are the same, do a string compare
298 if(r1->cf_type == r2->cf_type){
299 return stricmp(r1->path, r2->path);
302 // otherwise return them in order of CF_TYPE_* precedence
303 return (r1->cf_type < r2->cf_type) ? -1 : 1;
306 // Go through a root and look for pack files
307 void cf_build_pack_list( cf_root *root )
309 char filespec[MAX_PATH_LEN];
311 cf_root_sort *temp_roots_sort, *rptr_sort;
312 int temp_root_count, root_index;
314 // determine how many packfiles there are
315 temp_root_count = cf_get_packfile_count(root);
316 if(temp_root_count <= 0){
320 // allocate a temporary array of temporary roots so we can easily sort them
321 temp_roots_sort = (cf_root_sort*)malloc(sizeof(cf_root_sort) * temp_root_count);
322 if(temp_roots_sort == NULL){
327 // now just setup all the root info
329 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
332 strcpy( filespec, root->path );
334 if(strlen(Pathtypes[i].path)){
335 strcat( filespec, Pathtypes[i].path );
336 strcat( filespec, "/" );
342 dirp = opendir (filespec);
344 while ((dir = readdir (dirp)) != NULL)
346 if (!fnmatch ("*.vp", dir->d_name, 0))
348 Assert(root_index < temp_root_count);
351 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
355 if (stat(fn, &buf) == -1) {
359 if (!S_ISREG(buf.st_mode)) {
363 // get a temp pointer
364 rptr_sort = &temp_roots_sort[root_index++];
366 // fill in all the proper info
367 strcpy(rptr_sort->path, root->path);
369 if(strlen(Pathtypes[i].path)){
370 strcat(rptr_sort->path, Pathtypes[i].path );
371 strcat(rptr_sort->path, "/");
374 strcat(rptr_sort->path, dir->d_name );
375 rptr_sort->roottype = CF_ROOTTYPE_PACK;
376 rptr_sort->cf_type = i;
382 strcpy( filespec, root->path );
384 if(strlen(Pathtypes[i].path)){
385 strcat( filespec, Pathtypes[i].path );
386 strcat( filespec, "\\" );
388 strcat( filespec, "*.vp" );
392 find_handle = _findfirst( filespec, &find );
394 if (find_handle != -1) {
397 if (!(find.attrib & _A_SUBDIR)) {
398 Assert(root_index < temp_root_count);
400 // get a temp pointer
401 rptr_sort = &temp_roots_sort[root_index++];
403 // fill in all the proper info
404 strcpy(rptr_sort->path, root->path);
406 if(strlen(Pathtypes[i].path)){
407 strcat(rptr_sort->path, Pathtypes[i].path );
408 strcat(rptr_sort->path, "\\");
411 strcat(rptr_sort->path, find.name );
412 rptr_sort->roottype = CF_ROOTTYPE_PACK;
413 rptr_sort->cf_type = i;
416 } while (!_findnext(find_handle, &find));
418 _findclose( find_handle );
423 // these should always be the same
424 Assert(root_index == temp_root_count);
427 qsort(temp_roots_sort, temp_root_count, sizeof(cf_root_sort), cf_packfile_sort_func);
429 // now insert them all into the real root list properly
431 for(i=0; i<temp_root_count; i++){
432 new_root = cf_create_root();
433 strcpy( new_root->path, root->path );
435 // mwa -- 4/2/98 put in the next 2 lines because the path name needs to be there
436 // to find the files.
437 strcpy(new_root->path, temp_roots_sort[i].path);
438 new_root->roottype = CF_ROOTTYPE_PACK;
441 // free up the temp list
442 free(temp_roots_sort);
446 void cf_build_root_list(char *cdrom_dir)
452 //======================================================
453 // First, check the current directory.
454 // strcpy( root->path, "d:\\projects\\freespace\\" );
456 root = cf_create_root();
458 if ( !_getcwd(root->path, CF_MAX_PATHNAME_LENGTH ) ) {
459 Error(LOCATION, "Can't get current working directory -- %d", errno );
462 // do we already have a slash? as in the case of a root directory install
464 if(strlen(root->path) && (root->path[strlen(root->path)-1] != '/')){
465 strcat(root->path, "/"); // put trailing backslash on for easier path construction
467 if(strlen(root->path) && (root->path[strlen(root->path)-1] != '\\')){
468 strcat(root->path, "\\"); // put trailing backslash on for easier path construction
471 root->roottype = CF_ROOTTYPE_PATH;
473 //======================================================
474 // Next, check any VP files under the current directory.
475 cf_build_pack_list(root);
478 //======================================================
479 // Check the real CD if one...
480 if ( cdrom_dir && strlen(cdrom_dir) ) {
481 root = cf_create_root();
482 strcpy( root->path, cdrom_dir );
483 root->roottype = CF_ROOTTYPE_PATH;
485 //======================================================
486 // Next, check any VP files in the CD-ROM directory.
487 cf_build_pack_list(root);
493 // Given a lower case list of file extensions
494 // separated by spaces, return zero if ext is
496 int is_ext_in_list( char *ext_list, char *ext )
500 strncpy( tmp_ext, ext, 127 );
502 if ( strstr(ext_list, tmp_ext )) {
509 void cf_search_root_path(int root_index)
513 cf_root *root = cf_get_root(root_index);
515 mprintf(( "Searching root '%s'\n", root->path ));
517 char search_path[CF_MAX_PATHNAME_LENGTH];
519 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
525 strcpy( search_path, root->path );
527 if(strlen(Pathtypes[i].path)){
528 strcat( search_path, Pathtypes[i].path );
529 strcat( search_path, "/" );
532 dirp = opendir (search_path);
534 while ((dir = readdir (dirp)) != NULL)
536 if (!fnmatch ("*.*", dir->d_name, 0))
539 snprintf(fn, MAX_PATH-1, "%s/%s", search_path, dir->d_name);
543 if (stat(fn, &buf) == -1) {
547 if (!S_ISREG(buf.st_mode)) {
551 char *ext = strchr( dir->d_name, '.' );
553 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) {
555 cf_file *file = cf_create_file();
557 strcpy( file->name_ext, dir->d_name );
558 file->root_index = root_index;
559 file->pathtype_index = i;
562 file->write_time = buf.st_mtime;
563 file->size = buf.st_size;
565 file->pack_offset = 0; // Mark as a non-packed file
567 //mprintf(( "Found file '%s'\n", file->name_ext ));
575 strcpy( search_path, root->path );
577 if(strlen(Pathtypes[i].path)){
578 strcat( search_path, Pathtypes[i].path );
579 strcat( search_path, "\\" );
582 strcat( search_path, "*.*" );
587 find_handle = _findfirst( search_path, &find );
589 if (find_handle != -1) {
591 if (!(find.attrib & _A_SUBDIR)) {
593 char *ext = strchr( find.name, '.' );
595 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) {
597 cf_file *file = cf_create_file();
599 strcpy( file->name_ext, find.name );
600 file->root_index = root_index;
601 file->pathtype_index = i;
602 file->write_time = find.time_write;
603 file->size = find.size;
604 file->pack_offset = 0; // Mark as a non-packed file
606 //mprintf(( "Found file '%s'\n", file->name_ext ));
613 } while (!_findnext(find_handle, &find));
615 _findclose( find_handle );
623 typedef struct VP_FILE_HEADER {
630 typedef struct VP_FILE {
637 void cf_search_root_pack(int root_index)
641 cf_root *root = cf_get_root(root_index);
643 //mprintf(( "Searching root pack '%s'\n", root->path ));
647 FILE *fp = fopen( root->path, "rb" );
648 // Read the file header
653 VP_FILE_HEADER VP_header;
655 Assert( sizeof(VP_header) == 16 );
656 fread(&VP_header, 1, sizeof(VP_header), fp);
659 fseek(fp, VP_header.index_offset, SEEK_SET);
661 char search_path[CF_MAX_PATHNAME_LENGTH];
663 strcpy( search_path, "" );
665 // Go through all the files
666 for (i=0; i<VP_header.num_files; i++ ) {
669 fread( &find, sizeof(VP_FILE), 1, fp );
671 if ( find.size == 0 ) {
672 if ( !stricmp( find.filename, ".." )) {
673 int l = strlen(search_path);
674 char *p = &search_path[l-1];
676 while( (p > search_path) && (*p != '/') ) {
678 while( (p > search_path) && (*p != '\\') ) {
684 if ( strlen(search_path) ) {
686 strcat( search_path, "/" );
688 strcat( search_path, "\\" );
691 strcat( search_path, find.filename );
694 //mprintf(( "Current dir = '%s'\n", search_path ));
698 for (j=CF_TYPE_ROOT; j<CF_MAX_PATH_TYPES; j++ ) {
700 if ( !stricmp( search_path, Pathtypes[j].path )) {
702 char *ext = strchr( find.filename, '.' );
704 if ( is_ext_in_list( Pathtypes[j].extensions, ext ) ) {
706 cf_file *file = cf_create_file();
708 strcpy( file->name_ext, find.filename );
709 file->root_index = root_index;
710 file->pathtype_index = j;
711 file->write_time = find.write_time;
712 file->size = find.size;
713 file->pack_offset = find.offset; // Mark as a non-packed file
715 //mprintf(( "Found pack file '%s'\n", file->name_ext ));
729 void cf_build_file_list()
735 // For each root, find all files...
736 for (i=1; i<Num_roots; i++ ) {
737 cf_root *root = cf_get_root(i);
738 if ( root->roottype == CF_ROOTTYPE_PATH ) {
739 cf_search_root_path(i);
740 } else if ( root->roottype == CF_ROOTTYPE_PACK ) {
741 cf_search_root_pack(i);
748 void cf_build_secondary_filelist(char *cdrom_dir)
756 // Init the path types
757 for (i=0; i<CF_MAX_PATH_TYPES; i++ ) {
758 Assert( Pathtypes[i].index == i );
759 if ( Pathtypes[i].extensions ) {
760 strlwr(Pathtypes[i].extensions);
764 // Init the root blocks
765 for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ ) {
766 Root_blocks[i] = NULL;
769 // Init the file blocks
770 for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
771 File_blocks[i] = NULL;
774 mprintf(( "Building file index...\n" ));
776 // build the list of searchable roots
777 cf_build_root_list(cdrom_dir);
779 // build the list of files themselves
780 cf_build_file_list();
782 mprintf(( "Found %d roots and %d files.\n", Num_roots, Num_files ));
785 void cf_free_secondary_filelist()
789 // Free the root blocks
790 for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ ) {
791 if ( Root_blocks[i] ) {
792 free( Root_blocks[i] );
793 Root_blocks[i] = NULL;
798 // Init the file blocks
799 for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
800 if ( File_blocks[i] ) {
801 free( File_blocks[i] );
802 File_blocks[i] = NULL;
808 // Searches for a file. Follows all rules and precedence and searches
809 // CD's and pack files.
810 // Input: filespace - Filename & extension
811 // pathtype - See CF_TYPE_ defines in CFILE.H
812 // Output: pack_filename - Absolute path and filename of this file. Could be a packfile or the actual file.
814 // offset - Offset into pack file. 0 if not a packfile.
815 // Returns: If not found returns 0.
816 int cf_find_file_location( char *filespec, int pathtype, char *pack_filename, int *size, int *offset, bool localize )
820 Assert(filespec && strlen(filespec));
822 // see if we have something other than just a filename
823 // our current rules say that any file that specifies a direct
824 // path will try to be opened on that path. If that open
825 // fails, then we will open the file based on the extension
828 // NOTE: full path should also include localization, if so desired
829 if ( strpbrk(filespec,"/\\:") ) { // do we have a full path already?
830 FILE *fp = fopen(filespec, "rb" );
832 if ( size ) *size = filelength(fileno(fp));
833 if ( offset ) *offset = 0;
834 if ( pack_filename ) {
835 strcpy( pack_filename, filespec );
841 return 0; // If they give a full path, fail if not found.
844 // Search the hard drive for files first.
845 int num_search_dirs = 0;
846 int search_order[CF_MAX_PATH_TYPES];
848 if ( CF_TYPE_SPECIFIED(pathtype) ) {
849 search_order[num_search_dirs++] = pathtype;
852 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++) {
853 if ( i != pathtype ) {
854 search_order[num_search_dirs++] = i;
858 for (i=0; i<num_search_dirs; i++ ) {
859 char longname[MAX_PATH_LEN];
861 cf_create_default_path_string( longname, search_order[i], filespec, localize );
863 FILE *fp = fopen(longname, "rb" );
865 if ( size ) *size = filelength(fileno(fp));
866 if ( offset ) *offset = 0;
867 if ( pack_filename ) {
868 strcpy( pack_filename, longname );
875 // Search the pak files and CD-ROM.
877 for (i=0; i<Num_files; i++ ) {
878 cf_file * f = cf_get_file(i);
880 // only search paths we're supposed to...
881 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
886 // create localized filespec
887 char temp[MAX_PATH_LEN];
888 strcpy(temp, filespec);
889 lcl_add_dir_to_path_with_filename(filespec);
891 if ( !stricmp(filespec, f->name_ext) ) {
892 if ( size ) *size = f->size;
893 if ( offset ) *offset = f->pack_offset;
894 if ( pack_filename ) {
895 cf_root * r = cf_get_root(f->root_index);
897 strcpy( pack_filename, r->path );
898 if ( f->pack_offset < 1 ) {
899 strcat( pack_filename, Pathtypes[f->pathtype_index].path );
901 strcat( pack_filename, "/" );
903 strcat( pack_filename, "\\" );
905 strcat( pack_filename, f->name_ext );
910 // restore original filespec
911 strcpy(filespec, temp);
914 // file either not localized or localized version not found
915 if ( !stricmp(filespec, f->name_ext) ) {
916 if ( size ) *size = f->size;
917 if ( offset ) *offset = f->pack_offset;
918 if ( pack_filename ) {
919 cf_root * r = cf_get_root(f->root_index);
921 strcpy( pack_filename, r->path );
922 if ( f->pack_offset < 1 ) {
924 if(strlen(Pathtypes[f->pathtype_index].path)){
925 strcat( pack_filename, Pathtypes[f->pathtype_index].path );
927 strcat( pack_filename, "/" );
929 strcat( pack_filename, "\\" );
933 strcat( pack_filename, f->name_ext );
944 // Returns true if filename matches filespec, else zero if not
945 int cf_matches_spec(char *filespec, char *filename)
947 char *src_ext, *dst_ext;
949 src_ext = strchr(filespec, '.');
955 dst_ext = strchr(filename, '.');
959 return !stricmp(dst_ext, src_ext);
962 int (*Get_file_list_filter)(char *filename) = NULL;
963 int Skip_packfile_search = 0;
965 int cf_file_already_in_list( int num_files, char **list, char *filename )
969 char name_no_extension[MAX_PATH_LEN];
971 strcpy(name_no_extension, filename );
972 char *p = strchr( name_no_extension, '.' );
975 for (i=0; i<num_files; i++ ) {
976 if ( !stricmp(list[i], name_no_extension ) ) {
985 // An alternative cf_get_file_list(), dynamic list version.
986 // This one has a 'type', which is a CF_TYPE_* value. Because this specifies the directory
987 // location, 'filter' only needs to be the filter itself, with no path information.
988 // See above descriptions of cf_get_file_list() for more information about how it all works.
989 int cf_get_file_list( int max, char **list, int pathtype, char *filter, int sort, file_list_info *info )
992 int i, l, num_files = 0, own_flag = 0;
999 Get_file_list_filter = NULL;
1005 if (!info && (sort == CF_SORT_TIME)) {
1006 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1010 char filespec[MAX_PATH_LEN];
1013 cf_create_default_path_string( filespec, pathtype, NULL );
1018 dirp = opendir (filespec);
1020 while ((dir = readdir (dirp)) != NULL)
1022 if (num_files >= max)
1025 if (fnmatch(filter, dir->d_name, 0) != 0)
1029 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
1033 if (stat(fn, &buf) == -1) {
1037 if (!S_ISREG(buf.st_mode)) {
1041 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1042 ptr = strrchr(dir->d_name, '.');
1044 l = ptr - dir->d_name;
1046 l = strlen(dir->d_name);
1048 list[num_files] = (char *)malloc(l + 1);
1049 strncpy(list[num_files], dir->d_name, l);
1050 list[num_files][l] = 0;
1052 info[num_files].write_time = buf.st_mtime;
1061 cf_create_default_path_string( filespec, pathtype, filter );
1063 find_handle = _findfirst( filespec, &find );
1064 if (find_handle != -1) {
1066 if (num_files >= max)
1069 if (!(find.attrib & _A_SUBDIR)) {
1070 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1071 ptr = strrchr(find.name, '.');
1073 l = ptr - find.name;
1075 l = strlen(find.name);
1077 list[num_files] = (char *)malloc(l + 1);
1078 strncpy(list[num_files], find.name, l);
1079 list[num_files][l] = 0;
1081 info[num_files].write_time = find.time_write;
1087 } while (!_findnext(find_handle, &find));
1089 _findclose( find_handle );
1094 // Search all the packfiles and CD.
1095 if ( !Skip_packfile_search ) {
1096 for (i=0; i<Num_files; i++ ) {
1097 cf_file * f = cf_get_file(i);
1099 // only search paths we're supposed to...
1100 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
1104 if (num_files >= max)
1107 if ( !cf_matches_spec( filter,f->name_ext)) {
1111 if ( cf_file_already_in_list(num_files,list,f->name_ext)) {
1115 if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1117 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1119 ptr = strrchr(f->name_ext, '.');
1121 l = ptr - f->name_ext;
1123 l = strlen(f->name_ext);
1125 list[num_files] = (char *)malloc(l + 1);
1126 strncpy(list[num_files], f->name_ext, l);
1127 list[num_files][l] = 0;
1130 info[num_files].write_time = f->write_time;
1140 if (sort != CF_SORT_NONE) {
1141 cf_sort_filenames( num_files, list, sort, info );
1148 Get_file_list_filter = NULL;
1152 int cf_file_already_in_list_preallocated( int num_files, char arr[][MAX_FILENAME_LEN], char *filename )
1156 char name_no_extension[MAX_PATH_LEN];
1158 strcpy(name_no_extension, filename );
1159 char *p = strchr( name_no_extension, '.' );
1162 for (i=0; i<num_files; i++ ) {
1163 if ( !stricmp(arr[i], name_no_extension ) ) {
1172 // An alternative cf_get_file_list(), fixed array version.
1173 // This one has a 'type', which is a CF_TYPE_* value. Because this specifies the directory
1174 // location, 'filter' only needs to be the filter itself, with no path information.
1175 // See above descriptions of cf_get_file_list() for more information about how it all works.
1176 int cf_get_file_list_preallocated( int max, char arr[][MAX_FILENAME_LEN], char **list, int pathtype, char *filter, int sort, file_list_info *info )
1178 int i, num_files = 0, own_flag = 0;
1181 Get_file_list_filter = NULL;
1186 for (i=0; i<max; i++) {
1190 sort = CF_SORT_NONE; // sorting of array directly not supported. Sorting done on list only
1193 if (!info && (sort == CF_SORT_TIME)) {
1194 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1198 char filespec[MAX_PATH_LEN];
1200 // Search the default directories
1202 cf_create_default_path_string( filespec, pathtype, NULL );
1207 dirp = opendir (filespec);
1209 while ((dir = readdir (dirp)) != NULL)
1211 if (num_files >= max)
1214 if (fnmatch(filter, dir->d_name, 0) != 0)
1218 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
1222 if (stat(fn, &buf) == -1) {
1226 if (!S_ISREG(buf.st_mode)) {
1230 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1232 strncpy(arr[num_files], dir->d_name, MAX_FILENAME_LEN - 1 );
1233 char *ptr = strrchr(arr[num_files], '.');
1239 info[num_files].write_time = buf.st_mtime;
1248 cf_create_default_path_string( filespec, pathtype, filter );
1253 find_handle = _findfirst( filespec, &find );
1254 if (find_handle != -1) {
1256 if (num_files >= max)
1259 if (!(find.attrib & _A_SUBDIR)) {
1261 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1263 strncpy(arr[num_files], find.name, MAX_FILENAME_LEN - 1 );
1264 char *ptr = strrchr(arr[num_files], '.');
1270 info[num_files].write_time = find.time_write;
1277 } while (!_findnext(find_handle, &find));
1279 _findclose( find_handle );
1284 // Search all the packfiles and CD.
1285 if ( !Skip_packfile_search ) {
1286 for (i=0; i<Num_files; i++ ) {
1287 cf_file * f = cf_get_file(i);
1289 // only search paths we're supposed to...
1290 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
1294 if (num_files >= max)
1297 if ( !cf_matches_spec( filter,f->name_ext)) {
1301 if ( cf_file_already_in_list_preallocated( num_files, arr, f->name_ext )) {
1305 if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1307 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1309 strncpy(arr[num_files], f->name_ext, MAX_FILENAME_LEN - 1 );
1310 char *ptr = strrchr(arr[num_files], '.');
1316 info[num_files].write_time = f->write_time;
1325 if (sort != CF_SORT_NONE) {
1327 cf_sort_filenames( num_files, list, sort, info );
1334 Get_file_list_filter = NULL;
1338 // Returns the default storage path for files given a
1339 // particular pathtype. In other words, the path to
1340 // the unpacked, non-cd'd, stored on hard drive path.
1341 // If filename isn't null it will also tack the filename
1342 // on the end, creating a completely valid filename.
1343 // Input: pathtype - CF_TYPE_??
1344 // filename - optional, if set, tacks the filename onto end of path.
1345 // Output: path - Fully qualified pathname.
1346 void cf_create_default_path_string( char *path, int pathtype, char *filename, bool localize )
1349 if ( filename && strpbrk(filename,"/") ) {
1351 if ( filename && strpbrk(filename,"/\\:") ) {
1353 // Already has full path
1354 strcpy( path, filename );
1357 cf_root *root = cf_get_root(0);
1360 strcpy(path, filename);
1364 Assert(CF_TYPE_SPECIFIED(pathtype));
1366 strcpy(path, root->path);
1367 strcat(path, Pathtypes[pathtype].path);
1369 // Don't add slash for root directory
1370 if (Pathtypes[pathtype].path[0] != '\0') {
1380 strcat(path, filename);
1382 // localize filename
1384 // create copy of path
1385 char temp_path[MAX_PATH_LEN];
1386 strcpy(temp_path, path);
1388 // localize the path
1389 lcl_add_dir_to_path_with_filename(path);
1391 // verify localized path
1392 FILE *fp = fopen(path, "rb");
1396 strcpy(path, temp_path);