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.11 2004/07/04 11:27:29 taylor
23 * cleanup CFILE code a little, warning fixes, remove redundant dir checks, amd64 support
25 * Revision 1.10 2004/06/11 00:29:22 tigital
26 * byte-swapping changes for bigendian systems
28 * Revision 1.9 2003/05/27 03:03:11 taylor
29 * fix second root (gamedir) searching
31 * Revision 1.8 2003/02/20 17:41:07 theoddone33
32 * Userdir patch from Taylor Richards
34 * Revision 1.7 2002/06/22 23:57:39 relnev
35 * remove writable strings.
37 * fix compile for intel compiler.
39 * Revision 1.6 2002/06/09 04:41:15 relnev
40 * added copyright header
42 * Revision 1.5 2002/06/05 04:03:32 relnev
43 * finished cfilesystem.
45 * removed some old code.
47 * fixed mouse save off-by-one.
51 * Revision 1.4 2002/05/28 17:26:57 theoddone33
52 * Fill in some timer and palette setting stubs. Still no display
54 * Revision 1.3 2002/05/28 06:45:38 theoddone33
57 * Revision 1.2 2002/05/28 06:28:20 theoddone33
58 * Filesystem mods, actually reads some data files now
60 * Revision 1.1.1.1 2002/05/03 03:28:08 root
64 * 6 9/08/99 10:01p Dave
65 * Make sure game won't run in a drive's root directory. Make sure
66 * standalone routes suqad war messages properly to the host.
68 * 5 9/03/99 1:31a Dave
69 * CD checking by act. Added support to play 2 cutscenes in a row
70 * seamlessly. Fixed super low level cfile bug related to files in the
71 * root directory of a CD. Added cheat code to set campaign mission # in
74 * 4 2/22/99 10:31p Andsager
75 * Get rid of unneeded includes.
77 * 3 10/13/98 9:19a Andsager
78 * Add localization support to cfile. Optional parameter with cfopen that
79 * looks for localized files.
81 * 2 10/07/98 10:52a Dave
84 * 1 10/07/98 10:48a Dave
86 * 14 8/31/98 2:06p Dave
87 * Make cfile sort the ordering or vp files. Added support/checks for
88 * recognizing "mission disk" players.
90 * 13 6/23/98 4:18p Hoffoss
91 * Fixed some bugs with AC release build.
93 * 12 5/20/98 10:46p John
94 * Added code that doesn't include duplicate filenames in any file list
97 * 11 5/14/98 2:14p Lawrance2
98 * Use filespec filtering for packfiles
100 * 10 5/03/98 11:53a John
101 * Fixed filename case mangling.
103 * 9 5/02/98 11:06p Allender
104 * correctly deal with pack pathnames
106 * 8 5/01/98 11:41a Allender
107 * Fixed bug with mission saving in Fred.
109 * 7 5/01/98 10:21a John
110 * Added code to find all pack files in all trees. Added code to create
111 * any directories that we write to.
113 * 6 4/30/98 10:21p John
114 * Added code to cleanup cfilesystem
116 * 5 4/30/98 10:18p John
117 * added source safe header
130 #include <winbase.h> /* needed for memory mapping of file functions */
132 #include <sys/types.h>
135 #include <sys/stat.h>
140 //#include "outwnd.h"
141 //#include "vecmat.h"
144 #include "cfilesystem.h"
145 #include "localize.h"
148 #define CF_ROOTTYPE_PATH 0
149 #define CF_ROOTTYPE_PACK 1
152 // specifying hard drive tree
153 // searching for pack files on hard drive // Found by searching all known paths
154 // specifying cd-rom tree
155 // searching for pack files on CD-rom tree
156 typedef struct cf_root {
157 char path[CF_MAX_PATHNAME_LENGTH]; // Contains something like c:\projects\freespace or c:\projects\freespace\freespace.vp
158 int roottype; // CF_ROOTTYPE_PATH = Path, CF_ROOTTYPE_PACK =Pack file
161 // convenient type for sorting (see cf_build_pack_list())
162 typedef struct cf_root_sort {
163 char path[CF_MAX_PATHNAME_LENGTH];
168 #define CF_NUM_ROOTS_PER_BLOCK 32
169 #define CF_MAX_ROOT_BLOCKS 256 // Can store 32*256 = 8192 Roots
170 #define CF_MAX_ROOTS (CF_NUM_ROOTS_PER_BLOCK * CF_MAX_ROOT_BLOCKS)
172 typedef struct cf_root_block {
173 cf_root roots[CF_NUM_ROOTS_PER_BLOCK];
176 static int Num_roots = 0;
177 static cf_root_block *Root_blocks[CF_MAX_ROOT_BLOCKS];
180 // Created by searching all roots in order. This means Files is then sorted by precedence.
181 typedef struct cf_file {
182 char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension
183 int root_index; // Where in Roots this is located
184 int pathtype_index; // Where in Paths this is located
185 time_t write_time; // When it was last written
186 int size; // How big it is in bytes
187 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.
190 #define CF_NUM_FILES_PER_BLOCK 256
191 #define CF_MAX_FILE_BLOCKS 128 // Can store 256*128 = 32768 files
193 typedef struct cf_file_block {
194 cf_file files[CF_NUM_FILES_PER_BLOCK];
197 static int Num_files = 0;
198 static cf_file_block *File_blocks[CF_MAX_FILE_BLOCKS];
201 // Return a pointer to to file 'index'.
202 cf_file *cf_get_file(int index)
204 int block = index / CF_NUM_FILES_PER_BLOCK;
205 int offset = index % CF_NUM_FILES_PER_BLOCK;
207 return &File_blocks[block]->files[offset];
210 // Create a new file and return a pointer to it.
211 cf_file *cf_create_file()
213 int block = Num_files / CF_NUM_FILES_PER_BLOCK;
214 int offset = Num_files % CF_NUM_FILES_PER_BLOCK;
216 if ( File_blocks[block] == NULL ) {
217 File_blocks[block] = (cf_file_block *)malloc( sizeof(cf_file_block) );
218 Assert( File_blocks[block] != NULL);
223 return &File_blocks[block]->files[offset];
226 extern int cfile_inited;
228 // Create a new root and return a pointer to it. The structure is assumed unitialized.
229 cf_root *cf_get_root(int n)
231 int block = n / CF_NUM_ROOTS_PER_BLOCK;
232 int offset = n % CF_NUM_ROOTS_PER_BLOCK;
237 return &Root_blocks[block]->roots[offset];
241 // Create a new root and return a pointer to it. The structure is assumed unitialized.
242 cf_root *cf_create_root()
244 int block = Num_roots / CF_NUM_ROOTS_PER_BLOCK;
245 int offset = Num_roots % CF_NUM_ROOTS_PER_BLOCK;
247 if ( Root_blocks[block] == NULL ) {
248 Root_blocks[block] = (cf_root_block *)malloc( sizeof(cf_root_block) );
249 Assert(Root_blocks[block] != NULL);
254 return &Root_blocks[block]->roots[offset];
257 // return the # of packfiles which exist
258 int cf_get_packfile_count(cf_root *root)
260 char filespec[MAX_PATH_LEN];
264 // count up how many packfiles we're gonna have
266 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
268 strcpy( filespec, root->path );
270 if(strlen(Pathtypes[i].path)){
271 strcat( filespec, Pathtypes[i].path );
272 strcat( filespec, "/" );
278 dirp = opendir (filespec);
280 while ((dir = readdir (dirp)) != NULL)
282 if (!fnmatch ("*.vp", dir->d_name, 0))
288 strcpy( filespec, root->path );
290 if(strlen(Pathtypes[i].path)){
291 strcat( filespec, Pathtypes[i].path );
292 strcat( filespec, "\\" );
295 strcat( filespec, "*.vp" );
300 find_handle = _findfirst( filespec, &find );
302 if (find_handle != -1) {
304 if (!(find.attrib & _A_SUBDIR)) {
308 } while (!_findnext(find_handle, &find));
310 _findclose( find_handle );
315 return packfile_count;
318 // packfile sort function
319 int cf_packfile_sort_func(const void *elem1, const void *elem2)
321 cf_root_sort *r1, *r2;
322 r1 = (cf_root_sort*)elem1;
323 r2 = (cf_root_sort*)elem2;
325 // if the 2 directory types are the same, do a string compare
326 if(r1->cf_type == r2->cf_type){
327 return stricmp(r1->path, r2->path);
330 // otherwise return them in order of CF_TYPE_* precedence
331 return (r1->cf_type < r2->cf_type) ? -1 : 1;
334 // Go through a root and look for pack files
335 void cf_build_pack_list( cf_root *root )
337 char filespec[MAX_PATH_LEN];
339 cf_root_sort *temp_roots_sort, *rptr_sort;
340 int temp_root_count, root_index;
342 // determine how many packfiles there are
343 temp_root_count = cf_get_packfile_count(root);
344 if(temp_root_count <= 0){
348 // allocate a temporary array of temporary roots so we can easily sort them
349 temp_roots_sort = (cf_root_sort*)malloc(sizeof(cf_root_sort) * temp_root_count);
350 if(temp_roots_sort == NULL){
355 // now just setup all the root info
357 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
360 strcpy( filespec, root->path );
362 if(strlen(Pathtypes[i].path)){
363 strcat( filespec, Pathtypes[i].path );
364 strcat( filespec, "/" );
370 dirp = opendir (filespec);
372 while ((dir = readdir (dirp)) != NULL)
374 if (!fnmatch ("*.vp", dir->d_name, 0))
376 Assert(root_index < temp_root_count);
379 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
383 if (stat(fn, &buf) == -1) {
387 if (!S_ISREG(buf.st_mode)) {
391 // get a temp pointer
392 rptr_sort = &temp_roots_sort[root_index++];
394 // fill in all the proper info
395 strcpy(rptr_sort->path, root->path);
397 if(strlen(Pathtypes[i].path)){
398 strcat(rptr_sort->path, Pathtypes[i].path );
399 strcat(rptr_sort->path, "/");
402 strcat(rptr_sort->path, dir->d_name );
403 rptr_sort->roottype = CF_ROOTTYPE_PACK;
404 rptr_sort->cf_type = i;
410 strcpy( filespec, root->path );
412 if(strlen(Pathtypes[i].path)){
413 strcat( filespec, Pathtypes[i].path );
414 strcat( filespec, "\\" );
416 strcat( filespec, "*.vp" );
420 find_handle = _findfirst( filespec, &find );
422 if (find_handle != -1) {
425 if (!(find.attrib & _A_SUBDIR)) {
426 Assert(root_index < temp_root_count);
428 // get a temp pointer
429 rptr_sort = &temp_roots_sort[root_index++];
431 // fill in all the proper info
432 strcpy(rptr_sort->path, root->path);
434 if(strlen(Pathtypes[i].path)){
435 strcat(rptr_sort->path, Pathtypes[i].path );
436 strcat(rptr_sort->path, "\\");
439 strcat(rptr_sort->path, find.name );
440 rptr_sort->roottype = CF_ROOTTYPE_PACK;
441 rptr_sort->cf_type = i;
444 } while (!_findnext(find_handle, &find));
446 _findclose( find_handle );
451 // these should always be the same
452 Assert(root_index == temp_root_count);
455 qsort(temp_roots_sort, temp_root_count, sizeof(cf_root_sort), cf_packfile_sort_func);
457 // now insert them all into the real root list properly
459 for(i=0; i<temp_root_count; i++){
460 new_root = cf_create_root();
461 strcpy( new_root->path, root->path );
463 // mwa -- 4/2/98 put in the next 2 lines because the path name needs to be there
464 // to find the files.
465 strcpy(new_root->path, temp_roots_sort[i].path);
466 new_root->roottype = CF_ROOTTYPE_PACK;
469 // free up the temp list
470 free(temp_roots_sort);
474 void cf_build_root_list(char *cdrom_dir)
481 // ================================================================
482 // use users HOME directory as default for loading and saving files
483 root = cf_create_root();
484 strcpy( root->path, Cfile_user_dir );
486 // do we already have a slash? as in the case of a root directory install
487 if(strlen(root->path) && (root->path[strlen(root->path)-1] != '/')){
488 strcat(root->path, "/"); // put trailing backslash on for easier path construction
490 root->roottype = CF_ROOTTYPE_PATH;
492 //======================================================
493 // Next, check any VP files under the current directory.
494 cf_build_pack_list(root);
497 //======================================================
498 // First, check the current directory.
499 // strcpy( root->path, "d:\\projects\\freespace\\" );
501 root = cf_create_root();
503 if ( !_getcwd(root->path, CF_MAX_PATHNAME_LENGTH ) ) {
504 Error(LOCATION, "Can't get current working directory -- %d", errno );
507 // do we already have a slash? as in the case of a root directory install
508 if(strlen(root->path) && (root->path[strlen(root->path)-1] != DIR_SEPARATOR_CHAR)){
509 strcat(root->path, DIR_SEPARATOR_STR); // put trailing backslash on for easier path construction
511 root->roottype = CF_ROOTTYPE_PATH;
513 //======================================================
514 // Next, check any VP files under the current directory.
515 cf_build_pack_list(root);
518 //======================================================
519 // Check the real CD if one...
520 if ( cdrom_dir && strlen(cdrom_dir) ) {
521 root = cf_create_root();
522 strcpy( root->path, cdrom_dir );
523 root->roottype = CF_ROOTTYPE_PATH;
525 //======================================================
526 // Next, check any VP files in the CD-ROM directory.
527 cf_build_pack_list(root);
533 // Given a lower case list of file extensions
534 // separated by spaces, return zero if ext is
536 int is_ext_in_list( char *ext_list, char *ext )
540 strncpy( tmp_ext, ext, 127 );
542 if ( strstr(ext_list, tmp_ext )) {
549 void cf_search_root_path(int root_index)
553 cf_root *root = cf_get_root(root_index);
555 mprintf(( "Searching root '%s'\n", root->path ));
557 char search_path[CF_MAX_PATHNAME_LENGTH];
559 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
561 strcpy( search_path, root->path );
563 if(strlen(Pathtypes[i].path)){
564 strcat( search_path, Pathtypes[i].path );
565 strcat( search_path, DIR_SEPARATOR_STR );
572 // only get pilots from the primary root as there could be nasty
573 // permission issues otherwise
574 if ( (root_index > 0) && ((Pathtypes[i].index == CF_TYPE_SINGLE_PLAYERS)
575 || (Pathtypes[i].index == CF_TYPE_MULTI_PLAYERS)) ) {
579 dirp = opendir (search_path);
581 while ((dir = readdir (dirp)) != NULL)
583 if (!fnmatch ("*.*", dir->d_name, 0))
586 snprintf(fn, MAX_PATH-1, "%s/%s", search_path, dir->d_name);
590 if (stat(fn, &buf) == -1) {
594 if (!S_ISREG(buf.st_mode)) {
598 char *ext = strchr( dir->d_name, '.' );
600 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) {
602 cf_file *file = cf_create_file();
604 strcpy( file->name_ext, dir->d_name );
605 file->root_index = root_index;
606 file->pathtype_index = i;
609 file->write_time = buf.st_mtime;
610 file->size = buf.st_size;
612 file->pack_offset = 0; // Mark as a non-packed file
614 //mprintf(( "Found file '%s'\n", file->name_ext ));
622 strcat( search_path, "*.*" );
627 find_handle = _findfirst( search_path, &find );
629 if (find_handle != -1) {
631 if (!(find.attrib & _A_SUBDIR)) {
633 char *ext = strchr( find.name, '.' );
635 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) {
637 cf_file *file = cf_create_file();
639 strcpy( file->name_ext, find.name );
640 file->root_index = root_index;
641 file->pathtype_index = i;
642 file->write_time = find.time_write;
643 file->size = find.size;
644 file->pack_offset = 0; // Mark as a non-packed file
646 //mprintf(( "Found file '%s'\n", file->name_ext ));
653 } while (!_findnext(find_handle, &find));
655 _findclose( find_handle );
663 typedef struct VP_FILE_HEADER {
670 typedef struct VP_FILE {
674 fs_time_t write_time;
677 void cf_search_root_pack(int root_index)
681 cf_root *root = cf_get_root(root_index);
683 //mprintf(( "Searching root pack '%s'\n", root->path ));
687 FILE *fp = fopen( root->path, "rb" );
688 // Read the file header
693 VP_FILE_HEADER VP_header;
695 Assert( sizeof(VP_header) == 16 );
696 fread(&VP_header, 1, sizeof(VP_header), fp);
698 VP_header.version = INTEL_INT( VP_header.version);
699 VP_header.index_offset = INTEL_INT( VP_header.index_offset);
700 VP_header.num_files = INTEL_INT( VP_header.num_files);
703 fseek(fp, VP_header.index_offset, SEEK_SET);
705 char search_path[CF_MAX_PATHNAME_LENGTH];
707 strcpy( search_path, "" );
709 // Go through all the files
710 for (i=0; i<VP_header.num_files; i++ ) {
713 fread( &find, sizeof(VP_FILE), 1, fp );
715 find.offset = INTEL_INT( find.offset );
716 find.size = INTEL_INT( find.size );
717 find.write_time = INTEL_INT(find.write_time);
719 if ( find.size == 0 ) {
720 if ( !stricmp( find.filename, ".." )) {
721 int l = strlen(search_path);
722 char *p = &search_path[l-1];
723 while( (p > search_path) && (*p != DIR_SEPARATOR_CHAR) ) {
728 if ( strlen(search_path) ) {
729 strcat( search_path, DIR_SEPARATOR_STR );
731 strcat( search_path, find.filename );
734 //mprintf(( "Current dir = '%s'\n", search_path ));
738 for (j=CF_TYPE_ROOT; j<CF_MAX_PATH_TYPES; j++ ) {
740 if ( !stricmp( search_path, Pathtypes[j].path )) {
742 char *ext = strchr( find.filename, '.' );
744 if ( is_ext_in_list( Pathtypes[j].extensions, ext ) ) {
746 cf_file *file = cf_create_file();
748 strcpy( file->name_ext, find.filename );
749 file->root_index = root_index;
750 file->pathtype_index = j;
751 file->write_time = find.write_time;
752 file->size = find.size;
753 file->pack_offset = find.offset; // Mark as a non-packed file
755 //mprintf(( "Found pack file '%s'\n", file->name_ext ));
769 void cf_build_file_list()
775 // For each root, find all files...
776 for (i=1; i<Num_roots; i++ ) {
777 cf_root *root = cf_get_root(i);
778 if ( root->roottype == CF_ROOTTYPE_PATH ) {
779 cf_search_root_path(i);
780 } else if ( root->roottype == CF_ROOTTYPE_PACK ) {
781 cf_search_root_pack(i);
788 void cf_build_secondary_filelist(char *cdrom_dir)
796 // Init the path types
797 for (i=0; i<CF_MAX_PATH_TYPES; i++ ) {
798 Assert( Pathtypes[i].index == i );
799 #if 0 /* they are already lowercased -- SBF */
800 if ( Pathtypes[i].extensions ) {
801 strlwr(Pathtypes[i].extensions);
806 // Init the root blocks
807 for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ ) {
808 Root_blocks[i] = NULL;
811 // Init the file blocks
812 for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
813 File_blocks[i] = NULL;
816 mprintf(( "Building file index...\n" ));
818 // build the list of searchable roots
819 cf_build_root_list(cdrom_dir);
821 // build the list of files themselves
822 cf_build_file_list();
824 mprintf(( "Found %d roots and %d files.\n", Num_roots, Num_files ));
827 void cf_free_secondary_filelist()
831 // Free the root blocks
832 for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ ) {
833 if ( Root_blocks[i] ) {
834 free( Root_blocks[i] );
835 Root_blocks[i] = NULL;
840 // Init the file blocks
841 for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
842 if ( File_blocks[i] ) {
843 free( File_blocks[i] );
844 File_blocks[i] = NULL;
850 // Searches for a file. Follows all rules and precedence and searches
851 // CD's and pack files.
852 // Input: filespace - Filename & extension
853 // pathtype - See CF_TYPE_ defines in CFILE.H
854 // Output: pack_filename - Absolute path and filename of this file. Could be a packfile or the actual file.
856 // offset - Offset into pack file. 0 if not a packfile.
857 // Returns: If not found returns 0.
858 int cf_find_file_location( char *filespec, int pathtype, char *pack_filename, int *size, int *offset, bool localize )
862 Assert(filespec && strlen(filespec));
864 // see if we have something other than just a filename
865 // our current rules say that any file that specifies a direct
866 // path will try to be opened on that path. If that open
867 // fails, then we will open the file based on the extension
870 // NOTE: full path should also include localization, if so desired
872 if ( strpbrk(filespec, "/") ) {
874 if ( strpbrk(filespec,"/\\:") ) { // do we have a full path already?
876 FILE *fp = fopen(filespec, "rb" );
878 if ( size ) *size = filelength(fileno(fp));
879 if ( offset ) *offset = 0;
880 if ( pack_filename ) {
881 strcpy( pack_filename, filespec );
887 return 0; // If they give a full path, fail if not found.
890 // Search the hard drive for files first.
891 int num_search_dirs = 0;
892 int search_order[CF_MAX_PATH_TYPES];
894 if ( CF_TYPE_SPECIFIED(pathtype) ) {
895 search_order[num_search_dirs++] = pathtype;
898 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++) {
899 if ( i != pathtype ) {
900 search_order[num_search_dirs++] = i;
904 for (i=0; i<num_search_dirs; i++ ) {
905 char longname[MAX_PATH_LEN];
907 cf_create_default_path_string( longname, search_order[i], filespec, localize );
909 FILE *fp = fopen(longname, "rb" );
911 if ( size ) *size = filelength(fileno(fp));
912 if ( offset ) *offset = 0;
913 if ( pack_filename ) {
914 strcpy( pack_filename, longname );
921 // Search the pak files and CD-ROM.
923 for (i=0; i<Num_files; i++ ) {
924 cf_file * f = cf_get_file(i);
926 // only search paths we're supposed to...
927 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
932 // create localized filespec
933 char temp[MAX_PATH_LEN];
934 strcpy(temp, filespec);
935 lcl_add_dir_to_path_with_filename(filespec);
937 if ( !stricmp(filespec, f->name_ext) ) {
938 if ( size ) *size = f->size;
939 if ( offset ) *offset = f->pack_offset;
940 if ( pack_filename ) {
941 cf_root * r = cf_get_root(f->root_index);
943 strcpy( pack_filename, r->path );
944 if ( f->pack_offset < 1 ) {
945 strcat( pack_filename, Pathtypes[f->pathtype_index].path );
946 strcat( pack_filename, DIR_SEPARATOR_STR );
947 strcat( pack_filename, f->name_ext );
952 // restore original filespec
953 strcpy(filespec, temp);
956 // file either not localized or localized version not found
957 if ( !stricmp(filespec, f->name_ext) ) {
958 if ( size ) *size = f->size;
959 if ( offset ) *offset = f->pack_offset;
960 if ( pack_filename ) {
961 cf_root * r = cf_get_root(f->root_index);
963 strcpy( pack_filename, r->path );
964 if ( f->pack_offset < 1 ) {
966 if(strlen(Pathtypes[f->pathtype_index].path)){
967 strcat( pack_filename, Pathtypes[f->pathtype_index].path );
968 strcat( pack_filename, DIR_SEPARATOR_STR );
971 strcat( pack_filename, f->name_ext );
982 // Returns true if filename matches filespec, else zero if not
983 int cf_matches_spec(char *filespec, char *filename)
985 char *src_ext, *dst_ext;
987 src_ext = strchr(filespec, '.');
993 dst_ext = strchr(filename, '.');
997 return !stricmp(dst_ext, src_ext);
1000 int (*Get_file_list_filter)(char *filename) = NULL;
1001 int Skip_packfile_search = 0;
1003 int cf_file_already_in_list( int num_files, char **list, char *filename )
1007 char name_no_extension[MAX_PATH_LEN];
1009 strcpy(name_no_extension, filename );
1010 char *p = strchr( name_no_extension, '.' );
1013 for (i=0; i<num_files; i++ ) {
1014 if ( !stricmp(list[i], name_no_extension ) ) {
1023 // An alternative cf_get_file_list(), dynamic list version.
1024 // This one has a 'type', which is a CF_TYPE_* value. Because this specifies the directory
1025 // location, 'filter' only needs to be the filter itself, with no path information.
1026 // See above descriptions of cf_get_file_list() for more information about how it all works.
1027 int cf_get_file_list( int max, char **list, int pathtype, char *filter, int sort, file_list_info *info )
1030 int i, l, num_files = 0, own_flag = 0;
1037 Get_file_list_filter = NULL;
1043 if (!info && (sort == CF_SORT_TIME)) {
1044 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1048 char filespec[MAX_PATH_LEN];
1051 cf_create_default_path_string( filespec, pathtype, NULL );
1056 dirp = opendir (filespec);
1058 while ((dir = readdir (dirp)) != NULL)
1060 if (num_files >= max)
1063 if (fnmatch(filter, dir->d_name, 0) != 0)
1067 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
1071 if (stat(fn, &buf) == -1) {
1075 if (!S_ISREG(buf.st_mode)) {
1079 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1080 ptr = strrchr(dir->d_name, '.');
1082 l = ptr - dir->d_name;
1084 l = strlen(dir->d_name);
1086 list[num_files] = (char *)malloc(l + 1);
1087 strncpy(list[num_files], dir->d_name, l);
1088 list[num_files][l] = 0;
1090 info[num_files].write_time = buf.st_mtime;
1099 cf_create_default_path_string( filespec, pathtype, filter );
1101 find_handle = _findfirst( filespec, &find );
1102 if (find_handle != -1) {
1104 if (num_files >= max)
1107 if (!(find.attrib & _A_SUBDIR)) {
1108 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1109 ptr = strrchr(find.name, '.');
1111 l = ptr - find.name;
1113 l = strlen(find.name);
1115 list[num_files] = (char *)malloc(l + 1);
1116 strncpy(list[num_files], find.name, l);
1117 list[num_files][l] = 0;
1119 info[num_files].write_time = find.time_write;
1125 } while (!_findnext(find_handle, &find));
1127 _findclose( find_handle );
1131 // Search all the packfiles and CD.
1132 if ( !Skip_packfile_search ) {
1133 for (i=0; i<Num_files; i++ ) {
1134 cf_file * f = cf_get_file(i);
1136 // only search paths we're supposed to...
1137 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
1141 if (num_files >= max)
1144 if ( !cf_matches_spec( filter,f->name_ext)) {
1148 if ( cf_file_already_in_list(num_files,list,f->name_ext)) {
1152 if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1154 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1156 ptr = strrchr(f->name_ext, '.');
1158 l = ptr - f->name_ext;
1160 l = strlen(f->name_ext);
1162 list[num_files] = (char *)malloc(l + 1);
1163 strncpy(list[num_files], f->name_ext, l);
1164 list[num_files][l] = 0;
1167 info[num_files].write_time = f->write_time;
1177 if (sort != CF_SORT_NONE) {
1178 cf_sort_filenames( num_files, list, sort, info );
1185 Get_file_list_filter = NULL;
1189 int cf_file_already_in_list_preallocated( int num_files, char arr[][MAX_FILENAME_LEN], char *filename )
1193 char name_no_extension[MAX_PATH_LEN];
1195 strcpy(name_no_extension, filename );
1196 char *p = strchr( name_no_extension, '.' );
1199 for (i=0; i<num_files; i++ ) {
1200 if ( !stricmp(arr[i], name_no_extension ) ) {
1209 // An alternative cf_get_file_list(), fixed array version.
1210 // This one has a 'type', which is a CF_TYPE_* value. Because this specifies the directory
1211 // location, 'filter' only needs to be the filter itself, with no path information.
1212 // See above descriptions of cf_get_file_list() for more information about how it all works.
1213 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 )
1215 int i, num_files = 0, own_flag = 0;
1218 Get_file_list_filter = NULL;
1223 for (i=0; i<max; i++) {
1227 sort = CF_SORT_NONE; // sorting of array directly not supported. Sorting done on list only
1230 if (!info && (sort == CF_SORT_TIME)) {
1231 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1235 char filespec[MAX_PATH_LEN];
1237 // Search the default directories
1239 cf_create_default_path_string( filespec, pathtype, NULL );
1244 dirp = opendir (filespec);
1246 while ((dir = readdir (dirp)) != NULL)
1248 if (num_files >= max)
1251 if (fnmatch(filter, dir->d_name, 0) != 0)
1255 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
1259 if (stat(fn, &buf) == -1) {
1263 if (!S_ISREG(buf.st_mode)) {
1267 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1269 strncpy(arr[num_files], dir->d_name, MAX_FILENAME_LEN - 1 );
1270 char *ptr = strrchr(arr[num_files], '.');
1276 info[num_files].write_time = buf.st_mtime;
1285 cf_create_default_path_string( filespec, pathtype, filter );
1290 find_handle = _findfirst( filespec, &find );
1291 if (find_handle != -1) {
1293 if (num_files >= max)
1296 if (!(find.attrib & _A_SUBDIR)) {
1298 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1300 strncpy(arr[num_files], find.name, MAX_FILENAME_LEN - 1 );
1301 char *ptr = strrchr(arr[num_files], '.');
1307 info[num_files].write_time = find.time_write;
1314 } while (!_findnext(find_handle, &find));
1316 _findclose( find_handle );
1321 // Search all the packfiles and CD.
1322 if ( !Skip_packfile_search ) {
1323 for (i=0; i<Num_files; i++ ) {
1324 cf_file * f = cf_get_file(i);
1326 // only search paths we're supposed to...
1327 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
1331 if (num_files >= max)
1334 if ( !cf_matches_spec( filter,f->name_ext)) {
1338 if ( cf_file_already_in_list_preallocated( num_files, arr, f->name_ext )) {
1342 if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1344 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1346 strncpy(arr[num_files], f->name_ext, MAX_FILENAME_LEN - 1 );
1347 char *ptr = strrchr(arr[num_files], '.');
1353 info[num_files].write_time = f->write_time;
1362 if (sort != CF_SORT_NONE) {
1364 cf_sort_filenames( num_files, list, sort, info );
1371 Get_file_list_filter = NULL;
1375 // Returns the default storage path for files given a
1376 // particular pathtype. In other words, the path to
1377 // the unpacked, non-cd'd, stored on hard drive path.
1378 // If filename isn't null it will also tack the filename
1379 // on the end, creating a completely valid filename.
1380 // Input: pathtype - CF_TYPE_??
1381 // filename - optional, if set, tacks the filename onto end of path.
1382 // Output: path - Fully qualified pathname.
1383 void cf_create_default_path_string( char *path, int pathtype, char *filename, bool localize )
1386 if ( filename && strpbrk(filename, "/") ) {
1388 if ( filename && strpbrk(filename,"/\\:") ) {
1390 // Already has full path
1391 strcpy( path, filename );
1394 cf_root *root = cf_get_root(0);
1397 strcpy(path, filename);
1401 Assert(CF_TYPE_SPECIFIED(pathtype));
1403 strcpy(path, root->path);
1404 strcat(path, Pathtypes[pathtype].path);
1406 // Don't add slash for root directory
1407 if (Pathtypes[pathtype].path[0] != '\0') {
1408 strcat(path, DIR_SEPARATOR_STR);
1413 strcat(path, filename);
1415 // localize filename
1417 // create copy of path
1418 char temp_path[MAX_PATH_LEN];
1419 strcpy(temp_path, path);
1421 // localize the path
1422 lcl_add_dir_to_path_with_filename(path);
1424 // verify localized path
1425 FILE *fp = fopen(path, "rb");
1429 strcpy(path, temp_path);