]> icculus.org git repositories - taylor/freespace2.git/blob - src/cfile/cfilesystem.cpp
platform and pstypes cleanup:
[taylor/freespace2.git] / src / cfile / cfilesystem.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
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
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/CFile/CfileSystem.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
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.
20  *
21  * $Log$
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
24  *
25  * Revision 1.10  2004/06/11 00:29:22  tigital
26  * byte-swapping changes for bigendian systems
27  *
28  * Revision 1.9  2003/05/27 03:03:11  taylor
29  * fix second root (gamedir) searching
30  *
31  * Revision 1.8  2003/02/20 17:41:07  theoddone33
32  * Userdir patch from Taylor Richards
33  *
34  * Revision 1.7  2002/06/22 23:57:39  relnev
35  * remove writable strings.
36  *
37  * fix compile for intel compiler.
38  *
39  * Revision 1.6  2002/06/09 04:41:15  relnev
40  * added copyright header
41  *
42  * Revision 1.5  2002/06/05 04:03:32  relnev
43  * finished cfilesystem.
44  *
45  * removed some old code.
46  *
47  * fixed mouse save off-by-one.
48  *
49  * sound cleanups.
50  *
51  * Revision 1.4  2002/05/28 17:26:57  theoddone33
52  * Fill in some timer and palette setting stubs.  Still no display
53  *
54  * Revision 1.3  2002/05/28 06:45:38  theoddone33
55  * Cleanup some stuff
56  *
57  * Revision 1.2  2002/05/28 06:28:20  theoddone33
58  * Filesystem mods, actually reads some data files now
59  *
60  * Revision 1.1.1.1  2002/05/03 03:28:08  root
61  * Initial import.
62  *
63  * 
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.
67  * 
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
72  * main hall.
73  * 
74  * 4     2/22/99 10:31p Andsager
75  * Get rid of unneeded includes.
76  * 
77  * 3     10/13/98 9:19a Andsager
78  * Add localization support to cfile.  Optional parameter with cfopen that
79  * looks for localized files.
80  * 
81  * 2     10/07/98 10:52a Dave
82  * Initial checkin.
83  * 
84  * 1     10/07/98 10:48a Dave
85  * 
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.
89  * 
90  * 13    6/23/98 4:18p Hoffoss
91  * Fixed some bugs with AC release build.
92  * 
93  * 12    5/20/98 10:46p John
94  * Added code that doesn't include duplicate filenames in any file list
95  * functions.
96  * 
97  * 11    5/14/98 2:14p Lawrance2
98  * Use filespec filtering for packfiles
99  * 
100  * 10    5/03/98 11:53a John
101  * Fixed filename case mangling.
102  * 
103  * 9     5/02/98 11:06p Allender
104  * correctly deal with pack pathnames
105  * 
106  * 8     5/01/98 11:41a Allender
107  * Fixed bug with mission saving in Fred.
108  * 
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.
112  * 
113  * 6     4/30/98 10:21p John
114  * Added code to cleanup cfilesystem
115  * 
116  * 5     4/30/98 10:18p John
117  * added source safe header
118  *
119  * $NoKeywords: $
120  */
121
122 #include <stdlib.h>
123 #include <string.h>
124 #include <stdio.h>
125 #include <errno.h>
126 #ifndef PLAT_UNIX
127 #include <io.h>
128 #include <direct.h>
129 #include <windows.h>
130 #include <winbase.h>            /* needed for memory mapping of file functions */
131 #else
132 #include <sys/types.h>
133 #include <dirent.h>
134 #include <fnmatch.h>
135 #include <sys/stat.h>
136 #include <unistd.h>
137 #endif
138
139 #include "pstypes.h"
140 //#include "outwnd.h"
141 //#include "vecmat.h"
142 //#include "timer.h"
143 #include "cfile.h"
144 #include "cfilesystem.h"
145 #include "localize.h"
146
147
148 #define CF_ROOTTYPE_PATH 0
149 #define CF_ROOTTYPE_PACK 1
150
151 //  Created by:
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
159 } cf_root;
160
161 // convenient type for sorting (see cf_build_pack_list())
162 typedef struct cf_root_sort { 
163         char                            path[CF_MAX_PATHNAME_LENGTH];
164         int                             roottype;
165         int                             cf_type;
166 } cf_root_sort;
167
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)
171
172 typedef struct cf_root_block {
173         cf_root                 roots[CF_NUM_ROOTS_PER_BLOCK];
174 } cf_root_block;
175
176 static int Num_roots = 0;
177 static cf_root_block  *Root_blocks[CF_MAX_ROOT_BLOCKS];
178   
179
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.
188 } cf_file;
189
190 #define CF_NUM_FILES_PER_BLOCK   256
191 #define CF_MAX_FILE_BLOCKS                      128                                             // Can store 256*128 = 32768 files
192
193 typedef struct cf_file_block {
194         cf_file                                         files[CF_NUM_FILES_PER_BLOCK];
195 } cf_file_block;
196
197 static int Num_files = 0;
198 static cf_file_block  *File_blocks[CF_MAX_FILE_BLOCKS];
199
200
201 // Return a pointer to to file 'index'.
202 cf_file *cf_get_file(int index)
203 {
204         int block = index / CF_NUM_FILES_PER_BLOCK;
205         int offset = index % CF_NUM_FILES_PER_BLOCK;
206
207         return &File_blocks[block]->files[offset];
208 }
209
210 // Create a new file and return a pointer to it.
211 cf_file *cf_create_file()
212 {
213         int block = Num_files / CF_NUM_FILES_PER_BLOCK;
214         int offset = Num_files % CF_NUM_FILES_PER_BLOCK;
215         
216         if ( File_blocks[block] == NULL )       {
217                 File_blocks[block] = (cf_file_block *)malloc( sizeof(cf_file_block) );
218                 SDL_assert( File_blocks[block] != NULL);
219         }
220
221         Num_files++;
222
223         return &File_blocks[block]->files[offset];
224 }
225
226 extern int cfile_inited;
227
228 // Create a new root and return a pointer to it.  The structure is assumed unitialized.
229 cf_root *cf_get_root(int n)
230 {
231         int block = n / CF_NUM_ROOTS_PER_BLOCK;
232         int offset = n % CF_NUM_ROOTS_PER_BLOCK;
233
234         if (!cfile_inited)
235                 return NULL;
236
237         return &Root_blocks[block]->roots[offset];
238 }
239
240
241 // Create a new root and return a pointer to it.  The structure is assumed unitialized.
242 cf_root *cf_create_root()
243 {
244         int block = Num_roots / CF_NUM_ROOTS_PER_BLOCK;
245         int offset = Num_roots % CF_NUM_ROOTS_PER_BLOCK;
246         
247         if ( Root_blocks[block] == NULL )       {
248                 Root_blocks[block] = (cf_root_block *)malloc( sizeof(cf_root_block) );
249                 SDL_assert(Root_blocks[block] != NULL);
250         }
251
252         Num_roots++;
253
254         return &Root_blocks[block]->roots[offset];
255 }
256
257 // return the # of packfiles which exist
258 int cf_get_packfile_count(cf_root *root)
259 {
260         char filespec[MAX_PATH_LEN];
261         int i;
262         int packfile_count;
263
264         // count up how many packfiles we're gonna have
265         packfile_count = 0;
266         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
267 #ifdef PLAT_UNIX
268                 SDL_strlcpy( filespec, root->path, sizeof(filespec) );
269
270                 if(strlen(Pathtypes[i].path)){
271                         SDL_strlcat( filespec, Pathtypes[i].path, sizeof(filespec) );
272                         SDL_strlcat( filespec, "/", sizeof(filespec) );
273                 }
274
275                 DIR *dirp;
276                 struct dirent *dir;
277
278                 dirp = opendir (filespec);
279                 if ( dirp ) {
280                         while ((dir = readdir (dirp)) != NULL)
281                         {
282                                 if (!fnmatch ("*.vp", dir->d_name, 0))
283                                         packfile_count++;
284                         }
285                         closedir(dirp);
286                 }
287 #else
288                 SDL_strlcpy( filespec, root->path, sizeof(filespec) );
289
290                 if(strlen(Pathtypes[i].path)){
291                         SDL_strlcat( filespec, Pathtypes[i].path, sizeof(filespec) );
292                         SDL_strlcat( filespec, "\\", sizeof(filespec) );
293                 }
294
295                 SDL_strlcat( filespec, "*.vp", sizeof(filespec) );
296
297                 int find_handle;
298                 _finddata_t find;
299                 
300                 find_handle = _findfirst( filespec, &find );
301
302                 if (find_handle != -1) {
303                         do {
304                                 if (!(find.attrib & _A_SUBDIR)) {
305                                         packfile_count++;
306                                 }
307
308                         } while (!_findnext(find_handle, &find));
309
310                         _findclose( find_handle );
311                 }       
312 #endif
313         }       
314
315         return packfile_count;
316 }
317
318 // packfile sort function
319 int cf_packfile_sort_func(const void *elem1, const void *elem2)
320 {
321         cf_root_sort *r1, *r2;
322         r1 = (cf_root_sort*)elem1;
323         r2 = (cf_root_sort*)elem2;
324
325         // if the 2 directory types are the same, do a string compare
326         if(r1->cf_type == r2->cf_type){
327                 return SDL_strcasecmp(r1->path, r2->path);
328         }
329
330         // otherwise return them in order of CF_TYPE_* precedence
331         return (r1->cf_type < r2->cf_type) ? -1 : 1;
332 }
333
334 // Go through a root and look for pack files
335 void cf_build_pack_list( cf_root *root )
336 {
337         char filespec[MAX_PATH_LEN];
338         int i;
339         cf_root_sort *temp_roots_sort, *rptr_sort;
340         int temp_root_count, root_index;
341
342         // determine how many packfiles there are
343         temp_root_count = cf_get_packfile_count(root);
344         if(temp_root_count <= 0){
345                 return;
346         }
347
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){
351                 Int3();
352                 return;
353         }
354
355         // now just setup all the root info
356         root_index = 0;
357         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
358
359 #ifdef PLAT_UNIX
360                 SDL_strlcpy( filespec, root->path, sizeof(filespec) );
361
362                 if(strlen(Pathtypes[i].path)){
363                         SDL_strlcat( filespec, Pathtypes[i].path, sizeof(filespec) );
364                         SDL_strlcat( filespec, "/", sizeof(filespec) );
365                 }
366
367                 DIR *dirp;
368                 struct dirent *dir;
369
370                 dirp = opendir (filespec);
371                 if ( dirp ) {
372                         while ((dir = readdir (dirp)) != NULL)
373                         {
374                                 if (!fnmatch ("*.vp", dir->d_name, 0))
375                                 {
376                                         SDL_assert(root_index < temp_root_count);
377
378                                         char fn[MAX_PATH_LEN];
379                                         SDL_snprintf(fn, sizeof(fn), "%s/%s", filespec, dir->d_name);
380                                                         
381                                         struct stat buf;
382                                         if (stat(fn, &buf) == -1) {
383                                                 continue;
384                                         }
385                                         
386                                         if (!S_ISREG(buf.st_mode)) {
387                                                 continue;
388                                         }
389                                         
390                                         // get a temp pointer
391                                         rptr_sort = &temp_roots_sort[root_index++];
392
393                                         // fill in all the proper info
394                                         SDL_strlcpy(rptr_sort->path, root->path, sizeof(rptr_sort->path));
395
396                                         if(strlen(Pathtypes[i].path)){
397                                                 SDL_strlcat(rptr_sort->path, Pathtypes[i].path, sizeof(rptr_sort->path));
398                                                 SDL_strlcat(rptr_sort->path, "/", sizeof(rptr_sort->path));
399                                         }
400
401                                         SDL_strlcat(rptr_sort->path, dir->d_name, sizeof(rptr_sort->path));
402                                         rptr_sort->roottype = CF_ROOTTYPE_PACK;
403                                         rptr_sort->cf_type = i;
404                                 }
405                         }
406                         closedir(dirp);
407                 }
408 #else
409                 SDL_strlcpy( filespec, root->path, sizeof(filespec) );
410
411                 if(strlen(Pathtypes[i].path)){
412                         SDL_strlcat( filespec, Pathtypes[i].path, sizeof(filespec) );
413                         SDL_strlcat( filespec, "\\", sizeof(filespec) );
414                 }
415                 SDL_strlcat( filespec, "*.vp", sizeof(filespec) );
416                 int find_handle;
417                 _finddata_t find;
418                 
419                 find_handle = _findfirst( filespec, &find );
420
421                 if (find_handle != -1) {
422                         do {
423                                 // add the new item
424                                 if (!(find.attrib & _A_SUBDIR)) {                                       
425                                         SDL_assert(root_index < temp_root_count);
426
427                                         // get a temp pointer
428                                         rptr_sort = &temp_roots_sort[root_index++];
429
430                                         // fill in all the proper info
431                                         SDL_strlcpy(rptr_sort->path, root->path, sizeof(rptr_sort->path));
432
433                                         if(strlen(Pathtypes[i].path)){
434                                                 SDL_strlcat(rptr_sort->path, Pathtypes[i].path, sizeof(rptr_sort->path) );
435                                                 SDL_strlcat(rptr_sort->path, "\\", sizeof(rptr_sort->path));
436                                         }
437
438                                         SDL_strlcat(rptr_sort->path, find.name, sizeof(rptr_sort->path) );
439                                         rptr_sort->roottype = CF_ROOTTYPE_PACK;
440                                         rptr_sort->cf_type = i;
441                                 }
442
443                         } while (!_findnext(find_handle, &find));
444
445                         _findclose( find_handle );
446                 }       
447 #endif
448         }       
449
450         // these should always be the same
451         SDL_assert(root_index == temp_root_count);
452
453         // sort tht roots
454         qsort(temp_roots_sort,  temp_root_count, sizeof(cf_root_sort), cf_packfile_sort_func);
455
456         // now insert them all into the real root list properly
457         cf_root *new_root;
458         for(i=0; i<temp_root_count; i++){               
459                 new_root = cf_create_root();
460                 SDL_strlcpy( new_root->path, root->path, sizeof(new_root->path) );
461
462                 // mwa -- 4/2/98 put in the next 2 lines because the path name needs to be there
463                 // to find the files.
464                 SDL_strlcpy(new_root->path, temp_roots_sort[i].path, sizeof(new_root->path));
465                 new_root->roottype = CF_ROOTTYPE_PACK;          
466         }
467
468         // free up the temp list
469         free(temp_roots_sort);
470 }
471
472
473 void cf_build_root_list(const char *extras_dir)
474 {
475         Num_roots = 0;
476
477         cf_root *root;
478
479         // ================================================================
480         // have user's writable directory as default for loading and saving files
481         root = cf_create_root();
482         SDL_strlcpy( root->path, Cfile_user_dir, sizeof(root->path) );
483
484         // do we already have a slash? as in the case of a root directory install
485         if(strlen(root->path) && (root->path[strlen(root->path)-1] != DIR_SEPARATOR_CHAR)){
486                 SDL_strlcat(root->path, DIR_SEPARATOR_STR, sizeof(root->path));         // put trailing backslash on for easier path construction
487         }
488         root->roottype = CF_ROOTTYPE_PATH;
489
490         //======================================================
491         // then check any VP files under the directory.
492         cf_build_pack_list(root);
493
494         //======================================================
495         // Next, use the executable's directory for game data
496         root = cf_create_root();
497         SDL_strlcpy( root->path, Cfile_root_dir, sizeof(root->path) );
498
499         // do we already have a slash? as in the case of a root directory install
500         if(strlen(root->path) && (root->path[strlen(root->path)-1] != DIR_SEPARATOR_CHAR)){
501                 SDL_strlcat(root->path, DIR_SEPARATOR_STR, sizeof(root->path));         // put trailing backslash on for easier path construction
502         }
503         root->roottype = CF_ROOTTYPE_PATH;
504
505         //======================================================
506         // then check any VP files under the current directory.
507         cf_build_pack_list(root);
508
509
510         //======================================================
511         // Check the real CD if one...
512         if ( extras_dir && strlen(extras_dir) ) {
513                 root = cf_create_root();
514                 SDL_strlcpy( root->path, extras_dir, sizeof(root->path) );
515                 root->roottype = CF_ROOTTYPE_PATH;
516
517                 //======================================================
518                 // Next, check any VP files in the CD-ROM directory.
519                 cf_build_pack_list(root);
520         }
521 }
522
523 // Given a lower case list of file extensions 
524 // separated by spaces, return zero if ext is
525 // not in the list.
526 int is_ext_in_list( const char *ext_list, char *ext )
527 {
528         char tmp_ext[128];
529
530         SDL_strlcpy( tmp_ext, ext, sizeof(tmp_ext) );
531         SDL_strlwr(tmp_ext);
532         if ( strstr(ext_list, tmp_ext ))        {
533                 return 1;
534         }       
535
536         return 0;
537 }
538
539 void cf_search_root_path(int root_index)
540 {
541         int i;
542
543         cf_root *root = cf_get_root(root_index);
544
545         mprintf(( "Searching root '%s'\n", root->path ));
546
547         char search_path[CF_MAX_PATHNAME_LENGTH];
548
549         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
550
551                 SDL_strlcpy( search_path, root->path, sizeof(search_path) );
552
553                 if(strlen(Pathtypes[i].path)){
554                         SDL_strlcat( search_path, Pathtypes[i].path, sizeof(search_path) );
555                         SDL_strlcat( search_path, DIR_SEPARATOR_STR, sizeof(search_path) );
556                 }
557
558 #ifdef PLAT_UNIX
559                 DIR *dirp;
560                 struct dirent *dir;
561
562                 // only get pilots from the primary root as there could be nasty
563                 // permission issues otherwise
564                 if ( (root_index > 0) && ((Pathtypes[i].index == CF_TYPE_SINGLE_PLAYERS)
565                                         || (Pathtypes[i].index == CF_TYPE_MULTI_PLAYERS)) ) {
566                         break;
567                 }
568
569                 dirp = opendir (search_path);
570                 if ( dirp ) {
571                         while ((dir = readdir (dirp)) != NULL)
572                         {
573                                 if (!fnmatch ("*.*", dir->d_name, 0))
574                                 {
575                                         char fn[MAX_PATH_LEN];
576                                         SDL_snprintf(fn, MAX_PATH_LEN, "%s/%s", search_path, dir->d_name);
577                                                         
578                                         struct stat buf;
579                                         if (stat(fn, &buf) == -1) {
580                                                 continue;
581                                         }
582                                         
583                                         if (!S_ISREG(buf.st_mode)) {
584                                                 continue;
585                                         }
586                                         
587                                         char *ext = SDL_strchr( dir->d_name, '.' );
588                                         if ( ext )      {
589                                                 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) )   {
590                                                         // Found a file!!!!
591                                                         cf_file *file = cf_create_file();
592
593                                                         SDL_strlcpy( file->name_ext, dir->d_name, sizeof(file->name_ext) );
594                                                         file->root_index = root_index;
595                                                         file->pathtype_index = i;
596
597
598                                                         file->write_time = buf.st_mtime;
599                                                         file->size = buf.st_size;
600
601                                                         file->pack_offset = 0;                  // Mark as a non-packed file
602
603                                                         //mprintf(( "Found file '%s'\n", file->name_ext ));
604                                                 }
605                                         }
606                                 }
607                         }
608                         closedir(dirp);
609                 }
610 #else
611                 SDL_strlcat( search_path, "*.*", sizeof(search_path) );
612
613                 int find_handle;
614                 _finddata_t find;
615                 
616                 find_handle = _findfirst( search_path, &find );
617
618                 if (find_handle != -1) {
619                         do {
620                                 if (!(find.attrib & _A_SUBDIR)) {
621
622                                         char *ext = SDL_strchr( find.name, '.' );
623                                         if ( ext )      {
624                                                 if ( is_ext_in_list( Pathtypes[i].extensions, ext ) )   {
625                                                         // Found a file!!!!
626                                                         cf_file *file = cf_create_file();
627
628                                                         SDL_strlcpy( file->name_ext, find.name, sizeof(file->name_ext) );
629                                                         file->root_index = root_index;
630                                                         file->pathtype_index = i;
631                                                         file->write_time = find.time_write;
632                                                         file->size = find.size;
633                                                         file->pack_offset = 0;                  // Mark as a non-packed file
634
635                                                         //mprintf(( "Found file '%s'\n", file->name_ext ));
636
637                                                 }
638                                         }
639
640                                 }
641
642                         } while (!_findnext(find_handle, &find));
643
644                         _findclose( find_handle );
645                 }
646 #endif
647
648         }
649 }
650
651
652 typedef struct VP_FILE_HEADER {
653         char id[4];
654         int version;
655         int index_offset;
656         int num_files;
657 } VP_FILE_HEADER;
658
659 typedef struct VP_FILE {
660         int     offset;
661         int     size;
662         char    filename[32];
663         fs_time_t write_time;
664 } VP_FILE;
665
666 void cf_search_root_pack(int root_index)
667 {
668         int i;
669
670         cf_root *root = cf_get_root(root_index);
671
672         //mprintf(( "Searching root pack '%s'\n", root->path ));
673
674         // Open data
675                 
676         FILE *fp = fopen( root->path, "rb" );
677         // Read the file header
678         if (!fp) {
679                 return;
680         }
681
682         VP_FILE_HEADER VP_header;
683
684         SDL_assert( sizeof(VP_header) == 16 );
685         fread(&VP_header, 1, sizeof(VP_header), fp);
686
687     VP_header.version = INTEL_INT( VP_header.version);
688     VP_header.index_offset = INTEL_INT( VP_header.index_offset);
689     VP_header.num_files = INTEL_INT( VP_header.num_files);
690         
691         // Read index info
692         fseek(fp, VP_header.index_offset, SEEK_SET);
693
694         char search_path[CF_MAX_PATHNAME_LENGTH];
695
696         SDL_strlcpy( search_path, "", sizeof(search_path) );
697         
698         // Go through all the files
699         for (i=0; i<VP_header.num_files; i++ )  {
700                 VP_FILE find;
701
702                 fread( &find, sizeof(VP_FILE), 1, fp );
703
704         find.offset = INTEL_INT( find.offset );
705         find.size = INTEL_INT( find.size );
706                 find.write_time = INTEL_INT(find.write_time);
707
708                 if ( find.size == 0 )   {
709                         if ( !SDL_strcasecmp( find.filename, ".." ))    {
710                                 int l = strlen(search_path);
711                                 char *p = &search_path[l-1];
712                                 while( (p > search_path) && (*p != DIR_SEPARATOR_CHAR) )        {
713                                         p--;
714                                 }
715                                 *p = 0;
716                         } else {
717                                 if ( strlen(search_path)        )       {
718                                         SDL_strlcat( search_path,       DIR_SEPARATOR_STR, sizeof(search_path) );
719                                 }
720                                 SDL_strlcat( search_path, find.filename, sizeof(search_path) );
721                         }
722
723                         //mprintf(( "Current dir = '%s'\n", search_path ));
724                 } else {
725         
726                         int j;
727                         for (j=CF_TYPE_ROOT; j<CF_MAX_PATH_TYPES; j++ ) {
728
729                                 if ( !SDL_strcasecmp( search_path, Pathtypes[j].path )) {
730
731                                         char *ext = SDL_strchr( find.filename, '.' );
732                                         if ( ext )      {
733                                                 if ( is_ext_in_list( Pathtypes[j].extensions, ext ) )   {
734                                                         // Found a file!!!!
735                                                         cf_file *file = cf_create_file();
736                                                         
737                                                         SDL_strlcpy( file->name_ext, find.filename, sizeof(file->name_ext) );
738                                                         file->root_index = root_index;
739                                                         file->pathtype_index = j;
740                                                         file->write_time = find.write_time;
741                                                         file->size = find.size;
742                                                         file->pack_offset = find.offset;                        // Mark as a non-packed file
743
744                                                         //mprintf(( "Found pack file '%s'\n", file->name_ext ));
745                                                 }
746                                         }
747                                         
748
749                                 }
750                         }
751
752                 }
753         }
754         fclose(fp);
755 }
756
757
758 void cf_build_file_list()
759 {
760         int i;
761
762         Num_files = 0;
763
764         // For each root, find all files...
765         for (i=1; i<Num_roots; i++ )    {
766                 cf_root *root = cf_get_root(i);
767                 if ( root->roottype == CF_ROOTTYPE_PATH )       {
768                         cf_search_root_path(i);
769                 } else if ( root->roottype == CF_ROOTTYPE_PACK )        {
770                         cf_search_root_pack(i);
771                 }
772         }
773
774 }
775
776
777 void cf_build_secondary_filelist(const char *extras_dir)
778 {
779         int i;
780
781         // Assume no files
782         Num_roots = 0;
783         Num_files = 0;
784
785         // Init the path types
786         for (i=0; i<CF_MAX_PATH_TYPES; i++ )    {
787                 SDL_assert( Pathtypes[i].index == i );
788 #if 0 /* they are already lowercased -- SBF */
789                 if ( Pathtypes[i].extensions )  {
790                         SDL_strlwr(Pathtypes[i].extensions);
791                 }
792 #endif          
793         }
794         
795         // Init the root blocks
796         for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ )   {
797                 Root_blocks[i] = NULL;
798         }
799
800         // Init the file blocks 
801         for (i=0; i<CF_MAX_FILE_BLOCKS; i++ )   {
802                 File_blocks[i] = NULL;
803         }
804
805         mprintf(( "Building file index...\n" ));
806         
807         // build the list of searchable roots
808         cf_build_root_list(extras_dir);
809
810         // build the list of files themselves
811         cf_build_file_list();
812
813         mprintf(( "Found %d roots and %d files.\n", Num_roots, Num_files ));
814 }
815
816 void cf_free_secondary_filelist()
817 {
818         int i;
819
820         // Free the root blocks
821         for (i=0; i<CF_MAX_ROOT_BLOCKS; i++ )   {
822                 if ( Root_blocks[i] )   {
823                         free( Root_blocks[i] );
824                         Root_blocks[i] = NULL;
825                 }
826         }
827         Num_roots = 0;
828
829         // Init the file blocks 
830         for (i=0; i<CF_MAX_FILE_BLOCKS; i++ )   {
831                 if ( File_blocks[i] )   {
832                         free( File_blocks[i] );
833                         File_blocks[i] = NULL;
834                 }
835         }
836         Num_files = 0;
837 }
838
839 // Searches for a file.   Follows all rules and precedence and searches
840 // CD's and pack files.
841 // Input:  filespace   - Filename & extension
842 //         pathtype    - See CF_TYPE_ defines in CFILE.H
843 // Output: pack_filename - Absolute path and filename of this file.   Could be a packfile or the actual file.
844 //         size        - File size
845 //         offset      - Offset into pack file.  0 if not a packfile.
846 // Returns: If not found returns 0.
847 int cf_find_file_location( const char *filespec, int pathtype, char *pack_filename, int *size, int *offset, bool localize )
848 {
849         int i;
850
851         SDL_assert(filespec && strlen(filespec));
852
853         // see if we have something other than just a filename
854         // our current rules say that any file that specifies a direct
855         // path will try to be opened on that path.  If that open
856         // fails, then we will open the file based on the extension
857         // of the file
858
859         // NOTE: full path should also include localization, if so desired
860 #ifdef PLAT_UNIX
861         if ( strpbrk(filespec, "/") ) {
862 #else
863         if ( strpbrk(filespec,"/\\:")  ) {              // do we have a full path already?
864 #endif
865                 FILE *fp = fopen(filespec, "rb" );
866                 if (fp) {
867                         if ( size ) *size = filelength(fileno(fp));
868                         if ( offset ) *offset = 0;
869                         if ( pack_filename ) {
870                                 SDL_strlcpy( pack_filename, filespec, MAX_PATH_LEN );
871                         }                               
872                         fclose(fp);
873                         return 1;               
874                 }
875
876                 return 0;               // If they give a full path, fail if not found.
877         }
878
879         // Search the hard drive for files first.
880         int num_search_dirs = 0;
881         int search_order[CF_MAX_PATH_TYPES];
882
883         if ( CF_TYPE_SPECIFIED(pathtype) )      {
884                 search_order[num_search_dirs++] = pathtype;
885         }
886
887         for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++)  {
888                 if ( i != pathtype )    {
889                         search_order[num_search_dirs++] = i;
890                 }
891         }
892
893         for (i=0; i<num_search_dirs; i++ )      {
894                 char longname[MAX_PATH_LEN];
895
896                 cf_create_default_path_string( longname, search_order[i], filespec, localize );
897
898                 FILE *fp = fopen(longname, "rb" );
899                 if (fp) {
900                         if ( size ) *size = filelength(fileno(fp));
901                         if ( offset ) *offset = 0;
902                         if ( pack_filename ) {
903                                 SDL_strlcpy( pack_filename, longname, MAX_PATH_LEN );
904                         }                               
905                         fclose(fp);
906                         return 1;               
907                 }
908         } 
909
910         // Search the pak files and CD-ROM.
911
912                 for (i=0; i<Num_files; i++ )    {
913                         cf_file * f = cf_get_file(i);
914
915                         // only search paths we're supposed to...
916                         if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index)  )    {
917                                 continue;
918                         }
919
920                         if (localize) {
921                                 // create localized filespec
922                                 char loc_filespec[MAX_PATH_LEN];
923                                 SDL_strlcpy(loc_filespec, filespec, sizeof(loc_filespec));
924                                 lcl_add_dir_to_path_with_filename(loc_filespec, sizeof(loc_filespec));
925                         
926                                 if ( !SDL_strcasecmp(loc_filespec, f->name_ext) )       {
927                                         if ( size ) *size = f->size;
928                                         if ( offset ) *offset = f->pack_offset;
929                                         if ( pack_filename ) {
930                                                 cf_root * r = cf_get_root(f->root_index);
931
932                                                 SDL_strlcpy( pack_filename, r->path, MAX_PATH_LEN );
933                                                 if ( f->pack_offset < 1 )       {
934                                                         SDL_strlcat( pack_filename, Pathtypes[f->pathtype_index].path, MAX_PATH_LEN );
935                                                         SDL_strlcat( pack_filename, DIR_SEPARATOR_STR, MAX_PATH_LEN );
936                                                         SDL_strlcat( pack_filename, f->name_ext, MAX_PATH_LEN );
937                                                 }
938                                         }                               
939                                         return 1;               
940                                 }
941                         }
942
943                         // file either not localized or localized version not found
944                         if ( !SDL_strcasecmp(filespec, f->name_ext) )   {
945                                 if ( size ) *size = f->size;
946                                 if ( offset ) *offset = f->pack_offset;
947                                 if ( pack_filename ) {
948                                         cf_root * r = cf_get_root(f->root_index);
949
950                                         SDL_strlcpy( pack_filename, r->path, MAX_PATH_LEN );
951                                         if ( f->pack_offset < 1 )       {
952
953                                                 if(strlen(Pathtypes[f->pathtype_index].path)){
954                                                         SDL_strlcat( pack_filename, Pathtypes[f->pathtype_index].path, MAX_PATH_LEN );
955                                                         SDL_strlcat( pack_filename, DIR_SEPARATOR_STR, MAX_PATH_LEN );
956                                                 }
957
958                                                 SDL_strlcat( pack_filename, f->name_ext, MAX_PATH_LEN );
959                                         }
960                                 }                               
961                                 return 1;               
962                         }
963                 }
964         
965         return 0;
966 }
967
968
969 // Returns true if filename matches filespec, else zero if not
970 int cf_matches_spec(const char *filespec, const char *filename)
971 {
972         const char *src_ext, *dst_ext;
973
974         src_ext = SDL_strchr(filespec, '.');
975         if (!src_ext)
976                 return 1;
977         if (*src_ext == '*')
978                 return 1;
979
980         dst_ext = SDL_strchr(filename, '.');
981         if (!dst_ext)
982                 return 1;
983         
984         return !SDL_strcasecmp(dst_ext, src_ext);
985 }
986
987 int (*Get_file_list_filter)(const char *filename) = NULL;
988 int Skip_packfile_search = 0;
989
990 int cf_file_already_in_list( int num_files, char **list, char *filename )
991 {
992         int i;
993
994         char name_no_extension[MAX_PATH_LEN];
995
996         SDL_strlcpy(name_no_extension, filename, sizeof(name_no_extension));
997         char *p = SDL_strchr( name_no_extension, '.' );
998         if ( p ) *p = 0;
999
1000         for (i=0; i<num_files; i++ )    {
1001                 if ( !SDL_strcasecmp(list[i], name_no_extension ) )     {
1002                         // Match found!
1003                         return 1;
1004                 }
1005         }
1006         // Not found
1007         return 0;
1008 }
1009
1010 // An alternative cf_get_file_list(), dynamic list version.
1011 // This one has a 'type', which is a CF_TYPE_* value.  Because this specifies the directory
1012 // location, 'filter' only needs to be the filter itself, with no path information.
1013 // See above descriptions of cf_get_file_list() for more information about how it all works.
1014 int cf_get_file_list( int max, char **list, int pathtype, const char *filter, int sort, file_list_info *info )
1015 {
1016         char *ptr;
1017         int i, l, num_files = 0, own_flag = 0;
1018 #ifndef PLAT_UNIX
1019         int find_handle;
1020         _finddata_t find;
1021 #endif
1022
1023         if (max < 1) {
1024                 Get_file_list_filter = NULL;
1025                 return 0;
1026         }
1027
1028         SDL_assert(list);
1029
1030         if (!info && (sort == CF_SORT_TIME)) {
1031                 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1032                 own_flag = 1;
1033         }
1034
1035         char filespec[MAX_PATH_LEN];
1036
1037 #ifdef PLAT_UNIX
1038         cf_create_default_path_string( filespec, pathtype, NULL );
1039
1040                 DIR *dirp;
1041                 struct dirent *dir;
1042
1043                 dirp = opendir (filespec);
1044                 if ( dirp ) {
1045                         while ((dir = readdir (dirp)) != NULL)
1046                         {
1047                                 if (num_files >= max)
1048                                         break;
1049                                 
1050                                 if (fnmatch(filter, dir->d_name, 0) != 0)
1051                                         continue;
1052                                 
1053                                 char fn[MAX_PATH_LEN];
1054                                 SDL_snprintf(fn, MAX_PATH_LEN, "%s/%s", filespec, dir->d_name);
1055                                                         
1056                                 struct stat buf;
1057                                 if (stat(fn, &buf) == -1) {
1058                                         continue;
1059                                 }
1060                                 
1061                                 if (!S_ISREG(buf.st_mode)) {
1062                                         continue;
1063                                 }
1064                                 
1065                                 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1066                                         ptr = strrchr(dir->d_name, '.');
1067                                         if (ptr)
1068                                                 l = ptr - dir->d_name;
1069                                         else
1070                                                 l = strlen(dir->d_name);
1071
1072                                         list[num_files] = (char *)malloc(l + 1);
1073                                         SDL_strlcpy(list[num_files], dir->d_name, l+1);
1074                                         if (info)
1075                                                 info[num_files].write_time = buf.st_mtime;
1076
1077                                         num_files++;
1078                                 }
1079                         }
1080                         
1081                         closedir(dirp);
1082                 }
1083 #else
1084         cf_create_default_path_string( filespec, pathtype, filter );
1085         
1086         find_handle = _findfirst( filespec, &find );
1087         if (find_handle != -1) {
1088                 do {
1089                         if (num_files >= max)
1090                                 break;
1091
1092                         if (!(find.attrib & _A_SUBDIR)) {
1093                                 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1094                                         ptr = strrchr(find.name, '.');
1095                                         if (ptr)
1096                                                 l = ptr - find.name;
1097                                         else
1098                                                 l = strlen(find.name);
1099
1100                                         list[num_files] = (char *)malloc(l + 1);
1101                                         SDL_strlcpy(list[num_files], find.name, l+1);
1102                                         if (info)
1103                                                 info[num_files].write_time = find.time_write;
1104
1105                                         num_files++;
1106                                 }
1107                         }
1108
1109                 } while (!_findnext(find_handle, &find));
1110
1111                 _findclose( find_handle );
1112         }
1113 #endif
1114
1115         // Search all the packfiles and CD.
1116         if ( !Skip_packfile_search )    {
1117                 for (i=0; i<Num_files; i++ )    {
1118                         cf_file * f = cf_get_file(i);
1119
1120                         // only search paths we're supposed to...
1121                         if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index)  )    {
1122                                 continue;
1123                         }
1124
1125                         if (num_files >= max)
1126                                 break;
1127
1128                         if ( !cf_matches_spec( filter,f->name_ext))     {
1129                                 continue;
1130                         }
1131
1132                         if ( cf_file_already_in_list(num_files,list,f->name_ext))       {
1133                                 continue;
1134                         }
1135
1136                         if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1137
1138                                 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1139
1140                                         ptr = strrchr(f->name_ext, '.');
1141                                         if (ptr)
1142                                                 l = ptr - f->name_ext;
1143                                         else
1144                                                 l = strlen(f->name_ext);
1145
1146                                         list[num_files] = (char *)malloc(l + 1);
1147                                         SDL_strlcpy(list[num_files], f->name_ext, l+1);
1148
1149                                 if (info)       {
1150                                         info[num_files].write_time = f->write_time;
1151                                 }
1152
1153                                 num_files++;
1154                         }
1155
1156                 }
1157         }
1158
1159
1160         if (sort != CF_SORT_NONE)       {
1161                 cf_sort_filenames( num_files, list, sort, info );
1162         }
1163
1164         if (own_flag)   {
1165                 free(info);
1166         }
1167
1168         Get_file_list_filter = NULL;
1169         return num_files;
1170 }
1171
1172 int cf_file_already_in_list_preallocated( int num_files, char arr[][MAX_FILENAME_LEN], const char *filename )
1173 {
1174         int i;
1175
1176         char name_no_extension[MAX_PATH_LEN];
1177
1178         SDL_strlcpy(name_no_extension, filename, sizeof(name_no_extension));
1179         char *p = SDL_strchr( name_no_extension, '.' );
1180         if ( p ) *p = 0;
1181
1182         for (i=0; i<num_files; i++ )    {
1183                 if ( !SDL_strcasecmp(arr[i], name_no_extension ) )      {
1184                         // Match found!
1185                         return 1;
1186                 }
1187         }
1188         // Not found
1189         return 0;
1190 }
1191
1192 // An alternative cf_get_file_list(), fixed array version.
1193 // This one has a 'type', which is a CF_TYPE_* value.  Because this specifies the directory
1194 // location, 'filter' only needs to be the filter itself, with no path information.
1195 // See above descriptions of cf_get_file_list() for more information about how it all works.
1196 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 )
1197 {
1198         int i, num_files = 0, own_flag = 0;
1199
1200         if (max < 1) {
1201                 Get_file_list_filter = NULL;
1202                 return 0;
1203         }
1204
1205         if (list) {
1206                 for (i=0; i<max; i++)   {
1207                         list[i] = arr[i];
1208                 }
1209         } else {
1210                 sort = CF_SORT_NONE;  // sorting of array directly not supported.  Sorting done on list only
1211         }
1212
1213         if (!info && (sort == CF_SORT_TIME)) {
1214                 info = (file_list_info *) malloc(sizeof(file_list_info) * max);
1215                 own_flag = 1;
1216         }
1217
1218         char filespec[MAX_PATH_LEN];
1219
1220         // Search the default directories
1221 #ifdef PLAT_UNIX
1222         cf_create_default_path_string( filespec, pathtype, NULL );
1223         
1224                 DIR *dirp;
1225                 struct dirent *dir;
1226
1227                 dirp = opendir (filespec);
1228                 if ( dirp ) {
1229                         while ((dir = readdir (dirp)) != NULL)
1230                         {
1231                                 if (num_files >= max)
1232                                         break;
1233                                 
1234                                 if (fnmatch(filter, dir->d_name, 0) != 0)
1235                                         continue;
1236                                 
1237                                 char fn[MAX_PATH_LEN];
1238                                 SDL_snprintf(fn, MAX_PATH_LEN, "%s/%s", filespec, dir->d_name);
1239                                                         
1240                                 struct stat buf;
1241                                 if (stat(fn, &buf) == -1) {
1242                                         continue;
1243                                 }
1244                                 
1245                                 if (!S_ISREG(buf.st_mode)) {
1246                                         continue;
1247                                 }
1248                                 
1249                                 if ( !Get_file_list_filter || (*Get_file_list_filter)(dir->d_name) ) {
1250
1251                                         SDL_strlcpy(arr[num_files], dir->d_name, MAX_FILENAME_LEN);
1252                                         char *ptr = strrchr(arr[num_files], '.');
1253                                         if ( ptr ) {
1254                                                 *ptr = 0;
1255                                         }
1256
1257                                         if (info)       {
1258                                                 info[num_files].write_time = buf.st_mtime;
1259                                         }
1260
1261                                         num_files++;
1262                                 }
1263                         }
1264                         closedir(dirp);
1265                 }
1266 #else
1267         cf_create_default_path_string( filespec, pathtype, filter );
1268         
1269         int find_handle;
1270         _finddata_t find;
1271         
1272         find_handle = _findfirst( filespec, &find );
1273         if (find_handle != -1) {
1274                 do {
1275                         if (num_files >= max)
1276                                 break;
1277
1278                         if (!(find.attrib & _A_SUBDIR)) {
1279
1280                                 if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) {
1281
1282                                         SDL_strlcpy(arr[num_files], find.name, MAX_FILENAME_LEN);
1283                                         char *ptr = strrchr(arr[num_files], '.');
1284                                         if ( ptr ) {
1285                                                 *ptr = 0;
1286                                         }
1287
1288                                         if (info)       {
1289                                                 info[num_files].write_time = find.time_write;
1290                                         }
1291
1292                                         num_files++;
1293                                 }
1294                         }
1295
1296                 } while (!_findnext(find_handle, &find));
1297
1298                 _findclose( find_handle );
1299         }
1300 #endif
1301                 
1302
1303         // Search all the packfiles and CD.
1304         if ( !Skip_packfile_search )    {
1305                 for (i=0; i<Num_files; i++ )    {
1306                         cf_file * f = cf_get_file(i);
1307
1308                         // only search paths we're supposed to...
1309                         if ( (pathtype != CF_TYPE_ANY) && (pathtype != f->pathtype_index)  )    {
1310                                 continue;
1311                         }
1312
1313                         if (num_files >= max)
1314                                 break;
1315
1316                         if ( !cf_matches_spec( filter,f->name_ext))     {
1317                                 continue;
1318                         }
1319
1320                         if ( cf_file_already_in_list_preallocated( num_files, arr, f->name_ext ))       {
1321                                 continue;
1322                         }
1323
1324                         if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
1325
1326                                 //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
1327
1328                                 SDL_strlcpy(arr[num_files], f->name_ext, MAX_FILENAME_LEN);
1329                                 char *ptr = strrchr(arr[num_files], '.');
1330                                 if ( ptr ) {
1331                                         *ptr = 0;
1332                                 }
1333
1334                                 if (info)       {
1335                                         info[num_files].write_time = f->write_time;
1336                                 }
1337
1338                                 num_files++;
1339                         }
1340
1341                 }
1342         }
1343
1344         if (sort != CF_SORT_NONE) {
1345                 SDL_assert(list);
1346                 cf_sort_filenames( num_files, list, sort, info );
1347         }
1348
1349         if (own_flag)   {
1350                 free(info);
1351         }
1352
1353         Get_file_list_filter = NULL;
1354         return num_files;
1355 }
1356
1357 // Returns the default storage path for files given a 
1358 // particular pathtype.   In other words, the path to 
1359 // the unpacked, non-cd'd, stored on hard drive path.
1360 // If filename isn't null it will also tack the filename
1361 // on the end, creating a completely valid filename.
1362 // Input:   pathtype  - CF_TYPE_??
1363 //          filename  - optional, if set, tacks the filename onto end of path.
1364 // Output:  path      - Fully qualified pathname.
1365 void cf_create_default_path_string( char *path, int pathtype, const char *filename, bool localize )
1366 {
1367 #ifdef PLAT_UNIX
1368         if ( filename && strpbrk(filename, "/")  ) {  
1369 #else
1370         if ( filename && strpbrk(filename,"/\\:")  ) {  
1371 #endif
1372                 // Already has full path
1373                 SDL_strlcpy( path, filename, MAX_PATH_LEN );
1374
1375         } else {
1376                 if ( cfile_init_paths() ) {
1377                         SDL_strlcpy(path, (filename) ? filename : "", MAX_PATH_LEN);
1378                         return;
1379                 }
1380
1381                 SDL_assert(CF_TYPE_SPECIFIED(pathtype));
1382
1383                 SDL_strlcpy(path, Cfile_user_dir, MAX_PATH_LEN);
1384                 SDL_strlcat(path, Pathtypes[pathtype].path, MAX_PATH_LEN);
1385
1386                 // Don't add slash for root directory
1387                 if (Pathtypes[pathtype].path[0] != '\0') {
1388                         SDL_strlcat(path, DIR_SEPARATOR_STR, MAX_PATH_LEN);
1389                 }
1390
1391                 // add filename
1392                 if (filename) {
1393                         SDL_strlcat(path, filename, MAX_PATH_LEN);
1394
1395                         // localize filename
1396                         if (localize) {
1397                                 // create copy of path
1398                                 char temp_path[MAX_PATH_LEN];
1399                                 SDL_strlcpy(temp_path, path, sizeof(temp_path));
1400
1401                                 // localize the path
1402                                 lcl_add_dir_to_path_with_filename(path, MAX_PATH_LEN);
1403
1404                                 // verify localized path
1405                                 FILE *fp = fopen(path, "rb");
1406                                 if (fp) {
1407                                         fclose(fp);
1408                                 } else {
1409                                         SDL_strlcpy(path, temp_path, sizeof(temp_path));
1410                                 }
1411                         }
1412                 }
1413         }
1414 }