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
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 SDL_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 SDL_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++ ) {
264 SDL_strlcpy( filespec, root->path, SDL_arraysize(filespec) );
266 if(strlen(Pathtypes[i].path)){
267 SDL_strlcat( filespec, Pathtypes[i].path, SDL_arraysize(filespec) );
268 SDL_strlcat( filespec, DIR_SEPARATOR_STR, SDL_arraysize(filespec) );
275 dirp = opendir (filespec);
277 while ((dir = readdir (dirp)) != NULL)
279 if (!fnmatch ("*.vp", dir->d_name, 0))
285 SDL_strlcat( filespec, "*.vp", SDL_arraysize(filespec) );
290 find_handle = _findfirst( filespec, &find );
292 if (find_handle != -1) {
294 if (!(find.attrib & _A_SUBDIR)) {
298 } while (!_findnext(find_handle, &find));
300 _findclose( find_handle );
305 return packfile_count;
308 // packfile sort function
309 int cf_packfile_sort_func(const void *elem1, const void *elem2)
311 cf_root_sort *r1, *r2;
312 r1 = (cf_root_sort*)elem1;
313 r2 = (cf_root_sort*)elem2;
315 // if the 2 directory types are the same, do a string compare
316 if(r1->cf_type == r2->cf_type){
317 return SDL_strcasecmp(r1->path, r2->path);
320 // otherwise return them in order of CF_TYPE_* precedence
321 return (r1->cf_type < r2->cf_type) ? -1 : 1;
324 // Go through a root and look for pack files
325 void cf_build_pack_list( cf_root *root )
327 char filespec[MAX_PATH_LEN];
329 cf_root_sort *temp_roots_sort, *rptr_sort;
330 int temp_root_count, root_index;
332 // determine how many packfiles there are
333 temp_root_count = cf_get_packfile_count(root);
334 if(temp_root_count <= 0){
338 // allocate a temporary array of temporary roots so we can easily sort them
339 temp_roots_sort = (cf_root_sort*)malloc(sizeof(cf_root_sort) * temp_root_count);
340 if(temp_roots_sort == NULL){
345 // now just setup all the root info
347 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
348 SDL_strlcpy( filespec, root->path, SDL_arraysize(filespec) );
350 if(strlen(Pathtypes[i].path)){
351 SDL_strlcat( filespec, Pathtypes[i].path, SDL_arraysize(filespec) );
352 SDL_strlcat( filespec, DIR_SEPARATOR_STR, SDL_arraysize(filespec) );
359 dirp = opendir (filespec);
361 while ((dir = readdir (dirp)) != NULL)
363 if (!fnmatch ("*.vp", dir->d_name, 0))
365 SDL_assert(root_index < temp_root_count);
367 char fn[MAX_PATH_LEN];
368 SDL_snprintf(fn, SDL_arraysize(fn), "%s/%s", filespec, dir->d_name);
371 if (stat(fn, &buf) == -1) {
375 if (!S_ISREG(buf.st_mode)) {
379 // get a temp pointer
380 rptr_sort = &temp_roots_sort[root_index++];
382 // fill in all the proper info
383 SDL_strlcpy(rptr_sort->path, root->path, SDL_arraysize(rptr_sort->path));
385 if(strlen(Pathtypes[i].path)){
386 SDL_strlcat(rptr_sort->path, Pathtypes[i].path, SDL_arraysize(rptr_sort->path));
387 SDL_strlcat(rptr_sort->path, "/", SDL_arraysize(rptr_sort->path));
390 SDL_strlcat(rptr_sort->path, dir->d_name, SDL_arraysize(rptr_sort->path));
391 rptr_sort->roottype = CF_ROOTTYPE_PACK;
392 rptr_sort->cf_type = i;
398 SDL_strlcat( filespec, "*.vp", SDL_arraysize(filespec) );
403 find_handle = _findfirst( filespec, &find );
405 if (find_handle != -1) {
408 if (!(find.attrib & _A_SUBDIR)) {
409 SDL_assert(root_index < temp_root_count);
411 // get a temp pointer
412 rptr_sort = &temp_roots_sort[root_index++];
414 // fill in all the proper info
415 SDL_strlcpy(rptr_sort->path, root->path, SDL_arraysize(rptr_sort->path));
417 if(strlen(Pathtypes[i].path)){
418 SDL_strlcat(rptr_sort->path, Pathtypes[i].path, SDL_arraysize(rptr_sort->path) );
419 SDL_strlcat(rptr_sort->path, "\\", SDL_arraysize(rptr_sort->path));
422 SDL_strlcat(rptr_sort->path, find.name, SDL_arraysize(rptr_sort->path) );
423 rptr_sort->roottype = CF_ROOTTYPE_PACK;
424 rptr_sort->cf_type = i;
427 } while (!_findnext(find_handle, &find));
429 _findclose( find_handle );
434 // these should always be the same
435 SDL_assert(root_index == temp_root_count);
438 qsort(temp_roots_sort, temp_root_count, sizeof(cf_root_sort), cf_packfile_sort_func);
440 // now insert them all into the real root list properly
442 for(i=0; i<temp_root_count; i++){
443 new_root = cf_create_root();
444 SDL_strlcpy( new_root->path, root->path, SDL_arraysize(new_root->path) );
446 // mwa -- 4/2/98 put in the next 2 lines because the path name needs to be there
447 // to find the files.
448 SDL_strlcpy(new_root->path, temp_roots_sort[i].path, SDL_arraysize(new_root->path));
449 new_root->roottype = CF_ROOTTYPE_PACK;
452 // free up the temp list
453 free(temp_roots_sort);
457 void cf_build_root_list(const char *extras_dir)
463 // ================================================================
464 // have user's writable directory as default for loading and saving files
465 root = cf_create_root();
466 SDL_strlcpy( root->path, Cfile_user_dir, SDL_arraysize(root->path) );
468 // do we already have a slash? as in the case of a root directory install
469 if(strlen(root->path) && (root->path[strlen(root->path)-1] != DIR_SEPARATOR_CHAR)){
470 SDL_strlcat(root->path, DIR_SEPARATOR_STR, SDL_arraysize(root->path)); // put trailing backslash on for easier path construction
472 root->roottype = CF_ROOTTYPE_PATH;
474 //======================================================
475 // then check any VP files under the directory.
476 cf_build_pack_list(root);
478 //======================================================
479 // Next, use the executable's directory for game data
480 root = cf_create_root();
481 SDL_strlcpy( root->path, Cfile_root_dir, SDL_arraysize(root->path) );
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] != DIR_SEPARATOR_CHAR)){
485 SDL_strlcat(root->path, DIR_SEPARATOR_STR, SDL_arraysize(root->path)); // put trailing backslash on for easier path construction
487 root->roottype = CF_ROOTTYPE_PATH;
489 //======================================================
490 // then check any VP files under the current directory.
491 cf_build_pack_list(root);
494 //======================================================
495 // Check the real CD if one...
496 if ( extras_dir && strlen(extras_dir) ) {
497 root = cf_create_root();
498 SDL_strlcpy( root->path, extras_dir, SDL_arraysize(root->path) );
499 root->roottype = CF_ROOTTYPE_PATH;
501 //======================================================
502 // Next, check any VP files in the CD-ROM directory.
503 cf_build_pack_list(root);
507 // Given a lower case list of file extensions
508 // separated by spaces, return zero if ext is
510 int is_ext_in_list( const char *ext_list, char *ext )
514 SDL_strlcpy( tmp_ext, ext, SDL_arraysize(tmp_ext) );
516 if ( strstr(ext_list, tmp_ext )) {
523 void cf_search_root_path(int root_index)
527 cf_root *root = cf_get_root(root_index);
529 mprintf(( "Searching root '%s'\n", root->path ));
531 char search_path[CF_MAX_PATHNAME_LENGTH];
533 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
535 SDL_strlcpy( search_path, root->path, SDL_arraysize(search_path) );
537 if(strlen(Pathtypes[i].path)){
538 SDL_strlcat( search_path, Pathtypes[i].path, SDL_arraysize(search_path) );
539 SDL_strlcat( search_path, DIR_SEPARATOR_STR, SDL_arraysize(search_path) );
546 // only get pilots from the primary root as there could be nasty
547 // permission issues otherwise
548 if ( (root_index > 0) && ((Pathtypes[i].index == CF_TYPE_SINGLE_PLAYERS)
549 || (Pathtypes[i].index == CF_TYPE_MULTI_PLAYERS)) ) {
553 dirp = opendir (search_path);
555 while ((dir = readdir (dirp)) != NULL)
557 if (!fnmatch ("*.*", dir->d_name, 0))
559 char fn[MAX_PATH_LEN];
560 SDL_snprintf(fn, MAX_PATH_LEN, "%s/%s", search_path, dir->d_name);
563 if (stat(fn, &buf) == -1) {
567 if (!S_ISREG(buf.st_mode)) {
571 char *ext = SDL_strchr( dir->d_name, '.' );
573 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) {
575 cf_file *file = cf_create_file();
577 SDL_strlcpy( file->name_ext, dir->d_name, SDL_arraysize(file->name_ext) );
578 file->root_index = root_index;
579 file->pathtype_index = i;
582 file->write_time = buf.st_mtime;
583 file->size = buf.st_size;
585 file->pack_offset = 0; // Mark as a non-packed file
587 //mprintf(( "Found file '%s'\n", file->name_ext ));
595 SDL_strlcat( search_path, "*.*", SDL_arraysize(search_path) );
600 find_handle = _findfirst( search_path, &find );
602 if (find_handle != -1) {
604 if (!(find.attrib & _A_SUBDIR)) {
606 char *ext = SDL_strchr( find.name, '.' );
608 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) {
610 cf_file *file = cf_create_file();
612 SDL_strlcpy( file->name_ext, find.name, SDL_arraysize(file->name_ext) );
613 file->root_index = root_index;
614 file->pathtype_index = i;
615 file->write_time = find.time_write;
616 file->size = find.size;
617 file->pack_offset = 0; // Mark as a non-packed file
619 //mprintf(( "Found file '%s'\n", file->name_ext ));
626 } while (!_findnext(find_handle, &find));
628 _findclose( find_handle );
636 typedef struct VP_FILE_HEADER {
643 typedef struct VP_FILE {
647 fs_time_t write_time;
650 void cf_search_root_pack(int root_index)
655 cf_root *root = cf_get_root(root_index);
657 //mprintf(( "Searching root pack '%s'\n", root->path ));
661 FILE *fp = fopen( root->path, "rb" );
662 // Read the file header
667 VP_FILE_HEADER VP_header;
669 SDL_assert( sizeof(VP_header) == 16 );
670 rc = fread(&VP_header, 1, sizeof(VP_header), fp);
672 if (rc != sizeof(VP_header)) {
677 VP_header.version = INTEL_INT( VP_header.version);
678 VP_header.index_offset = INTEL_INT( VP_header.index_offset);
679 VP_header.num_files = INTEL_INT( VP_header.num_files);
682 fseek(fp, VP_header.index_offset, SEEK_SET);
684 char search_path[CF_MAX_PATHNAME_LENGTH];
686 SDL_strlcpy( search_path, "", SDL_arraysize(search_path) );
688 // Go through all the files
689 for (i=0; i<VP_header.num_files; i++ ) {
692 rc = fread( &find, 1, sizeof(VP_FILE), fp );
694 if (rc != sizeof(VP_FILE)) {
698 find.offset = INTEL_INT( find.offset );
699 find.size = INTEL_INT( find.size );
700 find.write_time = INTEL_INT(find.write_time);
702 if ( find.size == 0 ) {
703 if ( !SDL_strcasecmp( find.filename, ".." )) {
704 int l = strlen(search_path);
705 char *p = &search_path[l-1];
706 while( (p > search_path) && (*p != DIR_SEPARATOR_CHAR) ) {
711 if ( strlen(search_path) ) {
712 SDL_strlcat( search_path, DIR_SEPARATOR_STR, SDL_arraysize(search_path) );
714 SDL_strlcat( search_path, find.filename, SDL_arraysize(search_path) );
717 //mprintf(( "Current dir = '%s'\n", search_path ));
721 for (j=CF_TYPE_ROOT; j<CF_MAX_PATH_TYPES; j++ ) {
723 if ( !SDL_strcasecmp( search_path, Pathtypes[j].path )) {
725 char *ext = SDL_strchr( find.filename, '.' );
727 if ( is_ext_in_list( Pathtypes[j].extensions, ext ) ) {
729 cf_file *file = cf_create_file();
731 SDL_strlcpy( file->name_ext, find.filename, SDL_arraysize(file->name_ext) );
732 file->root_index = root_index;
733 file->pathtype_index = j;
734 file->write_time = find.write_time;
735 file->size = find.size;
736 file->pack_offset = find.offset; // Mark as a non-packed file
738 //mprintf(( "Found pack file '%s'\n", file->name_ext ));
752 void cf_build_file_list()
758 // For each root, find all files...
759 for (i=1; i<Num_roots; i++ ) {
760 cf_root *root = cf_get_root(i);
761 if ( root->roottype == CF_ROOTTYPE_PATH ) {
762 cf_search_root_path(i);
763 } else if ( root->roottype == CF_ROOTTYPE_PACK ) {
764 cf_search_root_pack(i);
771 void cf_build_secondary_filelist(const char *extras_dir)
779 // Init the path types
780 for (i=0; i<CF_MAX_PATH_TYPES; i++ ) {
781 SDL_assert( Pathtypes[i].index == i );
782 #if 0 /* they are already lowercased -- SBF */
783 if ( Pathtypes[i].extensions ) {
784 SDL_strlwr(Pathtypes[i].extensions);
789 // Init the root blocks
790 for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ ) {
791 Root_blocks[i] = NULL;
794 // Init the file blocks
795 for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
796 File_blocks[i] = NULL;
799 mprintf(( "Building file index...\n" ));
801 // build the list of searchable roots
802 cf_build_root_list(extras_dir);
804 // build the list of files themselves
805 cf_build_file_list();
807 mprintf(( "Found %d roots and %d files.\n", Num_roots, Num_files ));
810 void cf_free_secondary_filelist()
814 // Free the root blocks
815 for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ ) {
816 if ( Root_blocks[i] ) {
817 free( Root_blocks[i] );
818 Root_blocks[i] = NULL;
823 // Init the file blocks
824 for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
825 if ( File_blocks[i] ) {
826 free( File_blocks[i] );
827 File_blocks[i] = NULL;
833 // Searches for a file. Follows all rules and precedence and searches
834 // CD's and pack files.
835 // Input: filespace - Filename & extension
836 // pathtype - See CF_TYPE_ defines in CFILE.H
837 // Output: pack_filename - Absolute path and filename of this file. Could be a packfile or the actual file.
839 // offset - Offset into pack file. 0 if not a packfile.
840 // Returns: If not found returns 0.
841 int cf_find_file_location( const char *filespec, int pathtype, char *pack_filename, int *size, int *offset, bool localize )
845 SDL_assert(filespec && strlen(filespec));
847 // see if we have something other than just a filename
848 // our current rules say that any file that specifies a direct
849 // path will try to be opened on that path. If that open
850 // fails, then we will open the file based on the extension
854 const char *toks = "/";
856 const char *toks = "/\\:";
859 // NOTE: full path should also include localization, if so desired
860 if ( strpbrk(filespec, toks) ) { // do we have a full path already?
861 FILE *fp = fopen(filespec, "rb" );
863 if ( size ) *size = filelength(fileno(fp));
864 if ( offset ) *offset = 0;
865 if ( pack_filename ) {
866 SDL_strlcpy( pack_filename, filespec, MAX_PATH_LEN );
872 return 0; // If they give a full path, fail if not found.
875 // Search the hard drive for files first.
876 int num_search_dirs = 0;
877 int search_order[CF_MAX_PATH_TYPES];
879 if ( CF_TYPE_SPECIFIED(pathtype) ) {
880 search_order[num_search_dirs++] = pathtype;
883 for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++) {
884 if ( i != pathtype ) {
885 search_order[num_search_dirs++] = i;
889 for (i=0; i<num_search_dirs; i++ ) {
890 char longname[MAX_PATH_LEN];
892 cf_create_default_path_string( longname, search_order[i], filespec, localize );
894 FILE *fp = fopen(longname, "rb" );
896 if ( size ) *size = filelength(fileno(fp));
897 if ( offset ) *offset = 0;
898 if ( pack_filename ) {
899 SDL_strlcpy( pack_filename, longname, MAX_PATH_LEN );
906 // Search the pak files and CD-ROM.
908 for (i=0; i<Num_files; i++ ) {
909 cf_file * f = cf_get_file(i);
911 // only search paths we're supposed to...
912 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
917 // create localized filespec
918 char loc_filespec[MAX_PATH_LEN];
919 SDL_strlcpy(loc_filespec, filespec, SDL_arraysize(loc_filespec));
920 lcl_add_dir_to_path_with_filename(loc_filespec, sizeof(loc_filespec));
922 if ( !SDL_strcasecmp(loc_filespec, f->name_ext) ) {
923 if ( size ) *size = f->size;
924 if ( offset ) *offset = f->pack_offset;
925 if ( pack_filename ) {
926 cf_root * r = cf_get_root(f->root_index);
928 SDL_strlcpy( pack_filename, r->path, MAX_PATH_LEN );
929 if ( f->pack_offset < 1 ) {
930 SDL_strlcat( pack_filename, Pathtypes[f->pathtype_index].path, MAX_PATH_LEN );
931 SDL_strlcat( pack_filename, DIR_SEPARATOR_STR, MAX_PATH_LEN );
932 SDL_strlcat( pack_filename, f->name_ext, MAX_PATH_LEN );
939 // file either not localized or localized version not found
940 if ( !SDL_strcasecmp(filespec, f->name_ext) ) {
941 if ( size ) *size = f->size;
942 if ( offset ) *offset = f->pack_offset;
943 if ( pack_filename ) {
944 cf_root * r = cf_get_root(f->root_index);
946 SDL_strlcpy( pack_filename, r->path, MAX_PATH_LEN );
947 if ( f->pack_offset < 1 ) {
949 if(strlen(Pathtypes[f->pathtype_index].path)){
950 SDL_strlcat( pack_filename, Pathtypes[f->pathtype_index].path, MAX_PATH_LEN );
951 SDL_strlcat( pack_filename, DIR_SEPARATOR_STR, MAX_PATH_LEN );
954 SDL_strlcat( pack_filename, f->name_ext, MAX_PATH_LEN );
965 // Returns true if filename matches filespec, else zero if not
966 int cf_matches_spec(const char *filespec, const char *filename)
968 const char *src_ext, *dst_ext;
970 src_ext = SDL_strchr(filespec, '.');
976 dst_ext = SDL_strchr(filename, '.');
980 return !SDL_strcasecmp(dst_ext, src_ext);
983 int (*Get_file_list_filter)(const char *filename) = NULL;
984 int Skip_packfile_search = 0;
986 int cf_file_already_in_list( int num_files, char **list, char *filename )
990 char name_no_extension[MAX_PATH_LEN];
992 SDL_strlcpy(name_no_extension, filename, SDL_arraysize(name_no_extension));
993 char *p = SDL_strchr( name_no_extension, '.' );
996 for (i=0; i<num_files; i++ ) {
997 if ( !SDL_strcasecmp(list[i], name_no_extension ) ) {
1006 // An alternative cf_get_file_list(), dynamic list version.
1007 // This one has a 'type', which is a CF_TYPE_* value. Because this specifies the directory
1008 // location, 'filter' only needs to be the filter itself, with no path information.
1009 // See above descriptions of cf_get_file_list() for more information about how it all works.
1010 int cf_get_file_list( int max, char **list, int pathtype, const char *filter, int sort, file_list_info *info )
1013 int i, l, num_files = 0, own_flag = 0;
1016 Get_file_list_filter = NULL;
1022 if (!info && (sort == CF_SORT_TIME)) {
1023 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1027 char filespec[MAX_PATH_LEN];
1030 cf_create_default_path_string( filespec, pathtype, NULL );
1035 dirp = opendir (filespec);
1037 while ((dir = readdir (dirp)) != NULL)
1039 if (num_files >= max)
1042 if (fnmatch(filter, dir->d_name, 0) != 0)
1045 char fn[MAX_PATH_LEN];
1046 SDL_snprintf(fn, MAX_PATH_LEN, "%s/%s", filespec, dir->d_name);
1049 if (stat(fn, &buf) == -1) {
1053 if (!S_ISREG(buf.st_mode)) {
1057 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1058 ptr = strrchr(dir->d_name, '.');
1060 l = ptr - dir->d_name;
1062 l = strlen(dir->d_name);
1064 list[num_files] = (char *)malloc(l + 1);
1065 SDL_strlcpy(list[num_files], dir->d_name, l+1);
1067 info[num_files].write_time = buf.st_mtime;
1076 cf_create_default_path_string( filespec, pathtype, filter );
1081 find_handle = _findfirst( filespec, &find );
1082 if (find_handle != -1) {
1084 if (num_files >= max)
1087 if (!(find.attrib & _A_SUBDIR)) {
1088 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1089 ptr = strrchr(find.name, '.');
1091 l = ptr - find.name;
1093 l = strlen(find.name);
1095 list[num_files] = (char *)malloc(l + 1);
1096 SDL_strlcpy(list[num_files], find.name, l+1);
1098 info[num_files].write_time = find.time_write;
1104 } while (!_findnext(find_handle, &find));
1106 _findclose( find_handle );
1110 // Search all the packfiles and CD.
1111 if ( !Skip_packfile_search ) {
1112 for (i=0; i<Num_files; i++ ) {
1113 cf_file * f = cf_get_file(i);
1115 // only search paths we're supposed to...
1116 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
1120 if (num_files >= max)
1123 if ( !cf_matches_spec( filter,f->name_ext)) {
1127 if ( cf_file_already_in_list(num_files,list,f->name_ext)) {
1131 if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1133 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1135 ptr = strrchr(f->name_ext, '.');
1137 l = ptr - f->name_ext;
1139 l = strlen(f->name_ext);
1141 list[num_files] = (char *)malloc(l + 1);
1142 SDL_strlcpy(list[num_files], f->name_ext, l+1);
1145 info[num_files].write_time = f->write_time;
1155 if (sort != CF_SORT_NONE) {
1156 cf_sort_filenames( num_files, list, sort, info );
1163 Get_file_list_filter = NULL;
1167 int cf_file_already_in_list_preallocated( int num_files, char arr[][MAX_FILENAME_LEN], const char *filename )
1171 char name_no_extension[MAX_PATH_LEN];
1173 SDL_strlcpy(name_no_extension, filename, SDL_arraysize(name_no_extension));
1174 char *p = SDL_strchr( name_no_extension, '.' );
1177 for (i=0; i<num_files; i++ ) {
1178 if ( !SDL_strcasecmp(arr[i], name_no_extension ) ) {
1187 // An alternative cf_get_file_list(), fixed array version.
1188 // This one has a 'type', which is a CF_TYPE_* value. Because this specifies the directory
1189 // location, 'filter' only needs to be the filter itself, with no path information.
1190 // See above descriptions of cf_get_file_list() for more information about how it all works.
1191 int cf_get_file_list_preallocated( int max, char arr[][MAX_FILENAME_LEN], char **list, int pathtype, const char *filter, int sort, file_list_info *info )
1193 int i, num_files = 0, own_flag = 0;
1196 Get_file_list_filter = NULL;
1201 for (i=0; i<max; i++) {
1205 sort = CF_SORT_NONE; // sorting of array directly not supported. Sorting done on list only
1208 if (!info && (sort == CF_SORT_TIME)) {
1209 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1213 char filespec[MAX_PATH_LEN];
1215 // Search the default directories
1217 cf_create_default_path_string( filespec, pathtype, NULL );
1222 dirp = opendir (filespec);
1224 while ((dir = readdir (dirp)) != NULL)
1226 if (num_files >= max)
1229 if (fnmatch(filter, dir->d_name, 0) != 0)
1232 char fn[MAX_PATH_LEN];
1233 SDL_snprintf(fn, MAX_PATH_LEN, "%s/%s", filespec, dir->d_name);
1236 if (stat(fn, &buf) == -1) {
1240 if (!S_ISREG(buf.st_mode)) {
1244 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1246 SDL_strlcpy(arr[num_files], dir->d_name, MAX_FILENAME_LEN);
1247 char *ptr = strrchr(arr[num_files], '.');
1253 info[num_files].write_time = buf.st_mtime;
1262 cf_create_default_path_string( filespec, pathtype, filter );
1267 find_handle = _findfirst( filespec, &find );
1268 if (find_handle != -1) {
1270 if (num_files >= max)
1273 if (!(find.attrib & _A_SUBDIR)) {
1275 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1277 SDL_strlcpy(arr[num_files], find.name, MAX_FILENAME_LEN);
1278 char *ptr = strrchr(arr[num_files], '.');
1284 info[num_files].write_time = find.time_write;
1291 } while (!_findnext(find_handle, &find));
1293 _findclose( find_handle );
1298 // Search all the packfiles and CD.
1299 if ( !Skip_packfile_search ) {
1300 for (i=0; i<Num_files; i++ ) {
1301 cf_file * f = cf_get_file(i);
1303 // only search paths we're supposed to...
1304 if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index) ) {
1308 if (num_files >= max)
1311 if ( !cf_matches_spec( filter,f->name_ext)) {
1315 if ( cf_file_already_in_list_preallocated( num_files, arr, f->name_ext )) {
1319 if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1321 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1323 SDL_strlcpy(arr[num_files], f->name_ext, MAX_FILENAME_LEN);
1324 char *ptr = strrchr(arr[num_files], '.');
1330 info[num_files].write_time = f->write_time;
1339 if (sort != CF_SORT_NONE) {
1341 cf_sort_filenames( num_files, list, sort, info );
1348 Get_file_list_filter = NULL;
1352 // Returns the default storage path for files given a
1353 // particular pathtype. In other words, the path to
1354 // the unpacked, non-cd'd, stored on hard drive path.
1355 // If filename isn't null it will also tack the filename
1356 // on the end, creating a completely valid filename.
1357 // Input: pathtype - CF_TYPE_??
1358 // filename - optional, if set, tacks the filename onto end of path.
1359 // Output: path - Fully qualified pathname.
1360 void cf_create_default_path_string( char *path, int pathtype, const char *filename, bool localize )
1363 const char *toks = "/";
1365 const char *toks = "/\\:";
1368 if ( filename && strpbrk(filename, toks) ) {
1369 // Already has full path
1370 SDL_strlcpy( path, filename, MAX_PATH_LEN );
1372 if ( cfile_init_paths() ) {
1373 SDL_strlcpy(path, (filename) ? filename : "", MAX_PATH_LEN);
1377 SDL_assert(CF_TYPE_SPECIFIED(pathtype));
1379 SDL_strlcpy(path, Cfile_user_dir, MAX_PATH_LEN);
1380 SDL_strlcat(path, Pathtypes[pathtype].path, MAX_PATH_LEN);
1382 // Don't add slash for root directory
1383 if (Pathtypes[pathtype].path[0] != '\0') {
1384 SDL_strlcat(path, DIR_SEPARATOR_STR, MAX_PATH_LEN);
1389 SDL_strlcat(path, filename, MAX_PATH_LEN);
1391 // localize filename
1393 // create copy of path
1394 char temp_path[MAX_PATH_LEN];
1395 SDL_strlcpy(temp_path, path, SDL_arraysize(temp_path));
1397 // localize the path
1398 lcl_add_dir_to_path_with_filename(path, MAX_PATH_LEN);
1400 // verify localized path
1401 FILE *fp = fopen(path, "rb");
1405 SDL_strlcpy(path, temp_path, SDL_arraysize(temp_path));
1412 // returns true if packfile has been indexed by CFILE (case-insensitive search)
1413 bool cf_has_packfile(const char *fn)
1416 char vp_name[CF_MAX_PATHNAME_LENGTH];
1424 if ( !SDL_strlen(fn) || (SDL_strlen(fn) < 4) ) {
1428 // add ".vp" if needed
1429 SDL_strlcpy(vp_name, fn, CF_MAX_PATHNAME_LENGTH);
1431 p = &vp_name[SDL_strlen(vp_name)-3];
1433 if ( SDL_strcasecmp(p, ".vp") ) {
1434 SDL_strlcat(vp_name, ".vp", CF_MAX_PATHNAME_LENGTH);
1437 // now see if we have the packfile
1438 for (i = 0; i < Num_roots; i++) {
1439 root = cf_get_root(i);
1441 if (root->roottype != CF_ROOTTYPE_PACK) {
1445 p = SDL_strrchr(root->path, DIR_SEPARATOR_CHAR);
1450 if ( !SDL_strcasecmp(p, vp_name) ) {