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.9 2003/05/27 03:03:11 taylor
23 * fix second root (gamedir) searching
25 * Revision 1.8 2003/02/20 17:41:07 theoddone33
26 * Userdir patch from Taylor Richards
28 * Revision 1.7 2002/06/22 23:57:39 relnev
29 * remove writable strings.
31 * fix compile for intel compiler.
33 * Revision 1.6 2002/06/09 04:41:15 relnev
34 * added copyright header
36 * Revision 1.5 2002/06/05 04:03:32 relnev
37 * finished cfilesystem.
39 * removed some old code.
41 * fixed mouse save off-by-one.
45 * Revision 1.4 2002/05/28 17:26:57 theoddone33
46 * Fill in some timer and palette setting stubs. Still no display
48 * Revision 1.3 2002/05/28 06:45:38 theoddone33
51 * Revision 1.2 2002/05/28 06:28:20 theoddone33
52 * Filesystem mods, actually reads some data files now
54 * Revision 1.1.1.1 2002/05/03 03:28:08 root
58 * 6 9/08/99 10:01p Dave
59 * Make sure game won't run in a drive's root directory. Make sure
60 * standalone routes suqad war messages properly to the host.
62 * 5 9/03/99 1:31a Dave
63 * CD checking by act. Added support to play 2 cutscenes in a row
64 * seamlessly. Fixed super low level cfile bug related to files in the
65 * root directory of a CD. Added cheat code to set campaign mission # in
68 * 4 2/22/99 10:31p Andsager
69 * Get rid of unneeded includes.
71 * 3 10/13/98 9:19a Andsager
72 * Add localization support to cfile. Optional parameter with cfopen that
73 * looks for localized files.
75 * 2 10/07/98 10:52a Dave
78 * 1 10/07/98 10:48a Dave
80 * 14 8/31/98 2:06p Dave
81 * Make cfile sort the ordering or vp files. Added support/checks for
82 * recognizing "mission disk" players.
84 * 13 6/23/98 4:18p Hoffoss
85 * Fixed some bugs with AC release build.
87 * 12 5/20/98 10:46p John
88 * Added code that doesn't include duplicate filenames in any file list
91 * 11 5/14/98 2:14p Lawrance2
92 * Use filespec filtering for packfiles
94 * 10 5/03/98 11:53a John
95 * Fixed filename case mangling.
97 * 9 5/02/98 11:06p Allender
98 * correctly deal with pack pathnames
100 * 8 5/01/98 11:41a Allender
101 * Fixed bug with mission saving in Fred.
103 * 7 5/01/98 10:21a John
104 * Added code to find all pack files in all trees. Added code to create
105 * any directories that we write to.
107 * 6 4/30/98 10:21p John
108 * Added code to cleanup cfilesystem
110 * 5 4/30/98 10:18p John
111 * added source safe header
124 #include <winbase.h> /* needed for memory mapping of file functions */
126 #include <sys/types.h>
129 #include <sys/stat.h>
134 //#include "outwnd.h"
135 //#include "vecmat.h"
138 #include "cfilesystem.h"
139 #include "localize.h"
142 #define CF_ROOTTYPE_PATH 0
143 #define CF_ROOTTYPE_PACK 1
146 // specifying hard drive tree
147 // searching for pack files on hard drive // Found by searching all known paths
148 // specifying cd-rom tree
149 // searching for pack files on CD-rom tree
150 typedef struct cf_root {
151 char path[CF_MAX_PATHNAME_LENGTH]; // Contains something like c:\projects\freespace or c:\projects\freespace\freespace.vp
152 int roottype; // CF_ROOTTYPE_PATH = Path, CF_ROOTTYPE_PACK =Pack file
155 // convenient type for sorting (see cf_build_pack_list())
156 typedef struct cf_root_sort {
157 char path[CF_MAX_PATHNAME_LENGTH];
162 #define CF_NUM_ROOTS_PER_BLOCK 32
163 #define CF_MAX_ROOT_BLOCKS 256 // Can store 32*256 = 8192 Roots
164 #define CF_MAX_ROOTS (CF_NUM_ROOTS_PER_BLOCK * CF_MAX_ROOT_BLOCKS)
166 typedef struct cf_root_block {
167 cf_root roots[CF_NUM_ROOTS_PER_BLOCK];
170 static int Num_roots = 0;
171 static cf_root_block *Root_blocks[CF_MAX_ROOT_BLOCKS];
174 // Created by searching all roots in order. This means Files is then sorted by precedence.
175 typedef struct cf_file {
176 char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension
177 int root_index; // Where in Roots this is located
178 int pathtype_index; // Where in Paths this is located
179 time_t write_time; // When it was last written
180 int size; // How big it is in bytes
181 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.
184 #define CF_NUM_FILES_PER_BLOCK 256
185 #define CF_MAX_FILE_BLOCKS 128 // Can store 256*128 = 32768 files
187 typedef struct cf_file_block {
188 cf_file files[CF_NUM_FILES_PER_BLOCK];
191 static int Num_files = 0;
192 static cf_file_block *File_blocks[CF_MAX_FILE_BLOCKS];
195 // Return a pointer to to file 'index'.
196 cf_file *cf_get_file(int index)
198 int block = index / CF_NUM_FILES_PER_BLOCK;
199 int offset = index % CF_NUM_FILES_PER_BLOCK;
201 return &File_blocks[block]->files[offset];
204 // Create a new file and return a pointer to it.
205 cf_file *cf_create_file()
207 int block = Num_files / CF_NUM_FILES_PER_BLOCK;
208 int offset = Num_files % CF_NUM_FILES_PER_BLOCK;
210 if ( File_blocks[block] == NULL ) {
211 File_blocks[block] = (cf_file_block *)malloc( sizeof(cf_file_block) );
212 Assert( File_blocks[block] != NULL);
217 return &File_blocks[block]->files[offset];
220 extern int cfile_inited;
222 // Create a new root and return a pointer to it. The structure is assumed unitialized.
223 cf_root *cf_get_root(int n)
225 int block = n / CF_NUM_ROOTS_PER_BLOCK;
226 int offset = n % CF_NUM_ROOTS_PER_BLOCK;
231 return &Root_blocks[block]->roots[offset];
235 // Create a new root and return a pointer to it. The structure is assumed unitialized.
236 cf_root *cf_create_root()
238 int block = Num_roots / CF_NUM_ROOTS_PER_BLOCK;
239 int offset = Num_roots % CF_NUM_ROOTS_PER_BLOCK;
241 if ( Root_blocks[block] == NULL ) {
242 Root_blocks[block] = (cf_root_block *)malloc( sizeof(cf_root_block) );
243 Assert(Root_blocks[block] != NULL);
248 return &Root_blocks[block]->roots[offset];
251 // return the # of packfiles which exist
252 int cf_get_packfile_count(cf_root *root)
254 char filespec[MAX_PATH_LEN];
258 // count up how many packfiles we're gonna have
260 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
262 strcpy( filespec, root->path );
264 if(strlen(Pathtypes[i].path)){
265 strcat( filespec, Pathtypes[i].path );
266 strcat( filespec, "/" );
272 dirp = opendir (filespec);
274 while ((dir = readdir (dirp)) != NULL)
276 if (!fnmatch ("*.vp", dir->d_name, 0))
282 strcpy( filespec, root->path );
284 if(strlen(Pathtypes[i].path)){
285 strcat( filespec, Pathtypes[i].path );
286 strcat( filespec, "\\" );
289 strcat( filespec, "*.vp" );
294 find_handle = _findfirst( filespec, &find );
296 if (find_handle != -1) {
298 if (!(find.attrib & _A_SUBDIR)) {
302 } while (!_findnext(find_handle, &find));
304 _findclose( find_handle );
309 return packfile_count;
312 // packfile sort function
313 int cf_packfile_sort_func(const void *elem1, const void *elem2)
315 cf_root_sort *r1, *r2;
316 r1 = (cf_root_sort*)elem1;
317 r2 = (cf_root_sort*)elem2;
319 // if the 2 directory types are the same, do a string compare
320 if(r1->cf_type == r2->cf_type){
321 return stricmp(r1->path, r2->path);
324 // otherwise return them in order of CF_TYPE_* precedence
325 return (r1->cf_type < r2->cf_type) ? -1 : 1;
328 // Go through a root and look for pack files
329 void cf_build_pack_list( cf_root *root )
331 char filespec[MAX_PATH_LEN];
333 cf_root_sort *temp_roots_sort, *rptr_sort;
334 int temp_root_count, root_index;
336 // determine how many packfiles there are
337 temp_root_count = cf_get_packfile_count(root);
338 if(temp_root_count <= 0){
342 // allocate a temporary array of temporary roots so we can easily sort them
343 temp_roots_sort = (cf_root_sort*)malloc(sizeof(cf_root_sort) * temp_root_count);
344 if(temp_roots_sort == NULL){
349 // now just setup all the root info
351 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
354 strcpy( filespec, root->path );
356 if(strlen(Pathtypes[i].path)){
357 strcat( filespec, Pathtypes[i].path );
358 strcat( filespec, "/" );
364 dirp = opendir (filespec);
366 while ((dir = readdir (dirp)) != NULL)
368 if (!fnmatch ("*.vp", dir->d_name, 0))
370 Assert(root_index < temp_root_count);
373 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
377 if (stat(fn, &buf) == -1) {
381 if (!S_ISREG(buf.st_mode)) {
385 // get a temp pointer
386 rptr_sort = &temp_roots_sort[root_index++];
388 // fill in all the proper info
389 strcpy(rptr_sort->path, root->path);
391 if(strlen(Pathtypes[i].path)){
392 strcat(rptr_sort->path, Pathtypes[i].path );
393 strcat(rptr_sort->path, "/");
396 strcat(rptr_sort->path, dir->d_name );
397 rptr_sort->roottype = CF_ROOTTYPE_PACK;
398 rptr_sort->cf_type = i;
404 strcpy( filespec, root->path );
406 if(strlen(Pathtypes[i].path)){
407 strcat( filespec, Pathtypes[i].path );
408 strcat( filespec, "\\" );
410 strcat( filespec, "*.vp" );
414 find_handle = _findfirst( filespec, &find );
416 if (find_handle != -1) {
419 if (!(find.attrib & _A_SUBDIR)) {
420 Assert(root_index < temp_root_count);
422 // get a temp pointer
423 rptr_sort = &temp_roots_sort[root_index++];
425 // fill in all the proper info
426 strcpy(rptr_sort->path, root->path);
428 if(strlen(Pathtypes[i].path)){
429 strcat(rptr_sort->path, Pathtypes[i].path );
430 strcat(rptr_sort->path, "\\");
433 strcat(rptr_sort->path, find.name );
434 rptr_sort->roottype = CF_ROOTTYPE_PACK;
435 rptr_sort->cf_type = i;
438 } while (!_findnext(find_handle, &find));
440 _findclose( find_handle );
445 // these should always be the same
446 Assert(root_index == temp_root_count);
449 qsort(temp_roots_sort, temp_root_count, sizeof(cf_root_sort), cf_packfile_sort_func);
451 // now insert them all into the real root list properly
453 for(i=0; i<temp_root_count; i++){
454 new_root = cf_create_root();
455 strcpy( new_root->path, root->path );
457 // mwa -- 4/2/98 put in the next 2 lines because the path name needs to be there
458 // to find the files.
459 strcpy(new_root->path, temp_roots_sort[i].path);
460 new_root->roottype = CF_ROOTTYPE_PACK;
463 // free up the temp list
464 free(temp_roots_sort);
468 void cf_build_root_list(char *cdrom_dir)
475 // ================================================================
476 // use users HOME directory as default for loading and saving files
477 root = cf_create_root();
478 strcpy( root->path, Cfile_user_dir );
480 // do we already have a slash? as in the case of a root directory install
481 if(strlen(root->path) && (root->path[strlen(root->path)-1] != '/')){
482 strcat(root->path, "/"); // put trailing backslash on for easier path construction
484 root->roottype = CF_ROOTTYPE_PATH;
486 //======================================================
487 // Next, check any VP files under the current directory.
488 cf_build_pack_list(root);
491 //======================================================
492 // First, check the current directory.
493 // strcpy( root->path, "d:\\projects\\freespace\\" );
495 root = cf_create_root();
497 if ( !_getcwd(root->path, CF_MAX_PATHNAME_LENGTH ) ) {
498 Error(LOCATION, "Can't get current working directory -- %d", errno );
501 // do we already have a slash? as in the case of a root directory install
503 if(strlen(root->path) && (root->path[strlen(root->path)-1] != '/')){
504 strcat(root->path, "/"); // put trailing backslash on for easier path construction
506 if(strlen(root->path) && (root->path[strlen(root->path)-1] != '\\')){
507 strcat(root->path, "\\"); // put trailing backslash on for easier path construction
510 root->roottype = CF_ROOTTYPE_PATH;
512 //======================================================
513 // Next, check any VP files under the current directory.
514 cf_build_pack_list(root);
517 //======================================================
518 // Check the real CD if one...
519 if ( cdrom_dir && strlen(cdrom_dir) ) {
520 root = cf_create_root();
521 strcpy( root->path, cdrom_dir );
522 root->roottype = CF_ROOTTYPE_PATH;
524 //======================================================
525 // Next, check any VP files in the CD-ROM directory.
526 cf_build_pack_list(root);
532 // Given a lower case list of file extensions
533 // separated by spaces, return zero if ext is
535 int is_ext_in_list( char *ext_list, char *ext )
539 strncpy( tmp_ext, ext, 127 );
541 if ( strstr(ext_list, tmp_ext )) {
548 void cf_search_root_path(int root_index)
552 cf_root *root = cf_get_root(root_index);
554 mprintf(( "Searching root '%s'\n", root->path ));
556 char search_path[CF_MAX_PATHNAME_LENGTH];
558 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
564 strcpy( search_path, root->path );
566 if(strlen(Pathtypes[i].path)){
567 strcat( search_path, Pathtypes[i].path );
568 strcat( search_path, "/" );
571 dirp = opendir (search_path);
573 while ((dir = readdir (dirp)) != NULL)
575 if (!fnmatch ("*.*", dir->d_name, 0))
578 snprintf(fn, MAX_PATH-1, "%s/%s", search_path, dir->d_name);
582 if (stat(fn, &buf) == -1) {
586 if (!S_ISREG(buf.st_mode)) {
590 char *ext = strchr( dir->d_name, '.' );
592 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) {
594 cf_file *file = cf_create_file();
596 strcpy( file->name_ext, dir->d_name );
597 file->root_index = root_index;
598 file->pathtype_index = i;
601 file->write_time = buf.st_mtime;
602 file->size = buf.st_size;
604 file->pack_offset = 0; // Mark as a non-packed file
606 //mprintf(( "Found file '%s'\n", file->name_ext ));
614 strcpy( search_path, root->path );
616 if(strlen(Pathtypes[i].path)){
617 strcat( search_path, Pathtypes[i].path );
618 strcat( search_path, "\\" );
621 strcat( search_path, "*.*" );
626 find_handle = _findfirst( search_path, &find );
628 if (find_handle != -1) {
630 if (!(find.attrib & _A_SUBDIR)) {
632 char *ext = strchr( find.name, '.' );
634 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) {
636 cf_file *file = cf_create_file();
638 strcpy( file->name_ext, find.name );
639 file->root_index = root_index;
640 file->pathtype_index = i;
641 file->write_time = find.time_write;
642 file->size = find.size;
643 file->pack_offset = 0; // Mark as a non-packed file
645 //mprintf(( "Found file '%s'\n", file->name_ext ));
652 } while (!_findnext(find_handle, &find));
654 _findclose( find_handle );
662 typedef struct VP_FILE_HEADER {
669 typedef struct VP_FILE {
676 void cf_search_root_pack(int root_index)
680 cf_root *root = cf_get_root(root_index);
682 //mprintf(( "Searching root pack '%s'\n", root->path ));
686 FILE *fp = fopen( root->path, "rb" );
687 // Read the file header
692 VP_FILE_HEADER VP_header;
694 Assert( sizeof(VP_header) == 16 );
695 fread(&VP_header, 1, sizeof(VP_header), fp);
698 fseek(fp, VP_header.index_offset, SEEK_SET);
700 char search_path[CF_MAX_PATHNAME_LENGTH];
702 strcpy( search_path, "" );
704 // Go through all the files
705 for (i=0; i<VP_header.num_files; i++ ) {
708 fread( &find, sizeof(VP_FILE), 1, fp );
710 if ( find.size == 0 ) {
711 if ( !stricmp( find.filename, ".." )) {
712 int l = strlen(search_path);
713 char *p = &search_path[l-1];
715 while( (p > search_path) && (*p != '/') ) {
717 while( (p > search_path) && (*p != '\\') ) {
723 if ( strlen(search_path) ) {
725 strcat( search_path, "/" );
727 strcat( search_path, "\\" );
730 strcat( search_path, find.filename );
733 //mprintf(( "Current dir = '%s'\n", search_path ));
737 for (j=CF_TYPE_ROOT; j<CF_MAX_PATH_TYPES; j++ ) {
739 if ( !stricmp( search_path, Pathtypes[j].path )) {
741 char *ext = strchr( find.filename, '.' );
743 if ( is_ext_in_list( Pathtypes[j].extensions, ext ) ) {
745 cf_file *file = cf_create_file();
747 strcpy( file->name_ext, find.filename );
748 file->root_index = root_index;
749 file->pathtype_index = j;
750 file->write_time = find.write_time;
751 file->size = find.size;
752 file->pack_offset = find.offset; // Mark as a non-packed file
754 //mprintf(( "Found pack file '%s'\n", file->name_ext ));
768 void cf_build_file_list()
774 // For each root, find all files...
775 for (i=1; i<Num_roots; i++ ) {
776 cf_root *root = cf_get_root(i);
777 if ( root->roottype == CF_ROOTTYPE_PATH ) {
778 cf_search_root_path(i);
779 } else if ( root->roottype == CF_ROOTTYPE_PACK ) {
780 cf_search_root_pack(i);
787 void cf_build_secondary_filelist(char *cdrom_dir)
795 // Init the path types
796 for (i=0; i<CF_MAX_PATH_TYPES; i++ ) {
797 Assert( Pathtypes[i].index == i );
798 #if 0 /* they are already lowercased -- SBF */
799 if ( Pathtypes[i].extensions ) {
800 strlwr(Pathtypes[i].extensions);
805 // Init the root blocks
806 for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ ) {
807 Root_blocks[i] = NULL;
810 // Init the file blocks
811 for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
812 File_blocks[i] = NULL;
815 mprintf(( "Building file index...\n" ));
817 // build the list of searchable roots
818 cf_build_root_list(cdrom_dir);
820 // build the list of files themselves
821 cf_build_file_list();
823 mprintf(( "Found %d roots and %d files.\n", Num_roots, Num_files ));
826 void cf_free_secondary_filelist()
830 // Free the root blocks
831 for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ ) {
832 if ( Root_blocks[i] ) {
833 free( Root_blocks[i] );
834 Root_blocks[i] = NULL;
839 // Init the file blocks
840 for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
841 if ( File_blocks[i] ) {
842 free( File_blocks[i] );
843 File_blocks[i] = NULL;
849 // Searches for a file. Follows all rules and precedence and searches
850 // CD's and pack files.
851 // Input: filespace - Filename & extension
852 // pathtype - See CF_TYPE_ defines in CFILE.H
853 // Output: pack_filename - Absolute path and filename of this file. Could be a packfile or the actual file.
855 // offset - Offset into pack file. 0 if not a packfile.
856 // Returns: If not found returns 0.
857 int cf_find_file_location( char *filespec, int pathtype, char *pack_filename, int *size, int *offset, bool localize )
861 Assert(filespec && strlen(filespec));
863 // see if we have something other than just a filename
864 // our current rules say that any file that specifies a direct
865 // path will try to be opened on that path. If that open
866 // fails, then we will open the file based on the extension
869 // NOTE: full path should also include localization, if so desired
870 if ( strpbrk(filespec,"/\\:") ) { // do we have a full path already?
871 FILE *fp = fopen(filespec, "rb" );
873 if ( size ) *size = filelength(fileno(fp));
874 if ( offset ) *offset = 0;
875 if ( pack_filename ) {
876 strcpy( pack_filename, filespec );
882 return 0; // If they give a full path, fail if not found.
885 // Search the hard drive for files first.
886 int num_search_dirs = 0;
887 int search_order[CF_MAX_PATH_TYPES];
889 if ( CF_TYPE_SPECIFIED(pathtype) ) {
890 search_order[num_search_dirs++] = pathtype;
893 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++) {
894 if ( i != pathtype ) {
895 search_order[num_search_dirs++] = i;
899 for (i=0; i<num_search_dirs; i++ ) {
900 char longname[MAX_PATH_LEN];
902 cf_create_default_path_string( longname, search_order[i], filespec, localize );
904 FILE *fp = fopen(longname, "rb" );
906 if ( size ) *size = filelength(fileno(fp));
907 if ( offset ) *offset = 0;
908 if ( pack_filename ) {
909 strcpy( pack_filename, longname );
917 // search the secondary directory (game directory) as well since the user dir is default
918 for (i=0; i<num_search_dirs; i++ ) {
919 char longname[MAX_PATH_LEN];
921 cf_create_secondary_path_string( longname, search_order[i], filespec, localize );
923 FILE *fp = fopen(longname, "rb" );
925 if ( size ) *size = filelength(fileno(fp));
926 if ( offset ) *offset = 0;
927 if ( pack_filename ) {
928 strcpy( pack_filename, longname );
936 // Search the pak files and CD-ROM.
938 for (i=0; i<Num_files; i++ ) {
939 cf_file * f = cf_get_file(i);
941 // only search paths we're supposed to...
942 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
947 // create localized filespec
948 char temp[MAX_PATH_LEN];
949 strcpy(temp, filespec);
950 lcl_add_dir_to_path_with_filename(filespec);
952 if ( !stricmp(filespec, f->name_ext) ) {
953 if ( size ) *size = f->size;
954 if ( offset ) *offset = f->pack_offset;
955 if ( pack_filename ) {
956 cf_root * r = cf_get_root(f->root_index);
958 strcpy( pack_filename, r->path );
959 if ( f->pack_offset < 1 ) {
960 strcat( pack_filename, Pathtypes[f->pathtype_index].path );
962 strcat( pack_filename, "/" );
964 strcat( pack_filename, "\\" );
966 strcat( pack_filename, f->name_ext );
971 // restore original filespec
972 strcpy(filespec, temp);
975 // file either not localized or localized version not found
976 if ( !stricmp(filespec, f->name_ext) ) {
977 if ( size ) *size = f->size;
978 if ( offset ) *offset = f->pack_offset;
979 if ( pack_filename ) {
980 cf_root * r = cf_get_root(f->root_index);
982 strcpy( pack_filename, r->path );
983 if ( f->pack_offset < 1 ) {
985 if(strlen(Pathtypes[f->pathtype_index].path)){
986 strcat( pack_filename, Pathtypes[f->pathtype_index].path );
988 strcat( pack_filename, "/" );
990 strcat( pack_filename, "\\" );
994 strcat( pack_filename, f->name_ext );
1005 // Returns true if filename matches filespec, else zero if not
1006 int cf_matches_spec(char *filespec, char *filename)
1008 char *src_ext, *dst_ext;
1010 src_ext = strchr(filespec, '.');
1013 if (*src_ext == '*')
1016 dst_ext = strchr(filename, '.');
1020 return !stricmp(dst_ext, src_ext);
1023 int (*Get_file_list_filter)(char *filename) = NULL;
1024 int Skip_packfile_search = 0;
1026 int cf_file_already_in_list( int num_files, char **list, char *filename )
1030 char name_no_extension[MAX_PATH_LEN];
1032 strcpy(name_no_extension, filename );
1033 char *p = strchr( name_no_extension, '.' );
1036 for (i=0; i<num_files; i++ ) {
1037 if ( !stricmp(list[i], name_no_extension ) ) {
1046 // An alternative cf_get_file_list(), dynamic list version.
1047 // This one has a 'type', which is a CF_TYPE_* value. Because this specifies the directory
1048 // location, 'filter' only needs to be the filter itself, with no path information.
1049 // See above descriptions of cf_get_file_list() for more information about how it all works.
1050 int cf_get_file_list( int max, char **list, int pathtype, char *filter, int sort, file_list_info *info )
1053 int i, l, num_files = 0, own_flag = 0;
1060 Get_file_list_filter = NULL;
1066 if (!info && (sort == CF_SORT_TIME)) {
1067 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1071 char filespec[MAX_PATH_LEN];
1074 cf_create_default_path_string( filespec, pathtype, NULL );
1079 dirp = opendir (filespec);
1081 while ((dir = readdir (dirp)) != NULL)
1083 if (num_files >= max)
1086 if (fnmatch(filter, dir->d_name, 0) != 0)
1090 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
1094 if (stat(fn, &buf) == -1) {
1098 if (!S_ISREG(buf.st_mode)) {
1102 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1103 ptr = strrchr(dir->d_name, '.');
1105 l = ptr - dir->d_name;
1107 l = strlen(dir->d_name);
1109 list[num_files] = (char *)malloc(l + 1);
1110 strncpy(list[num_files], dir->d_name, l);
1111 list[num_files][l] = 0;
1113 info[num_files].write_time = buf.st_mtime;
1122 // grab secondary (game) directory as well but be sure to skip the pilot
1123 // directories as that would be a bad thing to load read-only pilots
1124 if (pathtype != (CF_TYPE_PLAYERS || CF_TYPE_SINGLE_PLAYERS || CF_TYPE_MULTI_PLAYERS)) {
1125 cf_create_secondary_path_string( filespec, pathtype, NULL );
1130 dirp = opendir (filespec);
1132 while ((dir = readdir (dirp)) != NULL)
1134 if (num_files >= max)
1137 if (fnmatch(filter, dir->d_name, 0) != 0)
1141 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
1145 if (stat(fn, &buf) == -1) {
1149 if (!S_ISREG(buf.st_mode)) {
1153 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1154 ptr = strrchr(dir->d_name, '.');
1156 l = ptr - dir->d_name;
1158 l = strlen(dir->d_name);
1160 list[num_files] = (char *)malloc(l + 1);
1161 strncpy(list[num_files], dir->d_name, l);
1162 list[num_files][l] = 0;
1164 info[num_files].write_time = buf.st_mtime;
1174 cf_create_default_path_string( filespec, pathtype, filter );
1176 find_handle = _findfirst( filespec, &find );
1177 if (find_handle != -1) {
1179 if (num_files >= max)
1182 if (!(find.attrib & _A_SUBDIR)) {
1183 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1184 ptr = strrchr(find.name, '.');
1186 l = ptr - find.name;
1188 l = strlen(find.name);
1190 list[num_files] = (char *)malloc(l + 1);
1191 strncpy(list[num_files], find.name, l);
1192 list[num_files][l] = 0;
1194 info[num_files].write_time = find.time_write;
1200 } while (!_findnext(find_handle, &find));
1202 _findclose( find_handle );
1207 // Search all the packfiles and CD.
1208 if ( !Skip_packfile_search ) {
1209 for (i=0; i<Num_files; i++ ) {
1210 cf_file * f = cf_get_file(i);
1212 // only search paths we're supposed to...
1213 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
1217 if (num_files >= max)
1220 if ( !cf_matches_spec( filter,f->name_ext)) {
1224 if ( cf_file_already_in_list(num_files,list,f->name_ext)) {
1228 if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1230 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1232 ptr = strrchr(f->name_ext, '.');
1234 l = ptr - f->name_ext;
1236 l = strlen(f->name_ext);
1238 list[num_files] = (char *)malloc(l + 1);
1239 strncpy(list[num_files], f->name_ext, l);
1240 list[num_files][l] = 0;
1243 info[num_files].write_time = f->write_time;
1253 if (sort != CF_SORT_NONE) {
1254 cf_sort_filenames( num_files, list, sort, info );
1261 Get_file_list_filter = NULL;
1265 int cf_file_already_in_list_preallocated( int num_files, char arr[][MAX_FILENAME_LEN], char *filename )
1269 char name_no_extension[MAX_PATH_LEN];
1271 strcpy(name_no_extension, filename );
1272 char *p = strchr( name_no_extension, '.' );
1275 for (i=0; i<num_files; i++ ) {
1276 if ( !stricmp(arr[i], name_no_extension ) ) {
1285 // An alternative cf_get_file_list(), fixed array version.
1286 // This one has a 'type', which is a CF_TYPE_* value. Because this specifies the directory
1287 // location, 'filter' only needs to be the filter itself, with no path information.
1288 // See above descriptions of cf_get_file_list() for more information about how it all works.
1289 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 )
1291 int i, num_files = 0, own_flag = 0;
1294 Get_file_list_filter = NULL;
1299 for (i=0; i<max; i++) {
1303 sort = CF_SORT_NONE; // sorting of array directly not supported. Sorting done on list only
1306 if (!info && (sort == CF_SORT_TIME)) {
1307 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1311 char filespec[MAX_PATH_LEN];
1313 // Search the default directories
1315 cf_create_default_path_string( filespec, pathtype, NULL );
1320 dirp = opendir (filespec);
1322 while ((dir = readdir (dirp)) != NULL)
1324 if (num_files >= max)
1327 if (fnmatch(filter, dir->d_name, 0) != 0)
1331 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
1335 if (stat(fn, &buf) == -1) {
1339 if (!S_ISREG(buf.st_mode)) {
1343 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1345 strncpy(arr[num_files], dir->d_name, MAX_FILENAME_LEN - 1 );
1346 char *ptr = strrchr(arr[num_files], '.');
1352 info[num_files].write_time = buf.st_mtime;
1361 // grab secondary (game) directory as well but be sure to skip the pilot
1362 // directories as that would be a bad thing to load read-only pilots
1363 if (pathtype != (CF_TYPE_PLAYERS || CF_TYPE_SINGLE_PLAYERS || CF_TYPE_MULTI_PLAYERS)) {
1364 cf_create_secondary_path_string( filespec, pathtype, NULL );
1369 dirp = opendir (filespec);
1371 while ((dir = readdir (dirp)) != NULL)
1373 if (num_files >= max)
1376 if (fnmatch(filter, dir->d_name, 0) != 0)
1380 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
1384 if (stat(fn, &buf) == -1) {
1388 if (!S_ISREG(buf.st_mode)) {
1392 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1394 strncpy(arr[num_files], dir->d_name, MAX_FILENAME_LEN - 1 );
1395 char *ptr = strrchr(arr[num_files], '.');
1401 info[num_files].write_time = buf.st_mtime;
1411 cf_create_default_path_string( filespec, pathtype, filter );
1416 find_handle = _findfirst( filespec, &find );
1417 if (find_handle != -1) {
1419 if (num_files >= max)
1422 if (!(find.attrib & _A_SUBDIR)) {
1424 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1426 strncpy(arr[num_files], find.name, MAX_FILENAME_LEN - 1 );
1427 char *ptr = strrchr(arr[num_files], '.');
1433 info[num_files].write_time = find.time_write;
1440 } while (!_findnext(find_handle, &find));
1442 _findclose( find_handle );
1447 // Search all the packfiles and CD.
1448 if ( !Skip_packfile_search ) {
1449 for (i=0; i<Num_files; i++ ) {
1450 cf_file * f = cf_get_file(i);
1452 // only search paths we're supposed to...
1453 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
1457 if (num_files >= max)
1460 if ( !cf_matches_spec( filter,f->name_ext)) {
1464 if ( cf_file_already_in_list_preallocated( num_files, arr, f->name_ext )) {
1468 if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1470 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1472 strncpy(arr[num_files], f->name_ext, MAX_FILENAME_LEN - 1 );
1473 char *ptr = strrchr(arr[num_files], '.');
1479 info[num_files].write_time = f->write_time;
1488 if (sort != CF_SORT_NONE) {
1490 cf_sort_filenames( num_files, list, sort, info );
1497 Get_file_list_filter = NULL;
1501 // Returns the default storage path for files given a
1502 // particular pathtype. In other words, the path to
1503 // the unpacked, non-cd'd, stored on hard drive path.
1504 // If filename isn't null it will also tack the filename
1505 // on the end, creating a completely valid filename.
1506 // Input: pathtype - CF_TYPE_??
1507 // filename - optional, if set, tacks the filename onto end of path.
1508 // Output: path - Fully qualified pathname.
1509 void cf_create_default_path_string( char *path, int pathtype, char *filename, bool localize )
1512 if ( filename && strpbrk(filename,"/") ) {
1514 if ( filename && strpbrk(filename,"/\\:") ) {
1516 // Already has full path
1517 strcpy( path, filename );
1520 cf_root *root = cf_get_root(0);
1523 strcpy(path, filename);
1527 Assert(CF_TYPE_SPECIFIED(pathtype));
1529 strcpy(path, root->path);
1530 strcat(path, Pathtypes[pathtype].path);
1532 // Don't add slash for root directory
1533 if (Pathtypes[pathtype].path[0] != '\0') {
1543 strcat(path, filename);
1545 // localize filename
1547 // create copy of path
1548 char temp_path[MAX_PATH_LEN];
1549 strcpy(temp_path, path);
1551 // localize the path
1552 lcl_add_dir_to_path_with_filename(path);
1554 // verify localized path
1555 FILE *fp = fopen(path, "rb");
1559 strcpy(path, temp_path);
1567 // this is the same as cf_create_default_path_string above but as it only shows
1568 // files in the users directory this function will find files in the game
1569 // installation directory
1570 void cf_create_secondary_path_string( char *path, int pathtype, char *filename, bool localize )
1572 if ( filename && strpbrk(filename,"/") ) {
1574 // Already has full path
1575 strcpy( path, filename );
1578 cf_root *root = cf_get_root(1);
1581 strcpy(path, filename);
1585 Assert(CF_TYPE_SPECIFIED(pathtype));
1587 strcpy(path, root->path);
1588 strcat(path, Pathtypes[pathtype].path);
1590 // Don't add slash for root directory
1591 if (Pathtypes[pathtype].path[0] != '\0') {
1597 strcat(path, filename);
1599 // localize filename
1601 // create copy of path
1602 char temp_path[MAX_PATH_LEN];
1603 strcpy(temp_path, path);
1605 // localize the path
1606 lcl_add_dir_to_path_with_filename(path);
1608 // verify localized path
1609 FILE *fp = fopen(path, "rb");
1613 strcpy(path, temp_path);