2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/CFile/CfileSystem.cpp $
15 * Functions to keep track of and find files that can exist
16 * on the harddrive, cd-rom, or in a pack file on either of those.
17 * This keeps a list of all the files in packfiles or on CD-rom
18 * and when you need a file you call one function which then searches
19 * all those locations, inherently enforcing precedence orders.
22 * Revision 1.7 2002/06/22 23:57:39 relnev
23 * remove writable strings.
25 * fix compile for intel compiler.
27 * Revision 1.6 2002/06/09 04:41:15 relnev
28 * added copyright header
30 * Revision 1.5 2002/06/05 04:03:32 relnev
31 * finished cfilesystem.
33 * removed some old code.
35 * fixed mouse save off-by-one.
39 * Revision 1.4 2002/05/28 17:26:57 theoddone33
40 * Fill in some timer and palette setting stubs. Still no display
42 * Revision 1.3 2002/05/28 06:45:38 theoddone33
45 * Revision 1.2 2002/05/28 06:28:20 theoddone33
46 * Filesystem mods, actually reads some data files now
48 * Revision 1.1.1.1 2002/05/03 03:28:08 root
52 * 6 9/08/99 10:01p Dave
53 * Make sure game won't run in a drive's root directory. Make sure
54 * standalone routes suqad war messages properly to the host.
56 * 5 9/03/99 1:31a Dave
57 * CD checking by act. Added support to play 2 cutscenes in a row
58 * seamlessly. Fixed super low level cfile bug related to files in the
59 * root directory of a CD. Added cheat code to set campaign mission # in
62 * 4 2/22/99 10:31p Andsager
63 * Get rid of unneeded includes.
65 * 3 10/13/98 9:19a Andsager
66 * Add localization support to cfile. Optional parameter with cfopen that
67 * looks for localized files.
69 * 2 10/07/98 10:52a Dave
72 * 1 10/07/98 10:48a Dave
74 * 14 8/31/98 2:06p Dave
75 * Make cfile sort the ordering or vp files. Added support/checks for
76 * recognizing "mission disk" players.
78 * 13 6/23/98 4:18p Hoffoss
79 * Fixed some bugs with AC release build.
81 * 12 5/20/98 10:46p John
82 * Added code that doesn't include duplicate filenames in any file list
85 * 11 5/14/98 2:14p Lawrance2
86 * Use filespec filtering for packfiles
88 * 10 5/03/98 11:53a John
89 * Fixed filename case mangling.
91 * 9 5/02/98 11:06p Allender
92 * correctly deal with pack pathnames
94 * 8 5/01/98 11:41a Allender
95 * Fixed bug with mission saving in Fred.
97 * 7 5/01/98 10:21a John
98 * Added code to find all pack files in all trees. Added code to create
99 * any directories that we write to.
101 * 6 4/30/98 10:21p John
102 * Added code to cleanup cfilesystem
104 * 5 4/30/98 10:18p John
105 * added source safe header
118 #include <winbase.h> /* needed for memory mapping of file functions */
120 #include <sys/types.h>
123 #include <sys/stat.h>
128 //#include "outwnd.h"
129 //#include "vecmat.h"
132 #include "cfilesystem.h"
133 #include "localize.h"
136 #define CF_ROOTTYPE_PATH 0
137 #define CF_ROOTTYPE_PACK 1
140 // specifying hard drive tree
141 // searching for pack files on hard drive // Found by searching all known paths
142 // specifying cd-rom tree
143 // searching for pack files on CD-rom tree
144 typedef struct cf_root {
145 char path[CF_MAX_PATHNAME_LENGTH]; // Contains something like c:\projects\freespace or c:\projects\freespace\freespace.vp
146 int roottype; // CF_ROOTTYPE_PATH = Path, CF_ROOTTYPE_PACK =Pack file
149 // convenient type for sorting (see cf_build_pack_list())
150 typedef struct cf_root_sort {
151 char path[CF_MAX_PATHNAME_LENGTH];
156 #define CF_NUM_ROOTS_PER_BLOCK 32
157 #define CF_MAX_ROOT_BLOCKS 256 // Can store 32*256 = 8192 Roots
158 #define CF_MAX_ROOTS (CF_NUM_ROOTS_PER_BLOCK * CF_MAX_ROOT_BLOCKS)
160 typedef struct cf_root_block {
161 cf_root roots[CF_NUM_ROOTS_PER_BLOCK];
164 static int Num_roots = 0;
165 static cf_root_block *Root_blocks[CF_MAX_ROOT_BLOCKS];
168 // Created by searching all roots in order. This means Files is then sorted by precedence.
169 typedef struct cf_file {
170 char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension
171 int root_index; // Where in Roots this is located
172 int pathtype_index; // Where in Paths this is located
173 time_t write_time; // When it was last written
174 int size; // How big it is in bytes
175 int pack_offset; // For pack files, where it is at. 0 if not in a pack file. This can be used to tell if in a pack file.
178 #define CF_NUM_FILES_PER_BLOCK 256
179 #define CF_MAX_FILE_BLOCKS 128 // Can store 256*128 = 32768 files
181 typedef struct cf_file_block {
182 cf_file files[CF_NUM_FILES_PER_BLOCK];
185 static int Num_files = 0;
186 static cf_file_block *File_blocks[CF_MAX_FILE_BLOCKS];
189 // Return a pointer to to file 'index'.
190 cf_file *cf_get_file(int index)
192 int block = index / CF_NUM_FILES_PER_BLOCK;
193 int offset = index % CF_NUM_FILES_PER_BLOCK;
195 return &File_blocks[block]->files[offset];
198 // Create a new file and return a pointer to it.
199 cf_file *cf_create_file()
201 int block = Num_files / CF_NUM_FILES_PER_BLOCK;
202 int offset = Num_files % CF_NUM_FILES_PER_BLOCK;
204 if ( File_blocks[block] == NULL ) {
205 File_blocks[block] = (cf_file_block *)malloc( sizeof(cf_file_block) );
206 Assert( File_blocks[block] != NULL);
211 return &File_blocks[block]->files[offset];
214 extern int cfile_inited;
216 // Create a new root and return a pointer to it. The structure is assumed unitialized.
217 cf_root *cf_get_root(int n)
219 int block = n / CF_NUM_ROOTS_PER_BLOCK;
220 int offset = n % CF_NUM_ROOTS_PER_BLOCK;
225 return &Root_blocks[block]->roots[offset];
229 // Create a new root and return a pointer to it. The structure is assumed unitialized.
230 cf_root *cf_create_root()
232 int block = Num_roots / CF_NUM_ROOTS_PER_BLOCK;
233 int offset = Num_roots % CF_NUM_ROOTS_PER_BLOCK;
235 if ( Root_blocks[block] == NULL ) {
236 Root_blocks[block] = (cf_root_block *)malloc( sizeof(cf_root_block) );
237 Assert(Root_blocks[block] != NULL);
242 return &Root_blocks[block]->roots[offset];
245 // return the # of packfiles which exist
246 int cf_get_packfile_count(cf_root *root)
248 char filespec[MAX_PATH_LEN];
252 // count up how many packfiles we're gonna have
254 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
256 strcpy( filespec, root->path );
258 if(strlen(Pathtypes[i].path)){
259 strcat( filespec, Pathtypes[i].path );
260 strcat( filespec, "/" );
266 dirp = opendir (filespec);
268 while ((dir = readdir (dirp)) != NULL)
270 if (!fnmatch ("*.vp", dir->d_name, 0))
276 strcpy( filespec, root->path );
278 if(strlen(Pathtypes[i].path)){
279 strcat( filespec, Pathtypes[i].path );
280 strcat( filespec, "\\" );
283 strcat( filespec, "*.vp" );
288 find_handle = _findfirst( filespec, &find );
290 if (find_handle != -1) {
292 if (!(find.attrib & _A_SUBDIR)) {
296 } while (!_findnext(find_handle, &find));
298 _findclose( find_handle );
303 return packfile_count;
306 // packfile sort function
307 int cf_packfile_sort_func(const void *elem1, const void *elem2)
309 cf_root_sort *r1, *r2;
310 r1 = (cf_root_sort*)elem1;
311 r2 = (cf_root_sort*)elem2;
313 // if the 2 directory types are the same, do a string compare
314 if(r1->cf_type == r2->cf_type){
315 return stricmp(r1->path, r2->path);
318 // otherwise return them in order of CF_TYPE_* precedence
319 return (r1->cf_type < r2->cf_type) ? -1 : 1;
322 // Go through a root and look for pack files
323 void cf_build_pack_list( cf_root *root )
325 char filespec[MAX_PATH_LEN];
327 cf_root_sort *temp_roots_sort, *rptr_sort;
328 int temp_root_count, root_index;
330 // determine how many packfiles there are
331 temp_root_count = cf_get_packfile_count(root);
332 if(temp_root_count <= 0){
336 // allocate a temporary array of temporary roots so we can easily sort them
337 temp_roots_sort = (cf_root_sort*)malloc(sizeof(cf_root_sort) * temp_root_count);
338 if(temp_roots_sort == NULL){
343 // now just setup all the root info
345 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
348 strcpy( filespec, root->path );
350 if(strlen(Pathtypes[i].path)){
351 strcat( filespec, Pathtypes[i].path );
352 strcat( filespec, "/" );
358 dirp = opendir (filespec);
360 while ((dir = readdir (dirp)) != NULL)
362 if (!fnmatch ("*.vp", dir->d_name, 0))
364 Assert(root_index < temp_root_count);
367 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
371 if (stat(fn, &buf) == -1) {
375 if (!S_ISREG(buf.st_mode)) {
379 // get a temp pointer
380 rptr_sort = &temp_roots_sort[root_index++];
382 // fill in all the proper info
383 strcpy(rptr_sort->path, root->path);
385 if(strlen(Pathtypes[i].path)){
386 strcat(rptr_sort->path, Pathtypes[i].path );
387 strcat(rptr_sort->path, "/");
390 strcat(rptr_sort->path, dir->d_name );
391 rptr_sort->roottype = CF_ROOTTYPE_PACK;
392 rptr_sort->cf_type = i;
398 strcpy( filespec, root->path );
400 if(strlen(Pathtypes[i].path)){
401 strcat( filespec, Pathtypes[i].path );
402 strcat( filespec, "\\" );
404 strcat( filespec, "*.vp" );
408 find_handle = _findfirst( filespec, &find );
410 if (find_handle != -1) {
413 if (!(find.attrib & _A_SUBDIR)) {
414 Assert(root_index < temp_root_count);
416 // get a temp pointer
417 rptr_sort = &temp_roots_sort[root_index++];
419 // fill in all the proper info
420 strcpy(rptr_sort->path, root->path);
422 if(strlen(Pathtypes[i].path)){
423 strcat(rptr_sort->path, Pathtypes[i].path );
424 strcat(rptr_sort->path, "\\");
427 strcat(rptr_sort->path, find.name );
428 rptr_sort->roottype = CF_ROOTTYPE_PACK;
429 rptr_sort->cf_type = i;
432 } while (!_findnext(find_handle, &find));
434 _findclose( find_handle );
439 // these should always be the same
440 Assert(root_index == temp_root_count);
443 qsort(temp_roots_sort, temp_root_count, sizeof(cf_root_sort), cf_packfile_sort_func);
445 // now insert them all into the real root list properly
447 for(i=0; i<temp_root_count; i++){
448 new_root = cf_create_root();
449 strcpy( new_root->path, root->path );
451 // mwa -- 4/2/98 put in the next 2 lines because the path name needs to be there
452 // to find the files.
453 strcpy(new_root->path, temp_roots_sort[i].path);
454 new_root->roottype = CF_ROOTTYPE_PACK;
457 // free up the temp list
458 free(temp_roots_sort);
462 void cf_build_root_list(char *cdrom_dir)
468 //======================================================
469 // First, check the current directory.
470 // strcpy( root->path, "d:\\projects\\freespace\\" );
472 root = cf_create_root();
474 if ( !_getcwd(root->path, CF_MAX_PATHNAME_LENGTH ) ) {
475 Error(LOCATION, "Can't get current working directory -- %d", errno );
478 // do we already have a slash? as in the case of a root directory install
480 if(strlen(root->path) && (root->path[strlen(root->path)-1] != '/')){
481 strcat(root->path, "/"); // put trailing backslash on for easier path construction
483 if(strlen(root->path) && (root->path[strlen(root->path)-1] != '\\')){
484 strcat(root->path, "\\"); // put trailing backslash on for easier path construction
487 root->roottype = CF_ROOTTYPE_PATH;
489 //======================================================
490 // Next, check any VP files under the current directory.
491 cf_build_pack_list(root);
494 //======================================================
495 // Check the real CD if one...
496 if ( cdrom_dir && strlen(cdrom_dir) ) {
497 root = cf_create_root();
498 strcpy( root->path, cdrom_dir );
499 root->roottype = CF_ROOTTYPE_PATH;
501 //======================================================
502 // Next, check any VP files in the CD-ROM directory.
503 cf_build_pack_list(root);
509 // Given a lower case list of file extensions
510 // separated by spaces, return zero if ext is
512 int is_ext_in_list( char *ext_list, char *ext )
516 strncpy( tmp_ext, ext, 127 );
518 if ( strstr(ext_list, tmp_ext )) {
525 void cf_search_root_path(int root_index)
529 cf_root *root = cf_get_root(root_index);
531 mprintf(( "Searching root '%s'\n", root->path ));
533 char search_path[CF_MAX_PATHNAME_LENGTH];
535 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
541 strcpy( search_path, root->path );
543 if(strlen(Pathtypes[i].path)){
544 strcat( search_path, Pathtypes[i].path );
545 strcat( search_path, "/" );
548 dirp = opendir (search_path);
550 while ((dir = readdir (dirp)) != NULL)
552 if (!fnmatch ("*.*", dir->d_name, 0))
555 snprintf(fn, MAX_PATH-1, "%s/%s", search_path, dir->d_name);
559 if (stat(fn, &buf) == -1) {
563 if (!S_ISREG(buf.st_mode)) {
567 char *ext = strchr( dir->d_name, '.' );
569 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) {
571 cf_file *file = cf_create_file();
573 strcpy( file->name_ext, dir->d_name );
574 file->root_index = root_index;
575 file->pathtype_index = i;
578 file->write_time = buf.st_mtime;
579 file->size = buf.st_size;
581 file->pack_offset = 0; // Mark as a non-packed file
583 //mprintf(( "Found file '%s'\n", file->name_ext ));
591 strcpy( search_path, root->path );
593 if(strlen(Pathtypes[i].path)){
594 strcat( search_path, Pathtypes[i].path );
595 strcat( search_path, "\\" );
598 strcat( search_path, "*.*" );
603 find_handle = _findfirst( search_path, &find );
605 if (find_handle != -1) {
607 if (!(find.attrib & _A_SUBDIR)) {
609 char *ext = strchr( find.name, '.' );
611 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) {
613 cf_file *file = cf_create_file();
615 strcpy( file->name_ext, find.name );
616 file->root_index = root_index;
617 file->pathtype_index = i;
618 file->write_time = find.time_write;
619 file->size = find.size;
620 file->pack_offset = 0; // Mark as a non-packed file
622 //mprintf(( "Found file '%s'\n", file->name_ext ));
629 } while (!_findnext(find_handle, &find));
631 _findclose( find_handle );
639 typedef struct VP_FILE_HEADER {
646 typedef struct VP_FILE {
653 void cf_search_root_pack(int root_index)
657 cf_root *root = cf_get_root(root_index);
659 //mprintf(( "Searching root pack '%s'\n", root->path ));
663 FILE *fp = fopen( root->path, "rb" );
664 // Read the file header
669 VP_FILE_HEADER VP_header;
671 Assert( sizeof(VP_header) == 16 );
672 fread(&VP_header, 1, sizeof(VP_header), fp);
675 fseek(fp, VP_header.index_offset, SEEK_SET);
677 char search_path[CF_MAX_PATHNAME_LENGTH];
679 strcpy( search_path, "" );
681 // Go through all the files
682 for (i=0; i<VP_header.num_files; i++ ) {
685 fread( &find, sizeof(VP_FILE), 1, fp );
687 if ( find.size == 0 ) {
688 if ( !stricmp( find.filename, ".." )) {
689 int l = strlen(search_path);
690 char *p = &search_path[l-1];
692 while( (p > search_path) && (*p != '/') ) {
694 while( (p > search_path) && (*p != '\\') ) {
700 if ( strlen(search_path) ) {
702 strcat( search_path, "/" );
704 strcat( search_path, "\\" );
707 strcat( search_path, find.filename );
710 //mprintf(( "Current dir = '%s'\n", search_path ));
714 for (j=CF_TYPE_ROOT; j<CF_MAX_PATH_TYPES; j++ ) {
716 if ( !stricmp( search_path, Pathtypes[j].path )) {
718 char *ext = strchr( find.filename, '.' );
720 if ( is_ext_in_list( Pathtypes[j].extensions, ext ) ) {
722 cf_file *file = cf_create_file();
724 strcpy( file->name_ext, find.filename );
725 file->root_index = root_index;
726 file->pathtype_index = j;
727 file->write_time = find.write_time;
728 file->size = find.size;
729 file->pack_offset = find.offset; // Mark as a non-packed file
731 //mprintf(( "Found pack file '%s'\n", file->name_ext ));
745 void cf_build_file_list()
751 // For each root, find all files...
752 for (i=1; i<Num_roots; i++ ) {
753 cf_root *root = cf_get_root(i);
754 if ( root->roottype == CF_ROOTTYPE_PATH ) {
755 cf_search_root_path(i);
756 } else if ( root->roottype == CF_ROOTTYPE_PACK ) {
757 cf_search_root_pack(i);
764 void cf_build_secondary_filelist(char *cdrom_dir)
772 // Init the path types
773 for (i=0; i<CF_MAX_PATH_TYPES; i++ ) {
774 Assert( Pathtypes[i].index == i );
775 #if 0 /* they are already lowercased -- SBF */
776 if ( Pathtypes[i].extensions ) {
777 strlwr(Pathtypes[i].extensions);
782 // Init the root blocks
783 for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ ) {
784 Root_blocks[i] = NULL;
787 // Init the file blocks
788 for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
789 File_blocks[i] = NULL;
792 mprintf(( "Building file index...\n" ));
794 // build the list of searchable roots
795 cf_build_root_list(cdrom_dir);
797 // build the list of files themselves
798 cf_build_file_list();
800 mprintf(( "Found %d roots and %d files.\n", Num_roots, Num_files ));
803 void cf_free_secondary_filelist()
807 // Free the root blocks
808 for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ ) {
809 if ( Root_blocks[i] ) {
810 free( Root_blocks[i] );
811 Root_blocks[i] = NULL;
816 // Init the file blocks
817 for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
818 if ( File_blocks[i] ) {
819 free( File_blocks[i] );
820 File_blocks[i] = NULL;
826 // Searches for a file. Follows all rules and precedence and searches
827 // CD's and pack files.
828 // Input: filespace - Filename & extension
829 // pathtype - See CF_TYPE_ defines in CFILE.H
830 // Output: pack_filename - Absolute path and filename of this file. Could be a packfile or the actual file.
832 // offset - Offset into pack file. 0 if not a packfile.
833 // Returns: If not found returns 0.
834 int cf_find_file_location( char *filespec, int pathtype, char *pack_filename, int *size, int *offset, bool localize )
838 Assert(filespec && strlen(filespec));
840 // see if we have something other than just a filename
841 // our current rules say that any file that specifies a direct
842 // path will try to be opened on that path. If that open
843 // fails, then we will open the file based on the extension
846 // NOTE: full path should also include localization, if so desired
847 if ( strpbrk(filespec,"/\\:") ) { // do we have a full path already?
848 FILE *fp = fopen(filespec, "rb" );
850 if ( size ) *size = filelength(fileno(fp));
851 if ( offset ) *offset = 0;
852 if ( pack_filename ) {
853 strcpy( pack_filename, filespec );
859 return 0; // If they give a full path, fail if not found.
862 // Search the hard drive for files first.
863 int num_search_dirs = 0;
864 int search_order[CF_MAX_PATH_TYPES];
866 if ( CF_TYPE_SPECIFIED(pathtype) ) {
867 search_order[num_search_dirs++] = pathtype;
870 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++) {
871 if ( i != pathtype ) {
872 search_order[num_search_dirs++] = i;
876 for (i=0; i<num_search_dirs; i++ ) {
877 char longname[MAX_PATH_LEN];
879 cf_create_default_path_string( longname, search_order[i], filespec, localize );
881 FILE *fp = fopen(longname, "rb" );
883 if ( size ) *size = filelength(fileno(fp));
884 if ( offset ) *offset = 0;
885 if ( pack_filename ) {
886 strcpy( pack_filename, longname );
893 // Search the pak files and CD-ROM.
895 for (i=0; i<Num_files; i++ ) {
896 cf_file * f = cf_get_file(i);
898 // only search paths we're supposed to...
899 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
904 // create localized filespec
905 char temp[MAX_PATH_LEN];
906 strcpy(temp, filespec);
907 lcl_add_dir_to_path_with_filename(filespec);
909 if ( !stricmp(filespec, f->name_ext) ) {
910 if ( size ) *size = f->size;
911 if ( offset ) *offset = f->pack_offset;
912 if ( pack_filename ) {
913 cf_root * r = cf_get_root(f->root_index);
915 strcpy( pack_filename, r->path );
916 if ( f->pack_offset < 1 ) {
917 strcat( pack_filename, Pathtypes[f->pathtype_index].path );
919 strcat( pack_filename, "/" );
921 strcat( pack_filename, "\\" );
923 strcat( pack_filename, f->name_ext );
928 // restore original filespec
929 strcpy(filespec, temp);
932 // file either not localized or localized version not found
933 if ( !stricmp(filespec, f->name_ext) ) {
934 if ( size ) *size = f->size;
935 if ( offset ) *offset = f->pack_offset;
936 if ( pack_filename ) {
937 cf_root * r = cf_get_root(f->root_index);
939 strcpy( pack_filename, r->path );
940 if ( f->pack_offset < 1 ) {
942 if(strlen(Pathtypes[f->pathtype_index].path)){
943 strcat( pack_filename, Pathtypes[f->pathtype_index].path );
945 strcat( pack_filename, "/" );
947 strcat( pack_filename, "\\" );
951 strcat( pack_filename, f->name_ext );
962 // Returns true if filename matches filespec, else zero if not
963 int cf_matches_spec(char *filespec, char *filename)
965 char *src_ext, *dst_ext;
967 src_ext = strchr(filespec, '.');
973 dst_ext = strchr(filename, '.');
977 return !stricmp(dst_ext, src_ext);
980 int (*Get_file_list_filter)(char *filename) = NULL;
981 int Skip_packfile_search = 0;
983 int cf_file_already_in_list( int num_files, char **list, char *filename )
987 char name_no_extension[MAX_PATH_LEN];
989 strcpy(name_no_extension, filename );
990 char *p = strchr( name_no_extension, '.' );
993 for (i=0; i<num_files; i++ ) {
994 if ( !stricmp(list[i], name_no_extension ) ) {
1003 // An alternative cf_get_file_list(), dynamic list version.
1004 // This one has a 'type', which is a CF_TYPE_* value. Because this specifies the directory
1005 // location, 'filter' only needs to be the filter itself, with no path information.
1006 // See above descriptions of cf_get_file_list() for more information about how it all works.
1007 int cf_get_file_list( int max, char **list, int pathtype, char *filter, int sort, file_list_info *info )
1010 int i, l, num_files = 0, own_flag = 0;
1017 Get_file_list_filter = NULL;
1023 if (!info && (sort == CF_SORT_TIME)) {
1024 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1028 char filespec[MAX_PATH_LEN];
1031 cf_create_default_path_string( filespec, pathtype, NULL );
1036 dirp = opendir (filespec);
1038 while ((dir = readdir (dirp)) != NULL)
1040 if (num_files >= max)
1043 if (fnmatch(filter, dir->d_name, 0) != 0)
1047 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
1051 if (stat(fn, &buf) == -1) {
1055 if (!S_ISREG(buf.st_mode)) {
1059 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1060 ptr = strrchr(dir->d_name, '.');
1062 l = ptr - dir->d_name;
1064 l = strlen(dir->d_name);
1066 list[num_files] = (char *)malloc(l + 1);
1067 strncpy(list[num_files], dir->d_name, l);
1068 list[num_files][l] = 0;
1070 info[num_files].write_time = buf.st_mtime;
1079 cf_create_default_path_string( filespec, pathtype, filter );
1081 find_handle = _findfirst( filespec, &find );
1082 if (find_handle != -1) {
1084 if (num_files >= max)
1087 if (!(find.attrib & _A_SUBDIR)) {
1088 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1089 ptr = strrchr(find.name, '.');
1091 l = ptr - find.name;
1093 l = strlen(find.name);
1095 list[num_files] = (char *)malloc(l + 1);
1096 strncpy(list[num_files], find.name, l);
1097 list[num_files][l] = 0;
1099 info[num_files].write_time = find.time_write;
1105 } while (!_findnext(find_handle, &find));
1107 _findclose( find_handle );
1112 // Search all the packfiles and CD.
1113 if ( !Skip_packfile_search ) {
1114 for (i=0; i<Num_files; i++ ) {
1115 cf_file * f = cf_get_file(i);
1117 // only search paths we're supposed to...
1118 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
1122 if (num_files >= max)
1125 if ( !cf_matches_spec( filter,f->name_ext)) {
1129 if ( cf_file_already_in_list(num_files,list,f->name_ext)) {
1133 if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1135 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1137 ptr = strrchr(f->name_ext, '.');
1139 l = ptr - f->name_ext;
1141 l = strlen(f->name_ext);
1143 list[num_files] = (char *)malloc(l + 1);
1144 strncpy(list[num_files], f->name_ext, l);
1145 list[num_files][l] = 0;
1148 info[num_files].write_time = f->write_time;
1158 if (sort != CF_SORT_NONE) {
1159 cf_sort_filenames( num_files, list, sort, info );
1166 Get_file_list_filter = NULL;
1170 int cf_file_already_in_list_preallocated( int num_files, char arr[][MAX_FILENAME_LEN], char *filename )
1174 char name_no_extension[MAX_PATH_LEN];
1176 strcpy(name_no_extension, filename );
1177 char *p = strchr( name_no_extension, '.' );
1180 for (i=0; i<num_files; i++ ) {
1181 if ( !stricmp(arr[i], name_no_extension ) ) {
1190 // An alternative cf_get_file_list(), fixed array version.
1191 // This one has a 'type', which is a CF_TYPE_* value. Because this specifies the directory
1192 // location, 'filter' only needs to be the filter itself, with no path information.
1193 // See above descriptions of cf_get_file_list() for more information about how it all works.
1194 int cf_get_file_list_preallocated( int max, char arr[][MAX_FILENAME_LEN], char **list, int pathtype, char *filter, int sort, file_list_info *info )
1196 int i, num_files = 0, own_flag = 0;
1199 Get_file_list_filter = NULL;
1204 for (i=0; i<max; i++) {
1208 sort = CF_SORT_NONE; // sorting of array directly not supported. Sorting done on list only
1211 if (!info && (sort == CF_SORT_TIME)) {
1212 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1216 char filespec[MAX_PATH_LEN];
1218 // Search the default directories
1220 cf_create_default_path_string( filespec, pathtype, NULL );
1225 dirp = opendir (filespec);
1227 while ((dir = readdir (dirp)) != NULL)
1229 if (num_files >= max)
1232 if (fnmatch(filter, dir->d_name, 0) != 0)
1236 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
1240 if (stat(fn, &buf) == -1) {
1244 if (!S_ISREG(buf.st_mode)) {
1248 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1250 strncpy(arr[num_files], dir->d_name, MAX_FILENAME_LEN - 1 );
1251 char *ptr = strrchr(arr[num_files], '.');
1257 info[num_files].write_time = buf.st_mtime;
1266 cf_create_default_path_string( filespec, pathtype, filter );
1271 find_handle = _findfirst( filespec, &find );
1272 if (find_handle != -1) {
1274 if (num_files >= max)
1277 if (!(find.attrib & _A_SUBDIR)) {
1279 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1281 strncpy(arr[num_files], find.name, MAX_FILENAME_LEN - 1 );
1282 char *ptr = strrchr(arr[num_files], '.');
1288 info[num_files].write_time = find.time_write;
1295 } while (!_findnext(find_handle, &find));
1297 _findclose( find_handle );
1302 // Search all the packfiles and CD.
1303 if ( !Skip_packfile_search ) {
1304 for (i=0; i<Num_files; i++ ) {
1305 cf_file * f = cf_get_file(i);
1307 // only search paths we're supposed to...
1308 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
1312 if (num_files >= max)
1315 if ( !cf_matches_spec( filter,f->name_ext)) {
1319 if ( cf_file_already_in_list_preallocated( num_files, arr, f->name_ext )) {
1323 if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1325 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1327 strncpy(arr[num_files], f->name_ext, MAX_FILENAME_LEN - 1 );
1328 char *ptr = strrchr(arr[num_files], '.');
1334 info[num_files].write_time = f->write_time;
1343 if (sort != CF_SORT_NONE) {
1345 cf_sort_filenames( num_files, list, sort, info );
1352 Get_file_list_filter = NULL;
1356 // Returns the default storage path for files given a
1357 // particular pathtype. In other words, the path to
1358 // the unpacked, non-cd'd, stored on hard drive path.
1359 // If filename isn't null it will also tack the filename
1360 // on the end, creating a completely valid filename.
1361 // Input: pathtype - CF_TYPE_??
1362 // filename - optional, if set, tacks the filename onto end of path.
1363 // Output: path - Fully qualified pathname.
1364 void cf_create_default_path_string( char *path, int pathtype, char *filename, bool localize )
1367 if ( filename && strpbrk(filename,"/") ) {
1369 if ( filename && strpbrk(filename,"/\\:") ) {
1371 // Already has full path
1372 strcpy( path, filename );
1375 cf_root *root = cf_get_root(0);
1378 strcpy(path, filename);
1382 Assert(CF_TYPE_SPECIFIED(pathtype));
1384 strcpy(path, root->path);
1385 strcat(path, Pathtypes[pathtype].path);
1387 // Don't add slash for root directory
1388 if (Pathtypes[pathtype].path[0] != '\0') {
1398 strcat(path, filename);
1400 // localize filename
1402 // create copy of path
1403 char temp_path[MAX_PATH_LEN];
1404 strcpy(temp_path, path);
1406 // localize the path
1407 lcl_add_dir_to_path_with_filename(path);
1409 // verify localized path
1410 FILE *fp = fopen(path, "rb");
1414 strcpy(path, temp_path);