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.10 2004/06/11 00:29:22 tigital
23 * byte-swapping changes for bigendian systems
25 * Revision 1.9 2003/05/27 03:03:11 taylor
26 * fix second root (gamedir) searching
28 * Revision 1.8 2003/02/20 17:41:07 theoddone33
29 * Userdir patch from Taylor Richards
31 * Revision 1.7 2002/06/22 23:57:39 relnev
32 * remove writable strings.
34 * fix compile for intel compiler.
36 * Revision 1.6 2002/06/09 04:41:15 relnev
37 * added copyright header
39 * Revision 1.5 2002/06/05 04:03:32 relnev
40 * finished cfilesystem.
42 * removed some old code.
44 * fixed mouse save off-by-one.
48 * Revision 1.4 2002/05/28 17:26:57 theoddone33
49 * Fill in some timer and palette setting stubs. Still no display
51 * Revision 1.3 2002/05/28 06:45:38 theoddone33
54 * Revision 1.2 2002/05/28 06:28:20 theoddone33
55 * Filesystem mods, actually reads some data files now
57 * Revision 1.1.1.1 2002/05/03 03:28:08 root
61 * 6 9/08/99 10:01p Dave
62 * Make sure game won't run in a drive's root directory. Make sure
63 * standalone routes suqad war messages properly to the host.
65 * 5 9/03/99 1:31a Dave
66 * CD checking by act. Added support to play 2 cutscenes in a row
67 * seamlessly. Fixed super low level cfile bug related to files in the
68 * root directory of a CD. Added cheat code to set campaign mission # in
71 * 4 2/22/99 10:31p Andsager
72 * Get rid of unneeded includes.
74 * 3 10/13/98 9:19a Andsager
75 * Add localization support to cfile. Optional parameter with cfopen that
76 * looks for localized files.
78 * 2 10/07/98 10:52a Dave
81 * 1 10/07/98 10:48a Dave
83 * 14 8/31/98 2:06p Dave
84 * Make cfile sort the ordering or vp files. Added support/checks for
85 * recognizing "mission disk" players.
87 * 13 6/23/98 4:18p Hoffoss
88 * Fixed some bugs with AC release build.
90 * 12 5/20/98 10:46p John
91 * Added code that doesn't include duplicate filenames in any file list
94 * 11 5/14/98 2:14p Lawrance2
95 * Use filespec filtering for packfiles
97 * 10 5/03/98 11:53a John
98 * Fixed filename case mangling.
100 * 9 5/02/98 11:06p Allender
101 * correctly deal with pack pathnames
103 * 8 5/01/98 11:41a Allender
104 * Fixed bug with mission saving in Fred.
106 * 7 5/01/98 10:21a John
107 * Added code to find all pack files in all trees. Added code to create
108 * any directories that we write to.
110 * 6 4/30/98 10:21p John
111 * Added code to cleanup cfilesystem
113 * 5 4/30/98 10:18p John
114 * added source safe header
127 #include <winbase.h> /* needed for memory mapping of file functions */
129 #include <sys/types.h>
132 #include <sys/stat.h>
137 //#include "outwnd.h"
138 //#include "vecmat.h"
141 #include "cfilesystem.h"
142 #include "localize.h"
145 #define CF_ROOTTYPE_PATH 0
146 #define CF_ROOTTYPE_PACK 1
149 // specifying hard drive tree
150 // searching for pack files on hard drive // Found by searching all known paths
151 // specifying cd-rom tree
152 // searching for pack files on CD-rom tree
153 typedef struct cf_root {
154 char path[CF_MAX_PATHNAME_LENGTH]; // Contains something like c:\projects\freespace or c:\projects\freespace\freespace.vp
155 int roottype; // CF_ROOTTYPE_PATH = Path, CF_ROOTTYPE_PACK =Pack file
158 // convenient type for sorting (see cf_build_pack_list())
159 typedef struct cf_root_sort {
160 char path[CF_MAX_PATHNAME_LENGTH];
165 #define CF_NUM_ROOTS_PER_BLOCK 32
166 #define CF_MAX_ROOT_BLOCKS 256 // Can store 32*256 = 8192 Roots
167 #define CF_MAX_ROOTS (CF_NUM_ROOTS_PER_BLOCK * CF_MAX_ROOT_BLOCKS)
169 typedef struct cf_root_block {
170 cf_root roots[CF_NUM_ROOTS_PER_BLOCK];
173 static int Num_roots = 0;
174 static cf_root_block *Root_blocks[CF_MAX_ROOT_BLOCKS];
177 // Created by searching all roots in order. This means Files is then sorted by precedence.
178 typedef struct cf_file {
179 char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension
180 int root_index; // Where in Roots this is located
181 int pathtype_index; // Where in Paths this is located
182 time_t write_time; // When it was last written
183 int size; // How big it is in bytes
184 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.
187 #define CF_NUM_FILES_PER_BLOCK 256
188 #define CF_MAX_FILE_BLOCKS 128 // Can store 256*128 = 32768 files
190 typedef struct cf_file_block {
191 cf_file files[CF_NUM_FILES_PER_BLOCK];
194 static int Num_files = 0;
195 static cf_file_block *File_blocks[CF_MAX_FILE_BLOCKS];
198 // Return a pointer to to file 'index'.
199 cf_file *cf_get_file(int index)
201 int block = index / CF_NUM_FILES_PER_BLOCK;
202 int offset = index % CF_NUM_FILES_PER_BLOCK;
204 return &File_blocks[block]->files[offset];
207 // Create a new file and return a pointer to it.
208 cf_file *cf_create_file()
210 int block = Num_files / CF_NUM_FILES_PER_BLOCK;
211 int offset = Num_files % CF_NUM_FILES_PER_BLOCK;
213 if ( File_blocks[block] == NULL ) {
214 File_blocks[block] = (cf_file_block *)malloc( sizeof(cf_file_block) );
215 Assert( File_blocks[block] != NULL);
220 return &File_blocks[block]->files[offset];
223 extern int cfile_inited;
225 // Create a new root and return a pointer to it. The structure is assumed unitialized.
226 cf_root *cf_get_root(int n)
228 int block = n / CF_NUM_ROOTS_PER_BLOCK;
229 int offset = n % CF_NUM_ROOTS_PER_BLOCK;
234 return &Root_blocks[block]->roots[offset];
238 // Create a new root and return a pointer to it. The structure is assumed unitialized.
239 cf_root *cf_create_root()
241 int block = Num_roots / CF_NUM_ROOTS_PER_BLOCK;
242 int offset = Num_roots % CF_NUM_ROOTS_PER_BLOCK;
244 if ( Root_blocks[block] == NULL ) {
245 Root_blocks[block] = (cf_root_block *)malloc( sizeof(cf_root_block) );
246 Assert(Root_blocks[block] != NULL);
251 return &Root_blocks[block]->roots[offset];
254 // return the # of packfiles which exist
255 int cf_get_packfile_count(cf_root *root)
257 char filespec[MAX_PATH_LEN];
261 // count up how many packfiles we're gonna have
263 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
265 strcpy( filespec, root->path );
267 if(strlen(Pathtypes[i].path)){
268 strcat( filespec, Pathtypes[i].path );
269 strcat( filespec, "/" );
275 dirp = opendir (filespec);
277 while ((dir = readdir (dirp)) != NULL)
279 if (!fnmatch ("*.vp", dir->d_name, 0))
285 strcpy( filespec, root->path );
287 if(strlen(Pathtypes[i].path)){
288 strcat( filespec, Pathtypes[i].path );
289 strcat( filespec, "\\" );
292 strcat( filespec, "*.vp" );
297 find_handle = _findfirst( filespec, &find );
299 if (find_handle != -1) {
301 if (!(find.attrib & _A_SUBDIR)) {
305 } while (!_findnext(find_handle, &find));
307 _findclose( find_handle );
312 return packfile_count;
315 // packfile sort function
316 int cf_packfile_sort_func(const void *elem1, const void *elem2)
318 cf_root_sort *r1, *r2;
319 r1 = (cf_root_sort*)elem1;
320 r2 = (cf_root_sort*)elem2;
322 // if the 2 directory types are the same, do a string compare
323 if(r1->cf_type == r2->cf_type){
324 return stricmp(r1->path, r2->path);
327 // otherwise return them in order of CF_TYPE_* precedence
328 return (r1->cf_type < r2->cf_type) ? -1 : 1;
331 // Go through a root and look for pack files
332 void cf_build_pack_list( cf_root *root )
334 char filespec[MAX_PATH_LEN];
336 cf_root_sort *temp_roots_sort, *rptr_sort;
337 int temp_root_count, root_index;
339 // determine how many packfiles there are
340 temp_root_count = cf_get_packfile_count(root);
341 if(temp_root_count <= 0){
345 // allocate a temporary array of temporary roots so we can easily sort them
346 temp_roots_sort = (cf_root_sort*)malloc(sizeof(cf_root_sort) * temp_root_count);
347 if(temp_roots_sort == NULL){
352 // now just setup all the root info
354 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
357 strcpy( filespec, root->path );
359 if(strlen(Pathtypes[i].path)){
360 strcat( filespec, Pathtypes[i].path );
361 strcat( filespec, "/" );
367 dirp = opendir (filespec);
369 while ((dir = readdir (dirp)) != NULL)
371 if (!fnmatch ("*.vp", dir->d_name, 0))
373 Assert(root_index < temp_root_count);
376 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
380 if (stat(fn, &buf) == -1) {
384 if (!S_ISREG(buf.st_mode)) {
388 // get a temp pointer
389 rptr_sort = &temp_roots_sort[root_index++];
391 // fill in all the proper info
392 strcpy(rptr_sort->path, root->path);
394 if(strlen(Pathtypes[i].path)){
395 strcat(rptr_sort->path, Pathtypes[i].path );
396 strcat(rptr_sort->path, "/");
399 strcat(rptr_sort->path, dir->d_name );
400 rptr_sort->roottype = CF_ROOTTYPE_PACK;
401 rptr_sort->cf_type = i;
407 strcpy( filespec, root->path );
409 if(strlen(Pathtypes[i].path)){
410 strcat( filespec, Pathtypes[i].path );
411 strcat( filespec, "\\" );
413 strcat( filespec, "*.vp" );
417 find_handle = _findfirst( filespec, &find );
419 if (find_handle != -1) {
422 if (!(find.attrib & _A_SUBDIR)) {
423 Assert(root_index < temp_root_count);
425 // get a temp pointer
426 rptr_sort = &temp_roots_sort[root_index++];
428 // fill in all the proper info
429 strcpy(rptr_sort->path, root->path);
431 if(strlen(Pathtypes[i].path)){
432 strcat(rptr_sort->path, Pathtypes[i].path );
433 strcat(rptr_sort->path, "\\");
436 strcat(rptr_sort->path, find.name );
437 rptr_sort->roottype = CF_ROOTTYPE_PACK;
438 rptr_sort->cf_type = i;
441 } while (!_findnext(find_handle, &find));
443 _findclose( find_handle );
448 // these should always be the same
449 Assert(root_index == temp_root_count);
452 qsort(temp_roots_sort, temp_root_count, sizeof(cf_root_sort), cf_packfile_sort_func);
454 // now insert them all into the real root list properly
456 for(i=0; i<temp_root_count; i++){
457 new_root = cf_create_root();
458 strcpy( new_root->path, root->path );
460 // mwa -- 4/2/98 put in the next 2 lines because the path name needs to be there
461 // to find the files.
462 strcpy(new_root->path, temp_roots_sort[i].path);
463 new_root->roottype = CF_ROOTTYPE_PACK;
466 // free up the temp list
467 free(temp_roots_sort);
471 void cf_build_root_list(char *cdrom_dir)
478 // ================================================================
479 // use users HOME directory as default for loading and saving files
480 root = cf_create_root();
481 strcpy( root->path, Cfile_user_dir );
483 // do we already have a slash? as in the case of a root directory install
484 if(strlen(root->path) && (root->path[strlen(root->path)-1] != '/')){
485 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 // First, check the current directory.
496 // strcpy( root->path, "d:\\projects\\freespace\\" );
498 root = cf_create_root();
500 if ( !_getcwd(root->path, CF_MAX_PATHNAME_LENGTH ) ) {
501 Error(LOCATION, "Can't get current working directory -- %d", errno );
504 // do we already have a slash? as in the case of a root directory install
506 if(strlen(root->path) && (root->path[strlen(root->path)-1] != '/')){
507 strcat(root->path, "/"); // put trailing backslash on for easier path construction
509 if(strlen(root->path) && (root->path[strlen(root->path)-1] != '\\')){
510 strcat(root->path, "\\"); // put trailing backslash on for easier path construction
513 root->roottype = CF_ROOTTYPE_PATH;
515 //======================================================
516 // Next, check any VP files under the current directory.
517 cf_build_pack_list(root);
520 //======================================================
521 // Check the real CD if one...
522 if ( cdrom_dir && strlen(cdrom_dir) ) {
523 root = cf_create_root();
524 strcpy( root->path, cdrom_dir );
525 root->roottype = CF_ROOTTYPE_PATH;
527 //======================================================
528 // Next, check any VP files in the CD-ROM directory.
529 cf_build_pack_list(root);
535 // Given a lower case list of file extensions
536 // separated by spaces, return zero if ext is
538 int is_ext_in_list( char *ext_list, char *ext )
542 strncpy( tmp_ext, ext, 127 );
544 if ( strstr(ext_list, tmp_ext )) {
551 void cf_search_root_path(int root_index)
555 cf_root *root = cf_get_root(root_index);
557 mprintf(( "Searching root '%s'\n", root->path ));
559 char search_path[CF_MAX_PATHNAME_LENGTH];
561 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
567 strcpy( search_path, root->path );
569 if(strlen(Pathtypes[i].path)){
570 strcat( search_path, Pathtypes[i].path );
571 strcat( search_path, "/" );
574 dirp = opendir (search_path);
576 while ((dir = readdir (dirp)) != NULL)
578 if (!fnmatch ("*.*", dir->d_name, 0))
581 snprintf(fn, MAX_PATH-1, "%s/%s", search_path, dir->d_name);
585 if (stat(fn, &buf) == -1) {
589 if (!S_ISREG(buf.st_mode)) {
593 char *ext = strchr( dir->d_name, '.' );
595 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) {
597 cf_file *file = cf_create_file();
599 strcpy( file->name_ext, dir->d_name );
600 file->root_index = root_index;
601 file->pathtype_index = i;
604 file->write_time = buf.st_mtime;
605 file->size = buf.st_size;
607 file->pack_offset = 0; // Mark as a non-packed file
609 //mprintf(( "Found file '%s'\n", file->name_ext ));
617 strcpy( search_path, root->path );
619 if(strlen(Pathtypes[i].path)){
620 strcat( search_path, Pathtypes[i].path );
621 strcat( search_path, "\\" );
624 strcat( search_path, "*.*" );
629 find_handle = _findfirst( search_path, &find );
631 if (find_handle != -1) {
633 if (!(find.attrib & _A_SUBDIR)) {
635 char *ext = strchr( find.name, '.' );
637 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) {
639 cf_file *file = cf_create_file();
641 strcpy( file->name_ext, find.name );
642 file->root_index = root_index;
643 file->pathtype_index = i;
644 file->write_time = find.time_write;
645 file->size = find.size;
646 file->pack_offset = 0; // Mark as a non-packed file
648 //mprintf(( "Found file '%s'\n", file->name_ext ));
655 } while (!_findnext(find_handle, &find));
657 _findclose( find_handle );
665 typedef struct VP_FILE_HEADER {
672 typedef struct VP_FILE {
679 void cf_search_root_pack(int root_index)
683 cf_root *root = cf_get_root(root_index);
685 //mprintf(( "Searching root pack '%s'\n", root->path ));
689 FILE *fp = fopen( root->path, "rb" );
690 // Read the file header
695 VP_FILE_HEADER VP_header;
697 Assert( sizeof(VP_header) == 16 );
698 fread(&VP_header, 1, sizeof(VP_header), fp);
700 VP_header.version = INTEL_INT( VP_header.version);
701 VP_header.index_offset = INTEL_INT( VP_header.index_offset);
702 VP_header.num_files = INTEL_INT( VP_header.num_files);
705 fseek(fp, VP_header.index_offset, SEEK_SET);
707 char search_path[CF_MAX_PATHNAME_LENGTH];
709 strcpy( search_path, "" );
711 // Go through all the files
712 for (i=0; i<VP_header.num_files; i++ ) {
715 fread( &find, sizeof(VP_FILE), 1, fp );
717 find.offset = INTEL_INT( find.offset );
718 find.size = INTEL_INT( find.size );
719 find.write_time = INTEL_INT(find.write_time);
721 if ( find.size == 0 ) {
722 if ( !stricmp( find.filename, ".." )) {
723 int l = strlen(search_path);
724 char *p = &search_path[l-1];
726 while( (p > search_path) && (*p != '/') ) {
728 while( (p > search_path) && (*p != '\\') ) {
734 if ( strlen(search_path) ) {
736 strcat( search_path, "/" );
738 strcat( search_path, "\\" );
741 strcat( search_path, find.filename );
744 //mprintf(( "Current dir = '%s'\n", search_path ));
748 for (j=CF_TYPE_ROOT; j<CF_MAX_PATH_TYPES; j++ ) {
750 if ( !stricmp( search_path, Pathtypes[j].path )) {
752 char *ext = strchr( find.filename, '.' );
754 if ( is_ext_in_list( Pathtypes[j].extensions, ext ) ) {
756 cf_file *file = cf_create_file();
758 strcpy( file->name_ext, find.filename );
759 file->root_index = root_index;
760 file->pathtype_index = j;
761 file->write_time = find.write_time;
762 file->size = find.size;
763 file->pack_offset = find.offset; // Mark as a non-packed file
765 //mprintf(( "Found pack file '%s'\n", file->name_ext ));
779 void cf_build_file_list()
785 // For each root, find all files...
786 for (i=1; i<Num_roots; i++ ) {
787 cf_root *root = cf_get_root(i);
788 if ( root->roottype == CF_ROOTTYPE_PATH ) {
789 cf_search_root_path(i);
790 } else if ( root->roottype == CF_ROOTTYPE_PACK ) {
791 cf_search_root_pack(i);
798 void cf_build_secondary_filelist(char *cdrom_dir)
806 // Init the path types
807 for (i=0; i<CF_MAX_PATH_TYPES; i++ ) {
808 Assert( Pathtypes[i].index == i );
809 #if 0 /* they are already lowercased -- SBF */
810 if ( Pathtypes[i].extensions ) {
811 strlwr(Pathtypes[i].extensions);
816 // Init the root blocks
817 for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ ) {
818 Root_blocks[i] = NULL;
821 // Init the file blocks
822 for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
823 File_blocks[i] = NULL;
826 mprintf(( "Building file index...\n" ));
828 // build the list of searchable roots
829 cf_build_root_list(cdrom_dir);
831 // build the list of files themselves
832 cf_build_file_list();
834 mprintf(( "Found %d roots and %d files.\n", Num_roots, Num_files ));
837 void cf_free_secondary_filelist()
841 // Free the root blocks
842 for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ ) {
843 if ( Root_blocks[i] ) {
844 free( Root_blocks[i] );
845 Root_blocks[i] = NULL;
850 // Init the file blocks
851 for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
852 if ( File_blocks[i] ) {
853 free( File_blocks[i] );
854 File_blocks[i] = NULL;
860 // Searches for a file. Follows all rules and precedence and searches
861 // CD's and pack files.
862 // Input: filespace - Filename & extension
863 // pathtype - See CF_TYPE_ defines in CFILE.H
864 // Output: pack_filename - Absolute path and filename of this file. Could be a packfile or the actual file.
866 // offset - Offset into pack file. 0 if not a packfile.
867 // Returns: If not found returns 0.
868 int cf_find_file_location( char *filespec, int pathtype, char *pack_filename, int *size, int *offset, bool localize )
872 Assert(filespec && strlen(filespec));
874 // see if we have something other than just a filename
875 // our current rules say that any file that specifies a direct
876 // path will try to be opened on that path. If that open
877 // fails, then we will open the file based on the extension
880 // NOTE: full path should also include localization, if so desired
881 if ( strpbrk(filespec,"/\\:") ) { // do we have a full path already?
882 FILE *fp = fopen(filespec, "rb" );
884 if ( size ) *size = filelength(fileno(fp));
885 if ( offset ) *offset = 0;
886 if ( pack_filename ) {
887 strcpy( pack_filename, filespec );
893 return 0; // If they give a full path, fail if not found.
896 // Search the hard drive for files first.
897 int num_search_dirs = 0;
898 int search_order[CF_MAX_PATH_TYPES];
900 if ( CF_TYPE_SPECIFIED(pathtype) ) {
901 search_order[num_search_dirs++] = pathtype;
904 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++) {
905 if ( i != pathtype ) {
906 search_order[num_search_dirs++] = i;
910 for (i=0; i<num_search_dirs; i++ ) {
911 char longname[MAX_PATH_LEN];
913 cf_create_default_path_string( longname, search_order[i], filespec, localize );
915 FILE *fp = fopen(longname, "rb" );
917 if ( size ) *size = filelength(fileno(fp));
918 if ( offset ) *offset = 0;
919 if ( pack_filename ) {
920 strcpy( pack_filename, longname );
928 // search the secondary directory (game directory) as well since the user dir is default
929 for (i=0; i<num_search_dirs; i++ ) {
930 char longname[MAX_PATH_LEN];
932 cf_create_secondary_path_string( longname, search_order[i], filespec, localize );
934 FILE *fp = fopen(longname, "rb" );
936 if ( size ) *size = filelength(fileno(fp));
937 if ( offset ) *offset = 0;
938 if ( pack_filename ) {
939 strcpy( pack_filename, longname );
947 // Search the pak files and CD-ROM.
949 for (i=0; i<Num_files; i++ ) {
950 cf_file * f = cf_get_file(i);
952 // only search paths we're supposed to...
953 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
958 // create localized filespec
959 char temp[MAX_PATH_LEN];
960 strcpy(temp, filespec);
961 lcl_add_dir_to_path_with_filename(filespec);
963 if ( !stricmp(filespec, f->name_ext) ) {
964 if ( size ) *size = f->size;
965 if ( offset ) *offset = f->pack_offset;
966 if ( pack_filename ) {
967 cf_root * r = cf_get_root(f->root_index);
969 strcpy( pack_filename, r->path );
970 if ( f->pack_offset < 1 ) {
971 strcat( pack_filename, Pathtypes[f->pathtype_index].path );
973 strcat( pack_filename, "/" );
975 strcat( pack_filename, "\\" );
977 strcat( pack_filename, f->name_ext );
982 // restore original filespec
983 strcpy(filespec, temp);
986 // file either not localized or localized version not found
987 if ( !stricmp(filespec, f->name_ext) ) {
988 if ( size ) *size = f->size;
989 if ( offset ) *offset = f->pack_offset;
990 if ( pack_filename ) {
991 cf_root * r = cf_get_root(f->root_index);
993 strcpy( pack_filename, r->path );
994 if ( f->pack_offset < 1 ) {
996 if(strlen(Pathtypes[f->pathtype_index].path)){
997 strcat( pack_filename, Pathtypes[f->pathtype_index].path );
999 strcat( pack_filename, "/" );
1001 strcat( pack_filename, "\\" );
1005 strcat( pack_filename, f->name_ext );
1016 // Returns true if filename matches filespec, else zero if not
1017 int cf_matches_spec(char *filespec, char *filename)
1019 char *src_ext, *dst_ext;
1021 src_ext = strchr(filespec, '.');
1024 if (*src_ext == '*')
1027 dst_ext = strchr(filename, '.');
1031 return !stricmp(dst_ext, src_ext);
1034 int (*Get_file_list_filter)(char *filename) = NULL;
1035 int Skip_packfile_search = 0;
1037 int cf_file_already_in_list( int num_files, char **list, char *filename )
1041 char name_no_extension[MAX_PATH_LEN];
1043 strcpy(name_no_extension, filename );
1044 char *p = strchr( name_no_extension, '.' );
1047 for (i=0; i<num_files; i++ ) {
1048 if ( !stricmp(list[i], name_no_extension ) ) {
1057 // An alternative cf_get_file_list(), dynamic list version.
1058 // This one has a 'type', which is a CF_TYPE_* value. Because this specifies the directory
1059 // location, 'filter' only needs to be the filter itself, with no path information.
1060 // See above descriptions of cf_get_file_list() for more information about how it all works.
1061 int cf_get_file_list( int max, char **list, int pathtype, char *filter, int sort, file_list_info *info )
1064 int i, l, num_files = 0, own_flag = 0;
1071 Get_file_list_filter = NULL;
1077 if (!info && (sort == CF_SORT_TIME)) {
1078 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1082 char filespec[MAX_PATH_LEN];
1085 cf_create_default_path_string( filespec, pathtype, NULL );
1090 dirp = opendir (filespec);
1092 while ((dir = readdir (dirp)) != NULL)
1094 if (num_files >= max)
1097 if (fnmatch(filter, dir->d_name, 0) != 0)
1101 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
1105 if (stat(fn, &buf) == -1) {
1109 if (!S_ISREG(buf.st_mode)) {
1113 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1114 ptr = strrchr(dir->d_name, '.');
1116 l = ptr - dir->d_name;
1118 l = strlen(dir->d_name);
1120 list[num_files] = (char *)malloc(l + 1);
1121 strncpy(list[num_files], dir->d_name, l);
1122 list[num_files][l] = 0;
1124 info[num_files].write_time = buf.st_mtime;
1133 // grab secondary (game) directory as well but be sure to skip the pilot
1134 // directories as that would be a bad thing to load read-only pilots
1135 if (pathtype != (CF_TYPE_PLAYERS || CF_TYPE_SINGLE_PLAYERS || CF_TYPE_MULTI_PLAYERS)) {
1136 cf_create_secondary_path_string( filespec, pathtype, NULL );
1141 dirp = opendir (filespec);
1143 while ((dir = readdir (dirp)) != NULL)
1145 if (num_files >= max)
1148 if (fnmatch(filter, dir->d_name, 0) != 0)
1152 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
1156 if (stat(fn, &buf) == -1) {
1160 if (!S_ISREG(buf.st_mode)) {
1164 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1165 ptr = strrchr(dir->d_name, '.');
1167 l = ptr - dir->d_name;
1169 l = strlen(dir->d_name);
1171 list[num_files] = (char *)malloc(l + 1);
1172 strncpy(list[num_files], dir->d_name, l);
1173 list[num_files][l] = 0;
1175 info[num_files].write_time = buf.st_mtime;
1185 cf_create_default_path_string( filespec, pathtype, filter );
1187 find_handle = _findfirst( filespec, &find );
1188 if (find_handle != -1) {
1190 if (num_files >= max)
1193 if (!(find.attrib & _A_SUBDIR)) {
1194 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1195 ptr = strrchr(find.name, '.');
1197 l = ptr - find.name;
1199 l = strlen(find.name);
1201 list[num_files] = (char *)malloc(l + 1);
1202 strncpy(list[num_files], find.name, l);
1203 list[num_files][l] = 0;
1205 info[num_files].write_time = find.time_write;
1211 } while (!_findnext(find_handle, &find));
1213 _findclose( find_handle );
1218 // Search all the packfiles and CD.
1219 if ( !Skip_packfile_search ) {
1220 for (i=0; i<Num_files; i++ ) {
1221 cf_file * f = cf_get_file(i);
1223 // only search paths we're supposed to...
1224 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
1228 if (num_files >= max)
1231 if ( !cf_matches_spec( filter,f->name_ext)) {
1235 if ( cf_file_already_in_list(num_files,list,f->name_ext)) {
1239 if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1241 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1243 ptr = strrchr(f->name_ext, '.');
1245 l = ptr - f->name_ext;
1247 l = strlen(f->name_ext);
1249 list[num_files] = (char *)malloc(l + 1);
1250 strncpy(list[num_files], f->name_ext, l);
1251 list[num_files][l] = 0;
1254 info[num_files].write_time = f->write_time;
1264 if (sort != CF_SORT_NONE) {
1265 cf_sort_filenames( num_files, list, sort, info );
1272 Get_file_list_filter = NULL;
1276 int cf_file_already_in_list_preallocated( int num_files, char arr[][MAX_FILENAME_LEN], char *filename )
1280 char name_no_extension[MAX_PATH_LEN];
1282 strcpy(name_no_extension, filename );
1283 char *p = strchr( name_no_extension, '.' );
1286 for (i=0; i<num_files; i++ ) {
1287 if ( !stricmp(arr[i], name_no_extension ) ) {
1296 // An alternative cf_get_file_list(), fixed array version.
1297 // This one has a 'type', which is a CF_TYPE_* value. Because this specifies the directory
1298 // location, 'filter' only needs to be the filter itself, with no path information.
1299 // See above descriptions of cf_get_file_list() for more information about how it all works.
1300 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 )
1302 int i, num_files = 0, own_flag = 0;
1305 Get_file_list_filter = NULL;
1310 for (i=0; i<max; i++) {
1314 sort = CF_SORT_NONE; // sorting of array directly not supported. Sorting done on list only
1317 if (!info && (sort == CF_SORT_TIME)) {
1318 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1322 char filespec[MAX_PATH_LEN];
1324 // Search the default directories
1326 cf_create_default_path_string( filespec, pathtype, NULL );
1331 dirp = opendir (filespec);
1333 while ((dir = readdir (dirp)) != NULL)
1335 if (num_files >= max)
1338 if (fnmatch(filter, dir->d_name, 0) != 0)
1342 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
1346 if (stat(fn, &buf) == -1) {
1350 if (!S_ISREG(buf.st_mode)) {
1354 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1356 strncpy(arr[num_files], dir->d_name, MAX_FILENAME_LEN - 1 );
1357 char *ptr = strrchr(arr[num_files], '.');
1363 info[num_files].write_time = buf.st_mtime;
1372 // grab secondary (game) directory as well but be sure to skip the pilot
1373 // directories as that would be a bad thing to load read-only pilots
1374 if (pathtype != (CF_TYPE_PLAYERS || CF_TYPE_SINGLE_PLAYERS || CF_TYPE_MULTI_PLAYERS)) {
1375 cf_create_secondary_path_string( filespec, pathtype, NULL );
1380 dirp = opendir (filespec);
1382 while ((dir = readdir (dirp)) != NULL)
1384 if (num_files >= max)
1387 if (fnmatch(filter, dir->d_name, 0) != 0)
1391 snprintf(fn, MAX_PATH-1, "%s/%s", filespec, dir->d_name);
1395 if (stat(fn, &buf) == -1) {
1399 if (!S_ISREG(buf.st_mode)) {
1403 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1405 strncpy(arr[num_files], dir->d_name, MAX_FILENAME_LEN - 1 );
1406 char *ptr = strrchr(arr[num_files], '.');
1412 info[num_files].write_time = buf.st_mtime;
1422 cf_create_default_path_string( filespec, pathtype, filter );
1427 find_handle = _findfirst( filespec, &find );
1428 if (find_handle != -1) {
1430 if (num_files >= max)
1433 if (!(find.attrib & _A_SUBDIR)) {
1435 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1437 strncpy(arr[num_files], find.name, MAX_FILENAME_LEN - 1 );
1438 char *ptr = strrchr(arr[num_files], '.');
1444 info[num_files].write_time = find.time_write;
1451 } while (!_findnext(find_handle, &find));
1453 _findclose( find_handle );
1458 // Search all the packfiles and CD.
1459 if ( !Skip_packfile_search ) {
1460 for (i=0; i<Num_files; i++ ) {
1461 cf_file * f = cf_get_file(i);
1463 // only search paths we're supposed to...
1464 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
1468 if (num_files >= max)
1471 if ( !cf_matches_spec( filter,f->name_ext)) {
1475 if ( cf_file_already_in_list_preallocated( num_files, arr, f->name_ext )) {
1479 if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1481 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1483 strncpy(arr[num_files], f->name_ext, MAX_FILENAME_LEN - 1 );
1484 char *ptr = strrchr(arr[num_files], '.');
1490 info[num_files].write_time = f->write_time;
1499 if (sort != CF_SORT_NONE) {
1501 cf_sort_filenames( num_files, list, sort, info );
1508 Get_file_list_filter = NULL;
1512 // Returns the default storage path for files given a
1513 // particular pathtype. In other words, the path to
1514 // the unpacked, non-cd'd, stored on hard drive path.
1515 // If filename isn't null it will also tack the filename
1516 // on the end, creating a completely valid filename.
1517 // Input: pathtype - CF_TYPE_??
1518 // filename - optional, if set, tacks the filename onto end of path.
1519 // Output: path - Fully qualified pathname.
1520 void cf_create_default_path_string( char *path, int pathtype, char *filename, bool localize )
1523 if ( filename && strpbrk(filename,"/") ) {
1525 if ( filename && strpbrk(filename,"/\\:") ) {
1527 // Already has full path
1528 strcpy( path, filename );
1531 cf_root *root = cf_get_root(0);
1534 strcpy(path, filename);
1538 Assert(CF_TYPE_SPECIFIED(pathtype));
1540 strcpy(path, root->path);
1541 strcat(path, Pathtypes[pathtype].path);
1543 // Don't add slash for root directory
1544 if (Pathtypes[pathtype].path[0] != '\0') {
1554 strcat(path, filename);
1556 // localize filename
1558 // create copy of path
1559 char temp_path[MAX_PATH_LEN];
1560 strcpy(temp_path, path);
1562 // localize the path
1563 lcl_add_dir_to_path_with_filename(path);
1565 // verify localized path
1566 FILE *fp = fopen(path, "rb");
1570 strcpy(path, temp_path);
1578 // this is the same as cf_create_default_path_string above but as it only shows
1579 // files in the users directory this function will find files in the game
1580 // installation directory
1581 void cf_create_secondary_path_string( char *path, int pathtype, char *filename, bool localize )
1583 if ( filename && strpbrk(filename,"/") ) {
1585 // Already has full path
1586 strcpy( path, filename );
1589 cf_root *root = cf_get_root(1);
1592 strcpy(path, filename);
1596 Assert(CF_TYPE_SPECIFIED(pathtype));
1598 strcpy(path, root->path);
1599 strcat(path, Pathtypes[pathtype].path);
1601 // Don't add slash for root directory
1602 if (Pathtypes[pathtype].path[0] != '\0') {
1608 strcat(path, filename);
1610 // localize filename
1612 // create copy of path
1613 char temp_path[MAX_PATH_LEN];
1614 strcpy(temp_path, path);
1616 // localize the path
1617 lcl_add_dir_to_path_with_filename(path);
1619 // verify localized path
1620 FILE *fp = fopen(path, "rb");
1624 strcpy(path, temp_path);