1 //********************************************************************************************
2 //*
3 //*    This file is part of Egoboo.
4 //*
5 //*    Egoboo is free software: you can redistribute it and/or modify it
6 //*    under the terms of the GNU General Public License as published by
7 //*    the Free Software Foundation, either version 3 of the License, or
8 //*    (at your option) any later version.
9 //*
10 //*    Egoboo is distributed in the hope that it will be useful, but
11 //*    WITHOUT ANY WARRANTY; without even the implied warranty of
12 //*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 //*    General Public License for more details.
14 //*
15 //*    You should have received a copy of the GNU General Public License
16 //*    along with Egoboo.  If not, see <http://www.gnu.org/licenses/>.
17 //*
18 //********************************************************************************************
19 
20 /// @file egoboo_vfs.c
21 /// @brief Implementation of the Egoboo virtual file system
22 /// @details
23 
24 #include "egoboo_vfs.h"
25 
26 #include "file_common.h"
27 #include "log.h"
28 
29 #include "egoboo_strutil.h"
30 #include "egoboo_endian.h"
31 #include "egoboo_fileutil.h"
32 
33 #include <physfs.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <errno.h>
37 #include "egoboo_mem.h"
38 
39 //--------------------------------------------------------------------------------------------
40 //--------------------------------------------------------------------------------------------
41 #define VFS_MAX_PATH 1024
42 
43 #define BAIL_MACRO(TEST,STR)    if( !(TEST) ) log_error( "egoboo vfs system encountered a fatal error - %s", STR );
44 
45 #if defined(__FUNCSIG__)
46 #    define BAIL_IF_NOT_INIT()    if( !_vfs_initialized ) log_error( "egoboo vfs function called while the system was not initialized -- %s\n", __FUNCSIG__ );
47 #elif defined(__FUNCTION__)
48 #    define BAIL_IF_NOT_INIT()    if( !_vfs_initialized ) log_error( "egoboo vfs function called while the system was not initialized -- %s\n", __FUNCTION__ );
49 #else
50 #    define BAIL_IF_NOT_INIT()    if( !_vfs_initialized ) log_error( "egoboo vfs function called while the system was not initialized -- \"%s\"(line %d)\n", __FILE__, __LINE__ );
51 #endif
52 
53 //--------------------------------------------------------------------------------------------
54 //--------------------------------------------------------------------------------------------
55 
56 typedef char VFS_PATH[VFS_MAX_PATH];
57 
58 #define MAX_MOUNTINFO 128
59 
60 /// The bits specifying the possible errors
61 enum e_vfs_error_bits
62 {
63     VFS_EOF   = ( 1 << 0 ),
64     VFS_ERROR = ( 1 << 1 )
65 };
66 
67 /// What type of file is actually being referenced in u_vfs_fileptr
68 enum e_vfs_mode
69 {
70     vfs_unknown = 0,
71     vfs_cfile,
72     vfs_physfs
73 };
74 typedef enum e_vfs_mode vfs_mode_t;
75 
76 /// An anonymized pointer type
77 union u_vfs_fileptr
78 {
79     void        * u;
80     FILE        * c;
81     PHYSFS_File * p;
82 };
83 typedef union u_vfs_fileptr vfs_fileptr_t;
84 
85 /// A container holding either a FILE * or a PHYSFS_File *, and translated error states
86 struct vfs_FILE
87 {
88     BIT_FIELD     flags;    // flags for stuff like ferror() that doesn't clear every time a filesystem call is made
89     vfs_mode_t    type;
90     vfs_fileptr_t ptr;
91 };
92 
93 /// A container for holding all the data for a search
94 struct s_vfs_search_context
95 {
96     char **    file_list;
97     char **    ptr;
98 
99     char       path[VFS_MAX_PATH];
100     char       ext[255];
101     BIT_FIELD  bits;
102 
103     VFS_PATH found;
104 };
105 
106 struct s_vfs_path_data
107 {
108     VFS_PATH mount;
109 
110     VFS_PATH full_path;
111     VFS_PATH root_path;
112     VFS_PATH relative_path;
113 };
114 typedef struct s_vfs_path_data vfs_path_data_t;
115 
116 //--------------------------------------------------------------------------------------------
117 //--------------------------------------------------------------------------------------------
118 
119 static int             _vfs_mount_info_count = 0;
120 static vfs_path_data_t _vfs_mount_info[MAX_MOUNTINFO];
121 
122 static bool_t _vfs_atexit_registered = bfalse;
123 
124 static bool_t _vfs_initialized = bfalse;
125 
126 //--------------------------------------------------------------------------------------------
127 //--------------------------------------------------------------------------------------------
128 static void                   _vfs_exit( void );
129 static vfs_search_context_t * _vfs_search( vfs_search_context_t ** ctxt );
130 static int                    _vfs_vfscanf( FILE * file, const char * format, va_list args );
131 
132 static int          _vfs_ensure_write_directory( const char * filename, bool_t is_directory );
133 static bool_t       _vfs_ensure_destination_file( const char * filename );
134 
135 static void         _vfs_translate_error( vfs_FILE * pfile );
136 
137 static bool_t       _vfs_mount_info_add( const char * mount_point, const char * root_path, const char * relative_path );
138 static int          _vfs_mount_info_matches( const char * mount_point, const char * local_path );
139 static bool_t       _vfs_mount_info_remove( int cnt );
140 static int          _vfs_mount_info_search( const char * some_path );
141 
142 //--------------------------------------------------------------------------------------------
143 //--------------------------------------------------------------------------------------------
vfs_init()144 void vfs_init()
145 {
146     VFS_PATH tmp_path;
147 
148     fs_init();
149 
150     if ( _vfs_initialized ) return;
151 
152     // set the root path to be the Data Directory, regardless of the executable's path
153     snprintf( tmp_path, SDL_arraysize( tmp_path ), "%s" SLASH_STR, fs_getDataDirectory() );
154     PHYSFS_init( tmp_path );
155 
156     //---- !!!! make sure the basic directories exist !!!!
157 
158     // ensure that the /user dierectory exists
159     if ( !fs_fileIsDirectory( fs_getUserDirectory() ) )
160     {
161         fs_createDirectory( fs_getUserDirectory() );
162     }
163 
164     // ensure that the /user/debug directory exists
165     if ( !fs_fileIsDirectory( fs_getUserDirectory() ) )
166     {
167         printf( "WARNING: Cannot create write directory %s\n", fs_getUserDirectory() );
168     }
169     else
170     {
171         char tmp_path[1024] = EMPTY_CSTR;
172 
173         snprintf( tmp_path, SDL_arraysize( tmp_path ), "%s/debug", fs_getUserDirectory() );
174 
175         str_convert_slash_sys( tmp_path, SDL_arraysize( tmp_path ) );
176         fs_createDirectory( tmp_path );
177     }
178 
179     // set the write directory to the root user directory
180     PHYSFS_setWriteDir( fs_getUserDirectory() );
181 
182     if ( !_vfs_atexit_registered )
183     {
184         atexit( _vfs_exit );
185         _vfs_atexit_registered = btrue;
186     }
187 
188     _vfs_initialized = btrue;
189 }
190 
191 //--------------------------------------------------------------------------------------------
_vfs_exit()192 void _vfs_exit()
193 {
194     PHYSFS_deinit();
195 }
196 
197 //--------------------------------------------------------------------------------------------
vfs_getVersion()198 const char* vfs_getVersion()
199 {
200     /// @details ZF@>  returns the current version of the PhysFS library which was used for compiling the binary
201     PHYSFS_Version version;
202     static STRING buffer = EMPTY_CSTR;
203 
204     //PHYSFS_getLinkedVersion(&version);        //Linked version number
205     PHYSFS_VERSION( &version );         //Compiled version number
206     snprintf( buffer, SDL_arraysize( buffer ), "%d.%d.%d", version.major, version.minor, version.patch );
207 
208     return buffer;
209 }
210 
211 //--------------------------------------------------------------------------------------------
212 //--------------------------------------------------------------------------------------------
vfs_openReadB(const char * filename)213 vfs_FILE * vfs_openReadB( const char * filename )
214 {
215     // open a file for reading in binary mode, using PhysFS
216 
217     vfs_FILE    * vfs_file;
218     PHYSFS_File * ftmp;
219 
220     BAIL_IF_NOT_INIT();
221 
222     if ( INVALID_CSTR( filename ) ) return NULL;
223 
224     // make sure that PHYSFS gets the filename with the slashes it wants
225     filename = vfs_resolveReadFilename( filename );
226 
227     ftmp = PHYSFS_openRead( filename );
228     if ( NULL == ftmp ) return NULL;
229 
230     vfs_file = EGOBOO_NEW( vfs_FILE );
231     if ( NULL == vfs_file ) return NULL;
232 
233     vfs_file->type  = vfs_physfs;
234     vfs_file->ptr.p = ftmp;
235 
236     return vfs_file;
237 }
238 
239 //--------------------------------------------------------------------------------------------
vfs_openWriteB(const char * filename)240 vfs_FILE * vfs_openWriteB( const char * filename )
241 {
242     // open a file for writing in binary mode, using PhysFS
243 
244     VFS_PATH      local_filename = EMPTY_CSTR;
245     vfs_FILE    * vfs_file;
246     PHYSFS_File * ftmp;
247 
248     BAIL_IF_NOT_INIT();
249 
250     if ( INVALID_CSTR( filename ) ) return NULL;
251 
252     // make a local copy of the filename
253     // and make sure that PHYSFS gets the local_filename with the slashes it wants
254     strncpy( local_filename, vfs_convert_fname( filename ), SDL_arraysize( local_filename ) );
255 
256     // make sure that the output directory exists
257     if ( !_vfs_ensure_write_directory( local_filename, bfalse ) ) return NULL;
258 
259     ftmp = PHYSFS_openRead( local_filename );
260     if ( NULL == ftmp ) return NULL;
261 
262     vfs_file = EGOBOO_NEW( vfs_FILE );
263     if ( NULL == vfs_file ) return NULL;
264 
265     vfs_file->type  = vfs_physfs;
266     vfs_file->ptr.p = ftmp;
267 
268     return vfs_file;
269 }
270 
271 //--------------------------------------------------------------------------------------------
vfs_openAppendB(const char * filename)272 vfs_FILE * vfs_openAppendB( const char * filename )
273 {
274     // open a file for appending in binary mode, using PhysFS
275 
276     vfs_FILE    * vfs_file;
277     PHYSFS_File * ftmp;
278 
279     BAIL_IF_NOT_INIT();
280 
281     if ( INVALID_CSTR( filename ) ) return NULL;
282 
283     // make sure that the destination directory exists, and that a data is copied
284     // from the source file in the read path, if necessary
285     if ( !_vfs_ensure_destination_file( filename ) ) return NULL;
286 
287     ftmp = PHYSFS_openAppend( filename );
288     if ( NULL == ftmp ) return NULL;
289 
290     vfs_file = EGOBOO_NEW( vfs_FILE );
291     if ( NULL == vfs_file ) return NULL;
292 
293     vfs_file->type  = vfs_physfs;
294     vfs_file->ptr.p = ftmp;
295 
296     return vfs_file;
297 }
298 
299 //--------------------------------------------------------------------------------------------
vfs_convert_fname_sys(const char * fname)300 const char * vfs_convert_fname_sys( const char * fname )
301 {
302     // PHYSFS and this vfs use a C_SLASH_STR at the front of relative filenames. if this is not removed
303     // when converting to system dependent filenames, it will reference the filename relative to the
304     // root point, rather than relative to the current directory.
305     //
306     // Added complication: if you are trying to specify a root filename,
307     // then you might expect a SLASH_CHR at the beginning of the pathname.
308     // To fix this, call str_convert_slash_sys(), and then check to see if the
309     // directory exists in the filesystem.
310     //
311     // This is not an ideal solution since we may be trying to specify a path for
312     // a tile that needs to be created. The optimal solution might be to check to see if
313     // the path belongs to a registered virtual mount point?
314 
315     static VFS_PATH local_fname = EMPTY_CSTR;
316 
317     size_t   offset;
318     VFS_PATH copy_fname  = EMPTY_CSTR;
319     char *   string_ptr;
320 
321     BAIL_IF_NOT_INIT();
322 
323     // test for a bad input filename
324     if ( INVALID_CSTR( fname ) )
325     {
326         strncpy( local_fname, SLASH_STR, SDL_arraysize( local_fname ) );
327         return local_fname;
328     }
329 
330     if ( VFS_TRUE == _vfs_mount_info_search( fname ) )
331     {
332         // this path contains a virtual mount point
333         EGOBOO_ASSERT( bfalse );
334         return local_fname;
335     }
336 
337     // make a local copy of the original filename, in case fname is
338     // a string literal or a pointer to local_fname
339     strncpy( copy_fname, fname, SDL_arraysize( copy_fname ) );
340 
341     // convert the path string to local notation
342     string_ptr = str_convert_slash_sys( copy_fname, strlen( fname ) );
343     if ( !VALID_CSTR( string_ptr ) )
344     {
345         strncpy( local_fname, SLASH_STR, SDL_arraysize( local_fname ) );
346         return local_fname;
347     }
348 
349     // resolve the conflict between
350     //    -1- directories relative to the PHYSFS root path starting with NET_SLASH
351     // and
352     //    -2- directories relative to the root of the filesystem beginning with a slash
353     // by trying to find the original path
354     //
355     // The following method is not exactly secure, since it would allow access to generic
356     // root directories using this code...
357 
358     // if the path already exists, just return the path
359     if ( fs_fileExists( copy_fname ) )
360     {
361         strncpy( local_fname, copy_fname, SDL_arraysize( local_fname ) );
362         return local_fname;
363     }
364 
365     // if the path didn't exist it might be because it contains a file that has not yet been created...
366     // no solution to that problem, yet.
367 
368     // ---- if we got to this point, we need to strip off any beginning slashes
369 
370     offset = 0;
371     if ( '.' == copy_fname[0] && '.' == copy_fname[1] )
372     {
373         offset++;
374     }
375 
376     // the string has already been converted to a system filename, so just check SLASH_CHR
377     while ( SLASH_CHR == copy_fname[offset] && offset < SDL_arraysize( copy_fname ) )
378     {
379         offset++;
380     }
381 
382     // copy the proper relative filename
383     strncpy( local_fname, copy_fname + offset, SDL_arraysize( local_fname ) );
384 
385     return local_fname;
386 }
387 
388 //--------------------------------------------------------------------------------------------
vfs_convert_fname(const char * fname)389 const char * vfs_convert_fname( const char * fname )
390 {
391     VFS_PATH        copy_fname  = EMPTY_CSTR;
392     static VFS_PATH local_fname = EMPTY_CSTR;
393 
394     BAIL_IF_NOT_INIT();
395 
396     // test for a bad iput filename
397     if ( INVALID_CSTR( fname ) )
398     {
399         strncpy( local_fname, NET_SLASH_STR, SDL_arraysize( local_fname ) );
400         return local_fname;
401     }
402 
403     // make a copy of the original filename, in case fname is
404     // a literal string or a pointer to local_fname
405     strncpy( copy_fname, fname, SDL_arraysize( copy_fname ) );
406 
407     if ( _vfs_mount_info_search( copy_fname ) )
408     {
409         snprintf( local_fname, SDL_arraysize( local_fname ), "%s", copy_fname );
410     }
411     else if ( NET_SLASH_CHR == copy_fname[0] || WIN32_SLASH_CHR == copy_fname[0] )
412     {
413         snprintf( local_fname, SDL_arraysize( local_fname ), "%s", copy_fname );
414     }
415     else
416     {
417         snprintf( local_fname, SDL_arraysize( local_fname ), NET_SLASH_STR "%s", copy_fname );
418     }
419 
420     return str_convert_slash_net( local_fname, strlen( local_fname ) );
421 }
422 
423 //--------------------------------------------------------------------------------------------
_vfs_potential_mount_point(const char * some_path,const char ** pstripped_pos)424 const char * _vfs_potential_mount_point( const char * some_path, const char ** pstripped_pos )
425 {
426     // This helper function was devised to read the first potential directory name
427     // from the given path. Because:
428     //
429     // 1) To use the PHYSFS_getMountPoint() function, the mount point you are testing
430     //    must be a system-dependent pathname, ending with a system-dependent path
431     //    separator.
432     //
433     // 2) if you use PHYSFS_getRealDir() to give you a path to a file, what it really does is
434     //    resolve the mount point, rather than the whole path name (path name without the
435     //    file, that is). To do the job we want in vfs_resolveReadFilename(), we need to be able
436     //    to strip the mount point off of the path that we were given before prepending the
437     //    path returned by PHYSFS_getRealDir()
438 
439     const char * ptmp, *path_begin, *path_end;
440     static VFS_PATH found_path;
441 
442     found_path[0] = CSTR_END;
443 
444     BAIL_IF_NOT_INIT();
445 
446     if ( !VALID_CSTR( some_path ) ) return found_path;
447 
448     ptmp = some_path;
449 
450     path_begin = some_path;
451     path_end   = some_path + VFS_MAX_PATH - 1;
452 
453     // strip any starting slashes
454     for ( ptmp = path_begin; ptmp < path_end; ptmp++ )
455     {
456         if ( NET_SLASH_CHR != *ptmp && WIN32_SLASH_CHR != *ptmp )
457         {
458             path_begin = ptmp;
459             break;
460         }
461     }
462 
463     // identify the slash after the mount point
464     ptmp = strpbrk( path_begin, "/\\" );
465     if ( NULL != ptmp ) path_end = ptmp + 1;
466 
467     // make sre we do not have an empty string
468     if ( path_end > path_begin )
469     {
470         size_t count = path_end - path_begin;
471         strncpy( found_path, path_begin, count );
472         found_path[count] = CSTR_END;
473     }
474 
475     // export the beginning of the string after the mount point, if possible
476     if ( NULL != pstripped_pos )
477     {
478         *pstripped_pos = path_end;
479     }
480 
481     // return the potential mount point in system-dependent format
482     return str_convert_slash_sys( found_path, strlen( found_path ) );
483 }
484 
485 //--------------------------------------------------------------------------------------------
vfs_listSearchPaths()486 void vfs_listSearchPaths()
487 {
488     //JJ> Lists all search paths that PhysFS uses (for debug use)
489 
490     char **i;
491 
492     BAIL_IF_NOT_INIT();
493 
494     printf( "LISTING ALL PHYSFS SEARCH PATHS:\n" );
495     printf( "----------------------------------\n" );
496     for ( i = PHYSFS_getSearchPath(); *i != NULL; i++ )   printf( "[%s] is in the search path.\n", *i );
497     printf( "----------------------------------\n" );
498 }
499 
500 //--------------------------------------------------------------------------------------------
vfs_resolveReadFilename(const char * src_filename)501 const char * vfs_resolveReadFilename( const char * src_filename )
502 {
503     static STRING read_name_str = EMPTY_CSTR;
504     VFS_PATH      loc_fname = EMPTY_CSTR, szTemp = EMPTY_CSTR;
505     int           retval_len = 0;
506     const char   *retval = NULL;
507 
508     BAIL_IF_NOT_INIT();
509 
510     if ( INVALID_CSTR( src_filename ) ) return NULL;
511 
512     // make a copy of the szTemp, in case we are passed a string literal
513     // as the argument of this function
514     strncpy( szTemp, src_filename, SDL_arraysize( szTemp ) );
515 
516     // make a temporary copy of the given filename with system-dependent slashes
517     // to see if the filename is already resolved
518     strncpy( loc_fname, szTemp, SDL_arraysize( loc_fname ) );
519     str_convert_slash_sys( loc_fname, SDL_arraysize( loc_fname ) );
520 
521     if ( fs_fileExists( loc_fname ) )
522     {
523         strncpy( read_name_str, loc_fname, SDL_arraysize( read_name_str ) );
524 
525         return read_name_str;
526     }
527 
528     // make another copy of the local filename
529     // and make sure that PHYSFS gets the filename with the slashes it wants
530     strncpy( loc_fname, vfs_convert_fname( szTemp ), SDL_arraysize( loc_fname ) );
531 
532     retval = NULL;
533     retval_len = 0;
534     if ( PHYSFS_isDirectory( loc_fname ) )
535     {
536         retval = PHYSFS_getRealDir( loc_fname );
537 
538         if ( VALID_CSTR( retval ) )
539         {
540             const char * ptmp = vfs_mount_info_strip_path( loc_fname );
541 
542             if ( VALID_CSTR( ptmp ) )
543             {
544                 snprintf( read_name_str, SDL_arraysize( read_name_str ), "%s/%s", retval, ptmp );
545             }
546             else
547             {
548                 snprintf( read_name_str, SDL_arraysize( read_name_str ), "%s/", retval );
549             }
550 
551             retval     = read_name_str;
552             retval_len = SDL_arraysize( read_name_str );
553         }
554     }
555     else
556     {
557         const char * tmp_dirname;
558         const char * ptmp = loc_fname;
559 
560         // make PHYSFS grab the actual directory
561         tmp_dirname = PHYSFS_getRealDir( loc_fname );
562 
563         if ( INVALID_CSTR( tmp_dirname ) )
564         {
565             // not found... just punt
566             strncpy( read_name_str, loc_fname, SDL_arraysize( read_name_str ) );
567             retval     = read_name_str;
568             retval_len = SDL_arraysize( read_name_str );
569         }
570         else
571         {
572             ptmp = vfs_mount_info_strip_path( loc_fname );
573 
574             snprintf( read_name_str, SDL_arraysize( read_name_str ), "%s/%s", tmp_dirname, ptmp );
575             retval     = read_name_str;
576             retval_len = SDL_arraysize( read_name_str );
577         }
578     }
579 
580     if ( VALID_CSTR( retval ) && retval_len > 0 )
581     {
582         retval = str_convert_slash_sys(( char* )retval, retval_len );
583     }
584 
585     return retval;
586 }
587 
588 //--------------------------------------------------------------------------------------------
vfs_resolveWriteFilename(const char * src_filename)589 const char * vfs_resolveWriteFilename( const char * src_filename )
590 {
591     static VFS_PATH  szFname = EMPTY_CSTR;
592     VFS_PATH szTemp;
593     const  char    * write_dir;
594 
595     BAIL_IF_NOT_INIT();
596 
597     if ( INVALID_CSTR( src_filename ) ) return szFname;
598 
599     // make a copy of the src_filename, in case we are passed a string literal
600     // as the argument of this function
601     strncpy( szTemp, src_filename, SDL_arraysize( szTemp ) );
602 
603     write_dir = PHYSFS_getWriteDir();
604     if ( NULL == write_dir )
605     {
606         log_warning( "PhysFS could not get write directory!\n" );
607         return NULL;
608     }
609 
610     // append the write_dir to the szTemp to get the total path
611     snprintf( szFname, SDL_arraysize( szFname ), "%s" SLASH_STR "%s", write_dir, szTemp );
612 
613     // make sure that the slashes are correct for this system, and that they are not doubled
614 
615     return str_convert_slash_sys( szFname, SDL_arraysize( szFname ) );
616 }
617 
618 //--------------------------------------------------------------------------------------------
vfs_openRead(const char * filename)619 vfs_FILE * vfs_openRead( const char * filename )
620 {
621     // open a file for reading in text mode, using c stdio
622 
623     const char  * real_filename;
624     vfs_FILE    * vfs_file;
625     FILE        * ftmp;
626 
627     BAIL_IF_NOT_INIT();
628 
629     // parse_filename = "";
630 
631     real_filename = vfs_resolveReadFilename( filename );
632     if ( NULL == real_filename ) return NULL;
633 
634     ftmp = fopen( real_filename, "r" );
635     if ( NULL == ftmp ) return NULL;
636 
637     vfs_file = EGOBOO_NEW( vfs_FILE );
638     if ( NULL == vfs_file ) return NULL;
639 
640     parse_filename = filename;
641 
642     vfs_file->type  = vfs_cfile;
643     vfs_file->ptr.c = ftmp;
644 
645     return vfs_file;
646 }
647 
648 //--------------------------------------------------------------------------------------------
_vfs_ensure_write_directory(const char * filename,bool_t is_directory)649 int _vfs_ensure_write_directory( const char * filename, bool_t is_directory )
650 {
651     /// @details BB@>
652 
653     int           retval;
654     VFS_PATH      temp_dirname = EMPTY_CSTR;
655     char        * tmpstr;
656 
657     BAIL_IF_NOT_INIT();
658 
659     if ( INVALID_CSTR( filename ) ) return 0;
660 
661     // make a working copy of the filename
662     // and make sure that PHYSFS gets the filename with the slashes it wants
663     strncpy( temp_dirname, vfs_convert_fname( filename ), SDL_arraysize( temp_dirname ) );
664 
665     // grab the system-independent path relative to the write directory
666     if ( !is_directory && !vfs_isDirectory( temp_dirname ) )
667     {
668         tmpstr = strrchr( temp_dirname, NET_SLASH_CHR );
669         if ( NULL == tmpstr )
670         {
671             strncpy( temp_dirname, NET_SLASH_STR, SDL_arraysize( temp_dirname ) );
672         }
673         else
674         {
675             *tmpstr = CSTR_END;
676         }
677 
678         if ( CSTR_END == temp_dirname[0] )
679         {
680             temp_dirname[0] = C_SLASH_CHR;
681             temp_dirname[1] = CSTR_END;
682         }
683     }
684 
685     // call mkdir() on this directory. PHYSFS will automatically generate the
686     // directories needed between the write directory and the specified directory
687     retval = 1;
688     if ( VALID_CSTR( temp_dirname ) )
689     {
690         retval = vfs_mkdir( temp_dirname );
691     }
692 
693     return retval;
694 }
695 
696 //--------------------------------------------------------------------------------------------
vfs_openWrite(const char * filename)697 vfs_FILE * vfs_openWrite( const char * filename )
698 {
699     // open a file for writing in text mode,  using c stdio
700 
701     VFS_PATH      local_filename = EMPTY_CSTR;
702     const char  * real_filename;
703     vfs_FILE    * vfs_file;
704     FILE        * ftmp;
705 
706     BAIL_IF_NOT_INIT();
707 
708     if ( INVALID_CSTR( filename ) ) return NULL;
709 
710     // make a local copy of the filename
711     // and make sure that PHYSFS gets the filename with the slashes it wants
712     strncpy( local_filename, vfs_convert_fname( filename ), SDL_arraysize( local_filename ) );
713 
714     // make sure that the output directory exists
715     if ( !_vfs_ensure_write_directory( local_filename, bfalse ) ) return NULL;
716 
717     // get the system-dependent filename
718     real_filename = vfs_resolveWriteFilename( filename );
719     if ( NULL == real_filename ) return NULL;
720 
721     ftmp = fopen( real_filename, "w" );
722     if ( NULL == ftmp ) return NULL;
723 
724     vfs_file = EGOBOO_NEW( vfs_FILE );
725     if ( NULL == vfs_file ) return NULL;
726 
727     vfs_file->type  = vfs_cfile;
728     vfs_file->ptr.c = ftmp;
729 
730     return vfs_file;
731 }
732 
733 //--------------------------------------------------------------------------------------------
_vfs_ensure_destination_file(const char * filename)734 bool_t _vfs_ensure_destination_file( const char * filename )
735 {
736     /// @details BB@> make sure that a copy of filename from the read path exists in
737     ///     the write directory, but do not overwrite any existing file
738 
739     VFS_PATH      local_filename = EMPTY_CSTR;
740     const char  * sys_src_name, * sys_dst_name;
741     bool_t        read_exists, write_exists;
742 
743     BAIL_IF_NOT_INIT();
744 
745     if ( INVALID_CSTR( filename ) ) return bfalse;
746 
747     // make a local copy of the filename
748     // and make sure that PHYSFS gets the filename with the slashes it wants
749     strncpy( local_filename, vfs_convert_fname( filename ), SDL_arraysize( local_filename ) );
750 
751     // make sure that the output directory exists
752     if ( !_vfs_ensure_write_directory( local_filename, bfalse ) ) return bfalse;
753 
754     // be a bit carefil here, in case the file exists in the read path and not in the write
755     // directory
756 
757     sys_src_name  = vfs_resolveReadFilename( local_filename );
758     read_exists   = fs_fileExists( sys_src_name ) > 0;
759 
760     sys_dst_name  = vfs_resolveWriteFilename( local_filename );
761     write_exists  = fs_fileExists( sys_dst_name ) > 0;
762 
763     if ( read_exists && !write_exists )
764     {
765         // read exists but write does not exist.
766         // copy the read file to the write file and then append
767         fs_copyFile( sys_src_name, sys_dst_name );
768 
769         write_exists  = fs_fileExists( sys_dst_name ) > 0;
770     }
771 
772     return write_exists;
773 }
774 
775 //--------------------------------------------------------------------------------------------
vfs_openAppend(const char * filename)776 vfs_FILE * vfs_openAppend( const char * filename )
777 {
778     // open a file for appending in text mode,  using c stdio
779 
780     vfs_FILE    * vfs_file;
781     FILE        * ftmp;
782     const char  * sys_dst_name;
783 
784     BAIL_IF_NOT_INIT();
785 
786     if ( INVALID_CSTR( filename ) ) return NULL;
787 
788     // make sure that the destination directory exists, and that a data is copied
789     // from the source file in the read path, if necessary
790     if ( !_vfs_ensure_destination_file( filename ) ) return NULL;
791 
792     sys_dst_name  = vfs_resolveWriteFilename( filename );
793     if ( INVALID_CSTR( sys_dst_name ) ) return NULL;
794 
795     // now open the file for append normally
796     ftmp = fopen( sys_dst_name, "a+" );
797     if ( NULL == ftmp ) return NULL;
798 
799     vfs_file = EGOBOO_NEW( vfs_FILE );
800     if ( NULL == vfs_file ) return NULL;
801 
802     vfs_file->type  = vfs_cfile;
803     vfs_file->ptr.c = ftmp;
804 
805     return vfs_file;
806 }
807 
808 //--------------------------------------------------------------------------------------------
vfs_close(vfs_FILE * pfile)809 int vfs_close( vfs_FILE * pfile )
810 {
811     // close a file, and git rid of the allocated file descriptor
812 
813     int retval;
814 
815     BAIL_IF_NOT_INIT();
816 
817     if ( NULL == pfile ) return 0;
818 
819     parse_filename = "";
820 
821     retval = 0;
822     if ( vfs_cfile == pfile->type )
823     {
824         retval = fclose( pfile->ptr.c );
825         memset( pfile, 0, sizeof( *pfile ) );
826 
827         EGOBOO_DELETE( pfile );
828     }
829     else if ( vfs_physfs == pfile->type )
830     {
831         retval = PHYSFS_close( pfile->ptr.p );
832         memset( pfile, 0, sizeof( *pfile ) );
833 
834         EGOBOO_DELETE( pfile );
835     }
836     else
837     {
838         // corrupted data?
839         fprintf( stderr, "Tried to deallocate an invalid vfs file descriptor\n" );
840     }
841 
842     return retval;
843 }
844 
845 //--------------------------------------------------------------------------------------------
vfs_flush(vfs_FILE * pfile)846 int vfs_flush( vfs_FILE * pfile )
847 {
848     int retval;
849 
850     BAIL_IF_NOT_INIT();
851 
852     if ( NULL == pfile ) return 0;
853 
854     retval = 0;
855     if ( vfs_cfile == pfile->type )
856     {
857         retval = fflush( pfile->ptr.c );
858     }
859     else if ( vfs_physfs == pfile->type )
860     {
861         retval = PHYSFS_flush( pfile->ptr.p );
862     }
863 
864     return retval;
865 }
866 
867 //--------------------------------------------------------------------------------------------
868 //--------------------------------------------------------------------------------------------
vfs_eof(vfs_FILE * pfile)869 int vfs_eof( vfs_FILE * pfile )
870 {
871     int retval;
872 
873     BAIL_IF_NOT_INIT();
874 
875     if ( NULL == pfile ) return 0;
876 
877     // check our own end-of-file condition
878     if ( 0 != ( pfile->flags & VFS_EOF ) )
879     {
880         return pfile->flags & VFS_EOF;
881     }
882 
883     retval = 1;
884     if ( vfs_cfile == pfile->type )
885     {
886         retval = feof( pfile->ptr.c );
887     }
888     else if ( vfs_physfs == pfile->type )
889     {
890         retval = PHYSFS_eof( pfile->ptr.p );
891     }
892 
893     if ( 0 != retval )
894     {
895         pfile->flags |= VFS_EOF;
896     }
897 
898     return retval;
899 }
900 
901 //--------------------------------------------------------------------------------------------
vfs_error(vfs_FILE * pfile)902 int vfs_error( vfs_FILE * pfile )
903 {
904     int retval;
905 
906     BAIL_IF_NOT_INIT();
907 
908     if ( NULL == pfile ) return 0;
909 
910     retval = 1;
911     if ( vfs_cfile == pfile->type )
912     {
913         retval = ferror( pfile->ptr.c );
914     }
915     else if ( vfs_physfs == pfile->type )
916     {
917         retval = ( NULL != PHYSFS_getLastError() );
918     }
919 
920     return retval;
921 }
922 
923 //--------------------------------------------------------------------------------------------
vfs_tell(vfs_FILE * pfile)924 long vfs_tell( vfs_FILE * pfile )
925 {
926     long retval;
927 
928     BAIL_IF_NOT_INIT();
929 
930     if ( NULL == pfile ) return 0;
931 
932     retval = 0;
933     if ( vfs_cfile == pfile->type )
934     {
935         retval = ftell( pfile->ptr.c );
936     }
937     else if ( vfs_physfs == pfile->type )
938     {
939         retval = PHYSFS_tell( pfile->ptr.p );
940     }
941 
942     return retval;
943 }
944 
945 //--------------------------------------------------------------------------------------------
vfs_seek(vfs_FILE * pfile,long offset)946 int vfs_seek( vfs_FILE * pfile, long offset )
947 {
948     int retval;
949 
950     BAIL_IF_NOT_INIT();
951 
952     if ( NULL == pfile ) return 0;
953 
954     retval = 0;
955     if ( vfs_cfile == pfile->type )
956     {
957         // reset the flags
958         pfile->flags = 0;
959 
960         // !!!! since we are opening non-binary files in text mode, fseek might act strangely !!!!
961         retval = fseek( pfile->ptr.c, offset, SEEK_SET );
962     }
963     else if ( vfs_physfs == pfile->type )
964     {
965         // reset the flags
966         pfile->flags = 0;
967 
968         retval = PHYSFS_seek( pfile->ptr.p, offset );
969     }
970 
971     if ( 0 != offset )
972     {
973         // set an eof flag if we set it to seek past the end of the file
974         vfs_eof( pfile );
975     }
976 
977     return retval;
978 }
979 
980 //--------------------------------------------------------------------------------------------
vfs_fileLength(vfs_FILE * pfile)981 long vfs_fileLength( vfs_FILE * pfile )
982 {
983     long retval;
984 
985     BAIL_IF_NOT_INIT();
986 
987     if ( NULL == pfile ) return 0;
988 
989     retval = 0;
990     if ( vfs_cfile == pfile->type )
991     {
992         // do a little dance with the file pointer to figure out the file length
993 
994         long pos = ftell( pfile->ptr.c );
995 
996         fseek( pfile->ptr.c, 0, SEEK_END );
997         retval = ftell( pfile->ptr.c );
998 
999         fseek( pfile->ptr.c, pos, SEEK_SET );
1000     }
1001     else if ( vfs_physfs == pfile->type )
1002     {
1003         retval = PHYSFS_fileLength( pfile->ptr.p );
1004     }
1005 
1006     return retval;
1007 }
1008 
1009 //--------------------------------------------------------------------------------------------
1010 //--------------------------------------------------------------------------------------------
vfs_mkdir(const char * dirName)1011 int vfs_mkdir( const char *dirName )
1012 {
1013     int retval;
1014 
1015     BAIL_IF_NOT_INIT();
1016 
1017     retval = PHYSFS_mkdir( vfs_convert_fname( dirName ) );
1018 
1019     if ( !retval )
1020     {
1021         log_debug( "vfs_copyDirectory() - Could not create new folder folder \"%s\". (%s)\n", dirName, vfs_getError() );
1022     }
1023 
1024     return retval;
1025 }
1026 
1027 //--------------------------------------------------------------------------------------------
vfs_delete_file(const char * filename)1028 int vfs_delete_file( const char *filename )
1029 {
1030     BAIL_IF_NOT_INIT();
1031 
1032     return PHYSFS_delete( vfs_convert_fname( filename ) );
1033 }
1034 
1035 //--------------------------------------------------------------------------------------------
vfs_exists(const char * fname)1036 int vfs_exists( const char *fname )
1037 {
1038     BAIL_IF_NOT_INIT();
1039 
1040     return PHYSFS_exists( vfs_convert_fname( fname ) );
1041 }
1042 
1043 //--------------------------------------------------------------------------------------------
vfs_isDirectory(const char * fname)1044 int vfs_isDirectory( const char *fname )
1045 {
1046     BAIL_IF_NOT_INIT();
1047 
1048     return PHYSFS_isDirectory( vfs_convert_fname( fname ) );
1049 }
1050 
1051 //--------------------------------------------------------------------------------------------
1052 //--------------------------------------------------------------------------------------------
vfs_read(void * buffer,size_t size,size_t count,vfs_FILE * pfile)1053 size_t vfs_read( void * buffer, size_t size, size_t count, vfs_FILE * pfile )
1054 {
1055     bool_t error = bfalse;
1056     size_t read_length;
1057 
1058     BAIL_IF_NOT_INIT();
1059 
1060     if ( NULL == pfile ) return 0;
1061 
1062     read_length = 0;
1063     if ( vfs_cfile == pfile->type )
1064     {
1065         read_length = fread( buffer, size, count, pfile->ptr.c );
1066         error = ( read_length != size );
1067     }
1068     else if ( vfs_physfs == pfile->type )
1069     {
1070         int retval = PHYSFS_read( pfile->ptr.p, buffer, size, count );
1071 
1072         if ( retval < 0 ) { error = btrue; pfile->flags |= VFS_ERROR; }
1073 
1074         if ( !error ) read_length = count;
1075     }
1076 
1077     if ( error ) _vfs_translate_error( pfile );
1078 
1079     return read_length;
1080 }
1081 
1082 //--------------------------------------------------------------------------------------------
vfs_write(void * buffer,size_t size,size_t count,vfs_FILE * pfile)1083 size_t vfs_write( void * buffer, size_t size, size_t count, vfs_FILE * pfile )
1084 {
1085     size_t retval;
1086 
1087     BAIL_IF_NOT_INIT();
1088 
1089     if ( NULL == pfile ) return 0;
1090 
1091     retval = 0;
1092     if ( vfs_cfile == pfile->type )
1093     {
1094         retval = fwrite( buffer, size, count, pfile->ptr.c );
1095     }
1096     else if ( vfs_physfs == pfile->type )
1097     {
1098         retval = PHYSFS_write( pfile->ptr.p, buffer, size, count );
1099     }
1100 
1101     return retval;
1102 }
1103 
1104 //--------------------------------------------------------------------------------------------
vfs_read_Sint16(vfs_FILE * pfile,Sint16 * val)1105 int vfs_read_Sint16( vfs_FILE * pfile, Sint16 * val )
1106 {
1107     int retval;
1108     bool_t error = bfalse;
1109 
1110     BAIL_IF_NOT_INIT();
1111 
1112     if ( NULL == pfile ) return 0;
1113 
1114     retval = 0;
1115     if ( vfs_cfile == pfile->type )
1116     {
1117         Uint16 itmp;
1118         retval = fread( &itmp, 1, sizeof( Uint16 ), pfile->ptr.c );
1119 
1120         error = ( 1 != retval );
1121 
1122         *val = ENDIAN_INT16( itmp );
1123     }
1124     else if ( vfs_physfs == pfile->type )
1125     {
1126         retval = PHYSFS_readSLE16( pfile->ptr.p, val );
1127 
1128         error = ( 0 == retval );
1129     }
1130 
1131     if ( error ) _vfs_translate_error( pfile );
1132 
1133     return retval;
1134 }
1135 
1136 //--------------------------------------------------------------------------------------------
vfs_read_Uint16(vfs_FILE * pfile,Uint16 * val)1137 int vfs_read_Uint16( vfs_FILE * pfile, Uint16 * val )
1138 {
1139     bool_t error = bfalse;
1140     int retval;
1141 
1142     BAIL_IF_NOT_INIT();
1143 
1144     if ( NULL == pfile ) return 0;
1145 
1146     retval = 0;
1147     if ( vfs_cfile == pfile->type )
1148     {
1149         Uint16 itmp;
1150         retval = fread( &itmp, 1, sizeof( Uint16 ), pfile->ptr.c );
1151 
1152         error = ( 1 != retval );
1153 
1154         *val = ENDIAN_INT16( itmp );
1155     }
1156     else if ( vfs_physfs == pfile->type )
1157     {
1158         retval = PHYSFS_readULE16( pfile->ptr.p, val );
1159 
1160         error = ( 0 == retval );
1161     }
1162 
1163     if ( error ) _vfs_translate_error( pfile );
1164 
1165     return retval;
1166 }
1167 
1168 //--------------------------------------------------------------------------------------------
vfs_read_Sint32(vfs_FILE * pfile,Sint32 * val)1169 int vfs_read_Sint32( vfs_FILE * pfile, Sint32 * val )
1170 {
1171     int retval;
1172     bool_t error = bfalse;
1173 
1174     BAIL_IF_NOT_INIT();
1175 
1176     if ( NULL == pfile ) return 0;
1177 
1178     retval = 0;
1179     if ( vfs_cfile == pfile->type )
1180     {
1181         Uint32 itmp;
1182         retval = fread( &itmp, 1, sizeof( Uint32 ), pfile->ptr.c );
1183 
1184         error = ( 1 != retval );
1185 
1186         *val = ENDIAN_INT32( itmp );
1187     }
1188     else if ( vfs_physfs == pfile->type )
1189     {
1190         retval = PHYSFS_readSLE32( pfile->ptr.p, val );
1191 
1192         error = ( 0 == retval );
1193     }
1194 
1195     if ( error ) _vfs_translate_error( pfile );
1196 
1197     return retval;
1198 }
1199 
1200 //--------------------------------------------------------------------------------------------
vfs_read_Uint32(vfs_FILE * pfile,Uint32 * val)1201 int vfs_read_Uint32( vfs_FILE * pfile, Uint32 * val )
1202 {
1203     int retval;
1204     bool_t error = bfalse;
1205 
1206     BAIL_IF_NOT_INIT();
1207 
1208     if ( NULL == pfile ) return 0;
1209 
1210     retval = 0;
1211     if ( vfs_cfile == pfile->type )
1212     {
1213         Uint32 itmp;
1214         retval = fread( &itmp, 1, sizeof( Uint32 ), pfile->ptr.c );
1215 
1216         error = ( 1 != retval );
1217 
1218         *val = ENDIAN_INT32( itmp );
1219     }
1220     else if ( vfs_physfs == pfile->type )
1221     {
1222         retval = PHYSFS_readULE32( pfile->ptr.p, val );
1223 
1224         error = ( 0 == retval );
1225     }
1226 
1227     if ( error ) _vfs_translate_error( pfile );
1228 
1229     return retval;
1230 }
1231 
1232 //--------------------------------------------------------------------------------------------
vfs_read_Sint64(vfs_FILE * pfile,Sint64 * val)1233 int vfs_read_Sint64( vfs_FILE * pfile, Sint64 * val )
1234 {
1235     int retval;
1236     bool_t error = bfalse;
1237 
1238     BAIL_IF_NOT_INIT();
1239 
1240     if ( NULL == pfile ) return 0;
1241 
1242     retval = 0;
1243     if ( vfs_cfile == pfile->type )
1244     {
1245         Uint64 itmp;
1246         retval = fread( &itmp, 1, sizeof( Uint64 ), pfile->ptr.c );
1247 
1248         error = ( 1 != retval );
1249 
1250         *val = ENDIAN_INT64( itmp );
1251     }
1252     else if ( vfs_physfs == pfile->type )
1253     {
1254         retval = PHYSFS_readSLE64( pfile->ptr.p, val );
1255 
1256         error = ( 0 == retval );
1257     }
1258 
1259     if ( error ) _vfs_translate_error( pfile );
1260 
1261     return retval;
1262 }
1263 
1264 //--------------------------------------------------------------------------------------------
vfs_read_Uint64(vfs_FILE * pfile,Uint64 * val)1265 int vfs_read_Uint64( vfs_FILE * pfile, Uint64 * val )
1266 {
1267     int retval;
1268     bool_t error = bfalse;
1269 
1270     BAIL_IF_NOT_INIT();
1271 
1272     if ( NULL == pfile ) return 0;
1273 
1274     retval = 0;
1275     if ( vfs_cfile == pfile->type )
1276     {
1277         Uint64 itmp;
1278         retval = fread( &itmp, 1, sizeof( Uint64 ), pfile->ptr.c );
1279 
1280         error = ( 1 != retval );
1281 
1282         *val = ENDIAN_INT64( itmp );
1283     }
1284     else if ( vfs_physfs == pfile->type )
1285     {
1286         retval = PHYSFS_readULE64( pfile->ptr.p, val );
1287 
1288         error = ( 0 == retval );
1289     }
1290 
1291     if ( error ) _vfs_translate_error( pfile );
1292 
1293     return retval;
1294 }
1295 
1296 //--------------------------------------------------------------------------------------------
vfs_read_float(vfs_FILE * pfile,float * val)1297 int vfs_read_float( vfs_FILE * pfile, float * val )
1298 {
1299     int retval;
1300     bool_t error = bfalse;
1301 
1302     BAIL_IF_NOT_INIT();
1303 
1304     if ( NULL == pfile ) return 0;
1305 
1306     retval = 0;
1307     if ( vfs_cfile == pfile->type )
1308     {
1309         float ftmp;
1310         retval = fread( &ftmp, 1, sizeof( float ), pfile->ptr.c );
1311 
1312         error = ( 1 != retval );
1313 
1314         *val = ENDIAN_FLOAT( ftmp );
1315     }
1316     else if ( vfs_physfs == pfile->type )
1317     {
1318         union { float f; Uint32 i; } convert;
1319         retval = PHYSFS_readULE32( pfile->ptr.p, &( convert.i ) );
1320 
1321         error = ( 0 == retval );
1322 
1323         *val = convert.f;
1324     }
1325 
1326     if ( error ) _vfs_translate_error( pfile );
1327 
1328     return retval;
1329 }
1330 
1331 //--------------------------------------------------------------------------------------------
1332 //--------------------------------------------------------------------------------------------
1333 //int fake_physfs_vscanf( PHYSFS_File * pfile, const char *format, va_list args )
1334 //{
1335 //    // UGH! Just break the format code into pieces and call fscanf on each piece
1336 //
1337 //    char   sub_format[256] = EMPTY_CSTR;
1338 //    char * format_end, * format_next;
1339 //    int    argcount = 0;
1340 //    void * ptr;
1341 //
1342 //    if( NULL == file || INVALID_CSTR(format) ) return 0;
1343 //
1344 //    format_end = (char *)(format + strlen(format));
1345 //
1346 //    // scan throuh the format string looking for formats
1347 //    argcount = 0;
1348 //    while( format < format_end )
1349 //    {
1350 //        bool_t found_format;
1351 //        char * format_tmp;
1352 //
1353 //        // find everything up to the first valid format code in the format string
1354 //        found_format = bfalse;
1355 //        format_tmp   = (char *)format;
1356 //        format_next  = format_tmp;
1357 //        while( format_next < format_end )
1358 //        {
1359 //            format_next = strchr( format_tmp, '%' );
1360 //
1361 //            // handle the occurrence of "%%"
1362 //            if( '%' == *(format_next + 1) )
1363 //            {
1364 //                format_tmp = format_next + 1;
1365 //            }
1366 //            else
1367 //            {
1368 //                found_format = btrue;
1369 //                break;
1370 //            }
1371 //        }
1372 //
1373 //        // copy the format string fragment
1374 //        if( found_format && format_next < format_end )
1375 //        {
1376 //            // scan the valid format code
1377 //            format_next += strcspn( format_next, "cCsSdioxXnueEfgG" ) + 1;
1378 //        }
1379 //        strncpy( sub_format, format, format_next - format );
1380 //
1381 //        // get a pointer to the variable to be filled
1382 //        ptr = NULL;
1383 //        if( found_format )
1384 //        {
1385 //            ptr = va_arg( args, void * );
1386 //        }
1387 //
1388 //        // do the call to fscanf()
1389 //        if( NULL == ptr )
1390 //        {
1391 //            PHYSFS_scanf( file, sub_format );
1392 //        }
1393 //        else
1394 //        {
1395 //            argcount += PHYSFS_scanf( file, sub_format, ptr );
1396 //        }
1397 //
1398 //        format = format_next;
1399 //    }
1400 //
1401 //    return argcount;
1402 //}
1403 //
1404 
1405 //--------------------------------------------------------------------------------------------
1406 //--------------------------------------------------------------------------------------------
fake_physfs_vprintf(PHYSFS_File * pfile,const char * format,va_list args)1407 int fake_physfs_vprintf( PHYSFS_File * pfile, const char *format, va_list args )
1408 {
1409     // fake an actual streaming write to the file by writing the string to a
1410     // "large" buffer
1411 
1412     int written;
1413     char buffer[4098] = EMPTY_CSTR;
1414 
1415     BAIL_IF_NOT_INIT();
1416 
1417     if ( NULL == pfile || INVALID_CSTR( format ) ) return 0;
1418 
1419     written = vsnprintf( buffer, SDL_arraysize( buffer ), format, args );
1420 
1421     if ( written > 0 )
1422     {
1423         written = PHYSFS_write( pfile, buffer, sizeof( char ), written );
1424     }
1425 
1426     return written;
1427 }
1428 
1429 //--------------------------------------------------------------------------------------------
vfs_printf(vfs_FILE * pfile,const char * format,...)1430 int vfs_printf( vfs_FILE * pfile, const char *format, ... )
1431 {
1432     va_list args;
1433     int retval;
1434 
1435     BAIL_IF_NOT_INIT();
1436 
1437     if ( NULL == pfile ) return 0;
1438 
1439     retval = 0;
1440     va_start( args, format );
1441     if ( vfs_cfile == pfile->type )
1442     {
1443         retval = vfprintf( pfile->ptr.c, format, args );
1444     }
1445     else
1446     {
1447         retval = fake_physfs_vprintf( pfile->ptr.p, format, args );
1448     }
1449     va_end( args );
1450 
1451     return retval;
1452 }
1453 
1454 //--------------------------------------------------------------------------------------------
vfs_scanf(vfs_FILE * pfile,const char * format,...)1455 int vfs_scanf( vfs_FILE * pfile, const char *format, ... )
1456 {
1457     va_list args;
1458     int retval;
1459 
1460     BAIL_IF_NOT_INIT();
1461 
1462     if ( NULL == pfile ) return 0;
1463 
1464     retval = 0;
1465     va_start( args, format );
1466     if ( vfs_cfile == pfile->type )
1467     {
1468         retval = _vfs_vfscanf( pfile->ptr.c, format, args );
1469     }
1470     //else if( vfs_physfs == pfile->type )
1471     //{
1472     //    retval = fake_physfs_vscanf( pfile->ptr.p, format, args );
1473     //}
1474     va_end( args );
1475 
1476     return retval;
1477 }
1478 
1479 //--------------------------------------------------------------------------------------------
1480 //--------------------------------------------------------------------------------------------
vfs_enumerateFiles(const char * dir_name)1481 char ** vfs_enumerateFiles( const char * dir_name )
1482 {
1483     BAIL_IF_NOT_INIT();
1484 
1485     return PHYSFS_enumerateFiles( vfs_convert_fname( dir_name ) );
1486 }
1487 
1488 //--------------------------------------------------------------------------------------------
vfs_freeList(void * listVar)1489 void    vfs_freeList( void * listVar )
1490 {
1491     BAIL_IF_NOT_INIT();
1492 
1493     PHYSFS_freeList( listVar );
1494 }
1495 
1496 //--------------------------------------------------------------------------------------------
_vfs_findClose(vfs_search_context_t * ctxt)1497 void _vfs_findClose( vfs_search_context_t * ctxt )
1498 {
1499     BAIL_IF_NOT_INIT();
1500 
1501     if ( NULL == ctxt ) return;
1502 
1503     if ( NULL != ctxt->file_list )
1504     {
1505         PHYSFS_freeList( ctxt->file_list );
1506         ctxt->file_list = NULL;
1507     }
1508     ctxt->ptr       = NULL;
1509 }
1510 
1511 //--------------------------------------------------------------------------------------------
_vfs_search(vfs_search_context_t ** pctxt)1512 vfs_search_context_t * _vfs_search( vfs_search_context_t ** pctxt )
1513 {
1514     const char * retval = NULL;
1515     static VFS_PATH  path_buffer = EMPTY_CSTR;
1516 
1517     BAIL_IF_NOT_INIT();
1518 
1519     if ( NULL == pctxt ) return NULL;
1520 
1521     // uninitialized file list?
1522     if ( NULL == ( *pctxt ) || NULL == ( *pctxt )->file_list )
1523     {
1524         ( *pctxt )->found[0] = CSTR_END;
1525         return ( *pctxt );
1526     }
1527 
1528     // emptry file list?
1529     if ( NULL == *(( *pctxt )->file_list ) )
1530     {
1531         goto _vfs_search_file_error;
1532     }
1533 
1534     if ( NULL == ( *pctxt )->ptr )
1535     {
1536         // if we haven't begun the search yet, get started
1537         ( *pctxt )->ptr = ( *pctxt )->file_list;
1538     }
1539     else
1540     {
1541         ( *pctxt )->ptr++;
1542     }
1543 
1544     // NULL == *((*pctxt)->ptr) signals the end of the list
1545     // if we exhausted the list, reset everything
1546     if ( NULL == ( *pctxt )->ptr || NULL == *(( *pctxt )->ptr ) )
1547     {
1548         goto _vfs_search_file_error;
1549     }
1550 
1551     // search for the correct extension (if any)
1552     retval = NULL;
1553     if ( CSTR_END == *( *pctxt )->ext )
1554     {
1555         int  found;
1556 
1557         for ( /* nothing */; NULL != *(( *pctxt )->ptr ); ( *pctxt )->ptr++ )
1558         {
1559             int is_dir;
1560             char * loc_path;
1561 
1562             if ( INVALID_CSTR(( *pctxt )->path ) )
1563             {
1564                 snprintf( path_buffer, SDL_arraysize( path_buffer ), NET_SLASH_STR "%s", *(( *pctxt )->ptr ) );
1565             }
1566             else
1567             {
1568                 snprintf( path_buffer, SDL_arraysize( path_buffer ), "%s" NET_SLASH_STR "%s", ( *pctxt )->path, *(( *pctxt )->ptr ) );
1569             }
1570 
1571             loc_path = ( char * )vfs_convert_fname( path_buffer );
1572 
1573             // have we found the correct type of object?
1574             found  = VFS_FALSE;
1575             is_dir = vfs_isDirectory( loc_path );
1576 
1577             if ( 0 != ( VFS_SEARCH_FILE & ( *pctxt )->bits ) )
1578             {
1579                 found = !is_dir;
1580             }
1581             else if ( 0 != ( VFS_SEARCH_DIR & ( *pctxt )->bits ) )
1582             {
1583                 found = is_dir;
1584             }
1585             else
1586             {
1587                 found = VFS_TRUE;
1588             }
1589 
1590             if ( found )
1591             {
1592                 retval = loc_path;
1593                 break;
1594             }
1595         }
1596     }
1597     else
1598     {
1599         size_t extension_length = strlen(( *pctxt )->ext );
1600 
1601         // scan through the list
1602         for ( /* nothing */; NULL != *(( *pctxt )->ptr ); ( *pctxt )->ptr++ )
1603         {
1604             int found, is_dir;
1605             size_t string_length;
1606             char * sztest;
1607             char * loc_path;
1608 
1609             //---- have we found the correct type of object?
1610 
1611             if ( INVALID_CSTR(( *pctxt )->path ) )
1612             {
1613                 snprintf( path_buffer, SDL_arraysize( path_buffer ), NET_SLASH_STR "%s", *(( *pctxt )->ptr ) );
1614             }
1615             else
1616             {
1617                 snprintf( path_buffer, SDL_arraysize( path_buffer ), "%s" NET_SLASH_STR "%s", ( *pctxt )->path, *(( *pctxt )->ptr ) );
1618             }
1619 
1620             loc_path = ( char * )vfs_convert_fname( path_buffer );
1621 
1622             found = VFS_FALSE;
1623             is_dir = vfs_isDirectory( loc_path );
1624             if ( 0 != ( VFS_SEARCH_FILE & ( *pctxt )->bits ) )
1625             {
1626                 found = !is_dir;
1627             }
1628             else if ( 0 != ( VFS_SEARCH_DIR & ( *pctxt )->bits ) )
1629             {
1630                 found = is_dir;
1631             }
1632             else
1633             {
1634                 found = VFS_TRUE;
1635             }
1636 
1637             if ( !found ) continue;
1638 
1639             //---- does the extension match?
1640             sztest = *(( *pctxt )->ptr );
1641 
1642             // get the length
1643             string_length = strlen( sztest );
1644 
1645             // grab the last bit of the test string
1646             if ((( signed )string_length - ( signed )extension_length ) >= 0 )
1647             {
1648                 sztest += ( string_length - extension_length );
1649             }
1650             else
1651             {
1652                 sztest = NULL;
1653             }
1654             if ( INVALID_CSTR( sztest ) ) continue;
1655 
1656             if ( 0 == strcmp( sztest, ( *pctxt )->ext ) )
1657             {
1658                 retval = loc_path;
1659                 break;
1660             }
1661         };
1662     }
1663 
1664     // reset the path buffer
1665     path_buffer[0] = CSTR_END;
1666 
1667     // test for the end condition again
1668     if ( NULL == ( *pctxt )->ptr || NULL == *(( *pctxt )->ptr ) )
1669     {
1670         vfs_findClose( pctxt );
1671         retval = NULL;
1672     }
1673     else
1674     {
1675         if ( 0 != ( VFS_SEARCH_BARE & ( *pctxt )->bits ) )
1676         {
1677             // do the "bare" option
1678             retval = NULL;
1679             if ( VALID_CSTR( *(( *pctxt )->ptr ) ) )
1680             {
1681                 strncpy( path_buffer, *(( *pctxt )->ptr ), SDL_arraysize( path_buffer ) );
1682                 retval = path_buffer;
1683             }
1684         }
1685         else
1686         {
1687             // return the full path
1688             if ( VALID_CSTR( retval ) )
1689             {
1690                 strncpy( path_buffer, retval, SDL_arraysize( path_buffer ) );
1691                 retval = path_buffer;
1692             }
1693             else
1694             {
1695                 retval = NULL;
1696             }
1697         }
1698     }
1699 
1700     if ( NULL == retval )
1701     {
1702         if ( NULL != *pctxt )
1703         {
1704             ( *pctxt )->found[0] = CSTR_END;
1705         }
1706     }
1707     else
1708     {
1709         strncpy(( *pctxt )->found, path_buffer, SDL_arraysize(( *pctxt )->found ) );
1710     }
1711 
1712     return *pctxt;
1713 
1714 _vfs_search_file_error:
1715 
1716     vfs_findClose( pctxt );
1717     return NULL;
1718 }
1719 
1720 //--------------------------------------------------------------------------------------------
vfs_findFirst(const char * search_path,const char * search_extension,Uint32 search_bits)1721 vfs_search_context_t * vfs_findFirst( const char * search_path, const char * search_extension, Uint32 search_bits )
1722 {
1723     vfs_search_context_t * ctxt;
1724 
1725     BAIL_IF_NOT_INIT();
1726 
1727     // create the new context
1728     ctxt = EGOBOO_NEW( vfs_search_context_t );
1729     if ( NULL == ctxt ) return NULL;
1730 
1731     // grab all the files
1732     ctxt->file_list = vfs_enumerateFiles( vfs_convert_fname( search_path ) );
1733     ctxt->ptr       = NULL;
1734 
1735     // no search list generated
1736     if ( NULL == ctxt->file_list )
1737     {
1738         return NULL;
1739     }
1740 
1741     // empty search list
1742     if ( NULL == *( ctxt->file_list ) )
1743     {
1744         vfs_findClose( &ctxt );
1745         return NULL;
1746     }
1747 
1748     // set the search extension
1749     if ( INVALID_CSTR( search_extension ) )
1750     {
1751         ctxt->ext[0] = CSTR_END;
1752     }
1753     else
1754     {
1755         snprintf( ctxt->ext, SDL_arraysize( ctxt->ext ), ".%s", search_extension );
1756     }
1757 
1758     // set the search path
1759     if ( INVALID_CSTR( search_path ) )
1760     {
1761         ctxt->path[0] = CSTR_END;
1762     }
1763     else
1764     {
1765         strncpy( ctxt->path, search_path, SDL_arraysize( ctxt->path ) );
1766     }
1767 
1768     ctxt->bits = search_bits;
1769 
1770     ctxt = _vfs_search( &ctxt );
1771 
1772     return ctxt;
1773 }
1774 
1775 //--------------------------------------------------------------------------------------------
vfs_findNext(vfs_search_context_t ** pctxt)1776 vfs_search_context_t * vfs_findNext( vfs_search_context_t ** pctxt )
1777 {
1778     // if there are no files, return an error value
1779 
1780     BAIL_IF_NOT_INIT();
1781 
1782     if ( NULL == pctxt || NULL == *pctxt ) return NULL;
1783 
1784     if ( NULL == ( *pctxt )->file_list )
1785     {
1786         return NULL;
1787     }
1788 
1789     *pctxt = _vfs_search( pctxt );
1790 
1791     return *pctxt;
1792 }
1793 
1794 //--------------------------------------------------------------------------------------------
vfs_findClose(vfs_search_context_t ** ctxt)1795 void vfs_findClose( vfs_search_context_t ** ctxt )
1796 {
1797     BAIL_IF_NOT_INIT();
1798 
1799     if ( NULL != ctxt )
1800     {
1801         _vfs_findClose( *ctxt );
1802         EGOBOO_DELETE(( *ctxt ) );
1803     }
1804 }
1805 
1806 //--------------------------------------------------------------------------------------------
vfs_removeDirectoryAndContents(const char * dirname,int recursive)1807 int vfs_removeDirectoryAndContents( const char * dirname, int recursive )
1808 {
1809     // buffer the directory delete through PHYSFS, so that we so not access functions that
1810     // we have no right to! :)
1811 
1812     const char *  write_dir;
1813 
1814     BAIL_IF_NOT_INIT();
1815 
1816     // make sure that this is a valid directory
1817     write_dir = vfs_resolveWriteFilename( dirname );
1818     if ( !fs_fileIsDirectory( write_dir ) ) return VFS_FALSE;
1819 
1820     fs_removeDirectoryAndContents( write_dir, recursive );
1821 
1822     return VFS_TRUE;
1823 }
1824 
1825 //--------------------------------------------------------------------------------------------
_vfs_copyFile(const char * source,const char * dest)1826 static bool_t _vfs_copyFile( const char *source, const char *dest )
1827 {
1828     /// @details ZZ@> This function copies a file on the local machine
1829     PHYSFS_File *sourcef = NULL, *destf = NULL;
1830     char         buf[4096] = EMPTY_CSTR;
1831     int          bytes_read;
1832     bool_t       retval = VFS_FALSE;
1833 
1834     BAIL_IF_NOT_INIT();
1835 
1836     sourcef = PHYSFS_openRead( source );
1837     if ( !sourcef )
1838     {
1839         goto _vfs_copyFile_end;
1840     }
1841 
1842     destf = PHYSFS_openWrite( dest );
1843     if ( !sourcef )
1844     {
1845         goto _vfs_copyFile_end;
1846     }
1847 
1848     retval = VFS_TRUE;
1849 
1850     bytes_read = btrue;
1851     while ( bytes_read > 0 )
1852     {
1853         bytes_read = PHYSFS_read( sourcef, buf, sizeof( char ), SDL_arraysize( buf ) );
1854         PHYSFS_write( destf, buf, sizeof( char ), bytes_read );
1855     }
1856 
1857 _vfs_copyFile_end:
1858 
1859     if ( NULL != sourcef ) PHYSFS_close( sourcef );
1860     if ( NULL != destf ) PHYSFS_close( destf );
1861 
1862     return retval;
1863 }
1864 
1865 //--------------------------------------------------------------------------------------------
vfs_copyFile(const char * source,const char * dest)1866 int vfs_copyFile( const char *source, const char *dest )
1867 {
1868     // buffer the directory delete through PHYSFS, so that we so not access functions that
1869     // we have no right to! :)
1870 
1871     VFS_PATH     sz_src = EMPTY_CSTR, sz_dst = EMPTY_CSTR;
1872     const char * real_dst, * real_src;
1873 
1874     BAIL_IF_NOT_INIT();
1875 
1876     if ( INVALID_CSTR( source ) || INVALID_CSTR( dest ) )
1877     {
1878         return VFS_FALSE;
1879     }
1880 
1881     if ( !_vfs_ensure_write_directory( dest, bfalse ) )
1882     {
1883         return VFS_FALSE;
1884     }
1885 
1886     strncpy( sz_src, vfs_resolveReadFilename( source ), SDL_arraysize( sz_src ) );
1887     real_src = sz_src;
1888 
1889     strncpy( sz_dst, vfs_resolveWriteFilename( dest ), SDL_arraysize( sz_dst ) );
1890     real_dst = sz_dst;
1891 
1892     if ( INVALID_CSTR( sz_src ) || INVALID_CSTR( sz_dst ) )
1893     {
1894         return VFS_FALSE;
1895     }
1896 
1897     // if they are the same files, do nothing
1898     if ( 0 == strcmp( sz_src, sz_dst ) )
1899     {
1900         return VFS_FALSE;
1901     }
1902 
1903     // !assume! that we are not dealing with archives, and just use the
1904     // fs_* copy command
1905     if ( !fs_copyFile( real_src, real_dst ) ) return VFS_FALSE;
1906 
1907     return VFS_TRUE;
1908 }
1909 
1910 //--------------------------------------------------------------------------------------------
vfs_copyDirectory(const char * sourceDir,const char * destDir)1911 int vfs_copyDirectory( const char *sourceDir, const char *destDir )
1912 {
1913     /// @details ZZ@> This function copies all files in a directory
1914     VFS_PATH srcPath = EMPTY_CSTR, destPath = EMPTY_CSTR;
1915     const char *fileName;
1916 
1917     VFS_PATH     szDst = EMPTY_CSTR;
1918     const char * real_dst;
1919 
1920     vfs_search_context_t * ctxt;
1921 
1922     BAIL_IF_NOT_INIT();
1923 
1924     if ( INVALID_CSTR( sourceDir ) || INVALID_CSTR( destDir ) )
1925     {
1926         return VFS_FALSE;
1927     }
1928 
1929     // make sure the destination directory exists
1930     if ( !vfs_mkdir( destDir ) )
1931     {
1932         return VFS_FALSE;
1933     }
1934 
1935     // get the a filename that we are allowed to write to
1936     snprintf( szDst, SDL_arraysize( szDst ), "%s",  vfs_resolveWriteFilename( destDir ) );
1937     real_dst = szDst;
1938 
1939     // List all the files in the directory
1940     ctxt = vfs_findFirst( vfs_convert_fname( sourceDir ), NULL, VFS_SEARCH_FILE | VFS_SEARCH_BARE );
1941     fileName = vfs_search_context_get_current( ctxt );
1942 
1943     while ( NULL != ctxt && VALID_CSTR( fileName ) )
1944     {
1945         // Ignore files that begin with a .
1946         if ( '.' != fileName[0] )
1947         {
1948             snprintf( srcPath, SDL_arraysize( srcPath ), "%s/%s", sourceDir, fileName );
1949             snprintf( destPath, SDL_arraysize( destPath ), "%s/%s", destDir, fileName );
1950 
1951             if ( !vfs_copyFile( srcPath, destPath ) )
1952             {
1953                 log_debug( "vfs_copyDirectory() - Failed to copy from \"%s\" to \"%s\" (%s)\n", srcPath, destPath, vfs_getError() );
1954             }
1955         }
1956         ctxt = vfs_findNext( &ctxt );
1957 
1958         fileName = vfs_search_context_get_current( ctxt );
1959     }
1960     vfs_findClose( &ctxt );
1961 
1962     return VFS_TRUE;
1963 }
1964 
1965 //--------------------------------------------------------------------------------------------
vfs_ungetc(int c,vfs_FILE * pfile)1966 int vfs_ungetc( int c, vfs_FILE * pfile )
1967 {
1968     int retval = 0;
1969 
1970     BAIL_IF_NOT_INIT();
1971 
1972     if ( NULL == pfile ) return 0;
1973 
1974     if ( vfs_cfile == pfile->type )
1975     {
1976         retval = ungetc( c, pfile->ptr.c );
1977     }
1978     else if ( vfs_physfs == pfile->type )
1979     {
1980         retval = 0;
1981         //retval = PHYSFS_write( pfile->ptr.p, &c, 1, sizeof(char) );
1982     }
1983 
1984     return retval;
1985 }
1986 
1987 //--------------------------------------------------------------------------------------------
vfs_getc(vfs_FILE * pfile)1988 int vfs_getc( vfs_FILE * pfile )
1989 {
1990     int retval = 0;
1991 
1992     BAIL_IF_NOT_INIT();
1993 
1994     if ( NULL == pfile ) return 0;
1995 
1996     retval = 0;
1997     if ( vfs_cfile == pfile->type )
1998     {
1999         retval = fgetc( pfile->ptr.c );
2000         if ( EOF == retval ) pfile->flags |= VFS_EOF;
2001     }
2002     else if ( vfs_physfs == pfile->type )
2003     {
2004         char cTmp;
2005         retval = PHYSFS_read( pfile->ptr.p, &cTmp, 1, sizeof( cTmp ) );
2006 
2007         if ( -1 == retval ) pfile->flags |= VFS_ERROR;
2008         if ( 0 == retval ) pfile->flags |= VFS_EOF;
2009     }
2010 
2011     return retval;
2012 }
2013 
2014 //--------------------------------------------------------------------------------------------
vfs_putc(int c,vfs_FILE * pfile)2015 int vfs_putc( int c, vfs_FILE * pfile )
2016 {
2017     int retval = 0;
2018 
2019     BAIL_IF_NOT_INIT();
2020 
2021     if ( NULL == pfile ) return 0;
2022 
2023     if ( vfs_cfile == pfile->type )
2024     {
2025         retval = fputc( c, pfile->ptr.c );
2026     }
2027     else if ( vfs_physfs == pfile->type )
2028     {
2029         retval = PHYSFS_write( pfile->ptr.p, &c, 1, sizeof( char ) );
2030     }
2031 
2032     return retval;
2033 }
2034 
2035 //--------------------------------------------------------------------------------------------
vfs_puts(const char * str,vfs_FILE * pfile)2036 int vfs_puts( const char * str , vfs_FILE * pfile )
2037 {
2038     int retval = 0;
2039 
2040     BAIL_IF_NOT_INIT();
2041 
2042     if ( NULL == pfile || INVALID_CSTR( str ) ) return 0;
2043 
2044     if ( vfs_cfile == pfile->type )
2045     {
2046         retval = fputs( str, pfile->ptr.c );
2047     }
2048     else if ( vfs_physfs == pfile->type )
2049     {
2050         size_t len = strlen( str );
2051 
2052         retval = PHYSFS_write( pfile->ptr.p, str, len + 1, sizeof( char ) );
2053     }
2054 
2055     return retval;
2056 }
2057 
2058 //--------------------------------------------------------------------------------------------
vfs_gets(char * buffer,int buffer_size,vfs_FILE * pfile)2059 char * vfs_gets( char * buffer, int buffer_size, vfs_FILE * pfile )
2060 {
2061     char * retval = NULL;
2062 
2063     BAIL_IF_NOT_INIT();
2064 
2065     if ( NULL == pfile ) return NULL;
2066 
2067     if ( NULL == buffer || 0 == buffer_size ) return buffer;
2068 
2069     if ( vfs_cfile == pfile->type )
2070     {
2071         retval = fgets( buffer, buffer_size, pfile->ptr.c );
2072         if ( NULL == retval ) pfile->flags |= VFS_EOF;
2073     }
2074     else if ( vfs_physfs == pfile->type )
2075     {
2076         int  iTmp;
2077         char cTmp;
2078         char * str_ptr, * str_end;
2079 
2080         str_ptr = buffer;
2081         str_end = buffer + buffer_size;
2082 
2083         iTmp = PHYSFS_read( pfile->ptr.p, &cTmp, 1, sizeof( cTmp ) );
2084         if ( -1 == iTmp ) pfile->flags |= VFS_ERROR;
2085         if ( 0 == iTmp ) pfile->flags |= VFS_EOF;
2086 
2087         while ( iTmp && ( str_ptr < str_end - 1 ) && CSTR_END != cTmp && 0 == pfile->flags )
2088         {
2089             *str_ptr = cTmp;
2090             str_ptr++;
2091 
2092             if ( ASCII_LINEFEED_CHAR ==  cTmp || C_CARRIAGE_RETURN_CHAR ==  cTmp ) break;
2093 
2094             iTmp = PHYSFS_read( pfile->ptr.p, &cTmp, 1, sizeof( cTmp ) );
2095 
2096             if ( -1 == iTmp ) pfile->flags |= VFS_ERROR;
2097             if ( 0 == iTmp ) pfile->flags |= VFS_EOF;
2098         }
2099         *str_ptr = CSTR_END;
2100 
2101         retval = buffer;
2102     }
2103 
2104     return retval;
2105 }
2106 
2107 //--------------------------------------------------------------------------------------------
vfs_empty_temp_directories()2108 void vfs_empty_temp_directories()
2109 {
2110     BAIL_IF_NOT_INIT();
2111 
2112     vfs_removeDirectoryAndContents( "import", VFS_TRUE );
2113     vfs_removeDirectoryAndContents( "remote", VFS_TRUE );
2114 }
2115 
2116 //--------------------------------------------------------------------------------------------
_vfs_vfscanf(FILE * file,const char * format,va_list args)2117 int _vfs_vfscanf( FILE * file, const char * format, va_list args )
2118 {
2119     // UGH! Just break the format code into pieces and call fscanf on each piece
2120 
2121     char   sub_format[256] = EMPTY_CSTR;
2122     char * format_end, * format_next;
2123     int    argcount = 0;
2124     void * ptr;
2125 
2126     BAIL_IF_NOT_INIT();
2127 
2128     if ( NULL == file || INVALID_CSTR( format ) ) return 0;
2129 
2130     format_end = ( char * )( format + strlen( format ) );
2131 
2132     // scan through the format string looking for formats
2133     argcount = 0;
2134     while ( format < format_end )
2135     {
2136         bool_t found_format;
2137         char * format_tmp;
2138 
2139         // find everything up to the first valid format code in the format string
2140         found_format = bfalse;
2141         format_tmp   = ( char * )format;
2142         format_next  = format_tmp;
2143         while ( format_next < format_end )
2144         {
2145             format_next = strchr( format_tmp, '%' );
2146 
2147             // handle the occurrence of "%%"
2148             if ( '%' == *( format_next + 1 ) )
2149             {
2150                 format_tmp = format_next + 1;
2151             }
2152             else
2153             {
2154                 found_format = btrue;
2155                 break;
2156             }
2157         }
2158 
2159         // copy the format string fragment
2160         if ( found_format && format_next < format_end )
2161         {
2162             // scan the valid format code
2163             format_next += strcspn( format_next, "cCsSdioxXnueEfgG" ) + 1;
2164         }
2165         strncpy( sub_format, format, format_next - format );
2166 
2167         // get a pointer to the variable to be filled
2168         ptr = NULL;
2169         if ( found_format )
2170         {
2171             ptr = va_arg( args, void * );
2172         }
2173 
2174         // do the call to fscanf()
2175         if ( NULL == ptr )
2176         {
2177             // this may cause a problem...
2178             fscanf( file, sub_format, NULL );
2179         }
2180         else
2181         {
2182             argcount += fscanf( file, sub_format, ptr );
2183         }
2184 
2185         format = format_next;
2186     }
2187 
2188     return argcount;
2189 }
2190 
2191 //--------------------------------------------------------------------------------------------
vfs_rewind(vfs_FILE * pfile)2192 int vfs_rewind( vfs_FILE * pfile )
2193 {
2194     BAIL_IF_NOT_INIT();
2195 
2196     if ( NULL == pfile ) return 0;
2197 
2198     return vfs_seek( pfile, 0 );
2199 }
2200 
2201 //--------------------------------------------------------------------------------------------
_vfs_translate_error(vfs_FILE * pfile)2202 void _vfs_translate_error( vfs_FILE * pfile )
2203 {
2204     BAIL_IF_NOT_INIT();
2205 
2206     if ( NULL == pfile ) return;
2207 
2208     if ( vfs_cfile == pfile->type )
2209     {
2210         if ( ferror( pfile->ptr.c ) )
2211         {
2212             SET_BIT( pfile->flags, VFS_ERROR );
2213         }
2214 
2215         if ( feof( pfile->ptr.c ) )
2216         {
2217             SET_BIT( pfile->flags, VFS_EOF );
2218         }
2219     }
2220     else if ( vfs_physfs == pfile->type )
2221     {
2222         if ( PHYSFS_eof( pfile->ptr.p ) )
2223         {
2224             SET_BIT( pfile->flags, VFS_EOF );
2225         }
2226     }
2227 }
2228 
2229 //--------------------------------------------------------------------------------------------
vfs_getError()2230 const char * vfs_getError()
2231 {
2232     /// @details ZF@> Returns the last error the PHYSFS system reported.
2233 
2234     static char errors[1024];
2235     const char * physfs_error, * file_error;
2236     bool_t is_error;
2237 
2238     BAIL_IF_NOT_INIT();
2239 
2240     // load up a default
2241     strncpy( errors, "unknown error", SDL_arraysize( errors ) );
2242 
2243     // assume no error
2244     is_error = bfalse;
2245 
2246     // try to get the physfs error state;
2247     physfs_error = PHYSFS_getLastError();
2248     if ( NULL == physfs_error ) physfs_error = "no error";
2249 
2250     // try to get the stdio error state
2251     file_error = strerror( errno );
2252 
2253     snprintf( errors, SDL_arraysize( errors ), "c stdio says:\"%s\" -- physfs says:\"%s\"", file_error, physfs_error );
2254 
2255     return errors;
2256 }
2257 
2258 //--------------------------------------------------------------------------------------------
vfs_add_mount_point(const char * root_path,const char * relative_path,const char * mount_point,int append)2259 int vfs_add_mount_point( const char * root_path, const char * relative_path, const char * mount_point, int append )
2260 {
2261     /// @details BB@> a wrapper for PHYSFS_mount
2262 
2263     int retval = -1;
2264     const char * loc_dirname;
2265     VFS_PATH     dirname;
2266 
2267     BAIL_IF_NOT_INIT();
2268 
2269     // a bare slash is taken to mean the PHYSFS root directory, not the root of the currently mounted volume
2270     if ( !VALID_CSTR( mount_point ) || 0 == strcmp( mount_point, C_SLASH_STR ) ) return 0;
2271 
2272     // make a complete version of the pathname
2273     if ( VALID_CSTR( root_path ) && VALID_CSTR( relative_path ) )
2274     {
2275         snprintf( dirname, SDL_arraysize( dirname ), "%s" SLASH_STR "%s", root_path, relative_path );
2276     }
2277     else if ( VALID_CSTR( root_path ) )
2278     {
2279         strncpy( dirname, root_path, SDL_arraysize( dirname ) );
2280     }
2281     else if ( VALID_CSTR( relative_path ) )
2282     {
2283         strncpy( dirname, relative_path, SDL_arraysize( dirname ) );
2284     }
2285     else
2286     {
2287         return 0;
2288     }
2289 
2290     /// @note ZF@> 2010-06-30 vfs_convert_fname_sys() broke the Linux version
2291     /// @note BB@> 2010-06-30 the error in vfs_convert_fname_sys() might be fixed now
2292     loc_dirname = vfs_convert_fname_sys( dirname );
2293 
2294     if ( _vfs_mount_info_add( mount_point, root_path, relative_path ) )
2295     {
2296         retval = PHYSFS_mount( loc_dirname, mount_point, append );
2297         if ( 1 != retval )
2298         {
2299             // go back and remove the mount info, since PHYSFS rejected the
2300             // data we gave it
2301             int i = _vfs_mount_info_matches( mount_point, loc_dirname );
2302             _vfs_mount_info_remove( i );
2303         }
2304     }
2305 
2306     return retval;
2307 }
2308 
2309 //--------------------------------------------------------------------------------------------
vfs_remove_mount_point(const char * mount_point)2310 int vfs_remove_mount_point( const char * mount_point )
2311 {
2312     /// @details BB@> Remove every single search path related to the given mount point.
2313 
2314     int retval, cnt;
2315 
2316     BAIL_IF_NOT_INIT();
2317 
2318     // don't allow it to remove the default directory
2319     if ( !VALID_CSTR( mount_point ) ) return 0;
2320     if ( 0 == strcmp( mount_point, C_SLASH_STR ) ) return 0;
2321 
2322     // assume we are going to fail
2323     retval = 0;
2324 
2325     // see if we have the mount point
2326     cnt = _vfs_mount_info_matches( mount_point, NULL );
2327 
2328     // does it exist in the list?
2329     if ( cnt < 0 ) return bfalse;
2330 
2331     while ( cnt >= 0 )
2332     {
2333         // we have to use the path name to remove the search path, not the mount point name
2334         PHYSFS_removeFromSearchPath( _vfs_mount_info[cnt].full_path );
2335 
2336         // remove the mount info from this index
2337         // PF> we remove it even if PHYSFS_removeFromSearchPath() fails or else we might get an infinite loop
2338         _vfs_mount_info_remove( cnt );
2339 
2340         cnt = _vfs_mount_info_matches( mount_point, NULL );
2341     }
2342 
2343     return retval;
2344 }
2345 
2346 //--------------------------------------------------------------------------------------------
vfs_search_context_get_current(vfs_search_context_t * ctxt)2347 const char * vfs_search_context_get_current( vfs_search_context_t * ctxt )
2348 {
2349     BAIL_IF_NOT_INIT();
2350 
2351     if ( NULL == ctxt ) return NULL;
2352 
2353     return ctxt->found;
2354 }
2355 
2356 //--------------------------------------------------------------------------------------------
2357 //--------------------------------------------------------------------------------------------
_vfs_mount_info_search(const char * some_path)2358 int _vfs_mount_info_search( const char * some_path )
2359 {
2360     /// @details BB@> check to see if the given path is actually relative to a registered
2361     ///               virtual mount point
2362 
2363     int cnt, retval = VFS_FALSE;
2364     VFS_PATH temp_path;
2365 
2366     BAIL_IF_NOT_INIT();
2367 
2368     if ( !VALID_CSTR( some_path ) ) return retval;
2369 
2370     for ( cnt = 0; cnt < _vfs_mount_info_count; cnt++ )
2371     {
2372         int len;
2373 
2374         if ( 0 == strcmp( _vfs_mount_info[cnt].mount, some_path ) )
2375         {
2376             retval = VFS_TRUE;
2377             break;
2378         }
2379 
2380         snprintf( temp_path, SDL_arraysize( temp_path ), "%s" NET_SLASH_STR, _vfs_mount_info[cnt].mount );
2381         len = strlen( temp_path );
2382 
2383         if ( 0 == strncmp( temp_path, some_path, len ) )
2384         {
2385             retval = VFS_TRUE;
2386             break;
2387         }
2388     }
2389 
2390     return retval;
2391 }
2392 
2393 //--------------------------------------------------------------------------------------------
vfs_mount_info_strip_path(const char * some_path)2394 const char * vfs_mount_info_strip_path( const char * some_path )
2395 {
2396     int cnt;
2397     size_t offset;
2398     const char * ptmp, * stripped_pos;
2399 
2400     BAIL_IF_NOT_INIT();
2401 
2402     stripped_pos = some_path;
2403 
2404     // strip any starting slashes
2405     for ( ptmp = some_path; ( CSTR_END != *ptmp ) && ptmp < some_path + VFS_MAX_PATH; ptmp++ )
2406     {
2407         if ( NET_SLASH_CHR != *ptmp && WIN32_SLASH_CHR != *ptmp )
2408         {
2409             break;
2410         }
2411     }
2412     some_path = ptmp;
2413 
2414     ptmp   = NULL;
2415     offset = 0;
2416     for ( cnt = 0; cnt < _vfs_mount_info_count; cnt++ )
2417     {
2418         offset = strlen( _vfs_mount_info[cnt].mount );
2419         if ( offset <= 0 ) continue;
2420 
2421         if ( 0 == strncmp( some_path, _vfs_mount_info[cnt].mount, offset ) )
2422         {
2423             stripped_pos = some_path + offset;
2424             break;
2425         }
2426     }
2427 
2428     return stripped_pos;
2429 }
2430 
2431 //--------------------------------------------------------------------------------------------
_vfs_mount_info_matches(const char * mount_point,const char * local_path)2432 int _vfs_mount_info_matches( const char * mount_point, const char * local_path )
2433 {
2434     int cnt, retval;
2435     const char * ptmp;
2436 
2437     BAIL_IF_NOT_INIT();
2438 
2439     // set to an invalid value;
2440     retval = -1;
2441 
2442     // are there any in the list?
2443     if ( 0 == _vfs_mount_info_count ) return retval;
2444 
2445     // alias the mount point
2446     ptmp = mount_point;
2447 
2448     // strip any starting slashes
2449     if ( VALID_CSTR( ptmp ) )
2450     {
2451         for ( /* nothing */; ptmp < mount_point + VFS_MAX_PATH; ptmp++ )
2452         {
2453             if (( NET_SLASH_CHR != *ptmp && WIN32_SLASH_CHR != *ptmp ) || CSTR_END == *ptmp )
2454             {
2455                 break;
2456             }
2457         }
2458     }
2459 
2460     if ( VALID_CSTR( ptmp ) && VALID_CSTR( local_path ) )
2461     {
2462         // find the first path info with the given mount_point and local_path
2463         for ( cnt = 0; cnt < _vfs_mount_info_count; cnt++ )
2464         {
2465             if ( 0 == strncmp( _vfs_mount_info[cnt].mount,     mount_point, VFS_MAX_PATH ) &&
2466                  0 == strncmp( _vfs_mount_info[cnt].full_path, local_path,  VFS_MAX_PATH ) )
2467             {
2468                 retval = cnt;
2469                 break;
2470             }
2471         }
2472     }
2473     else if ( VALID_CSTR( ptmp ) )
2474     {
2475         // find the first path info with the given mount_point
2476         for ( cnt = 0; cnt < _vfs_mount_info_count; cnt++ )
2477         {
2478             if ( 0 == strncmp( _vfs_mount_info[cnt].mount, mount_point, VFS_MAX_PATH ) )
2479             {
2480                 retval = cnt;
2481                 break;
2482             }
2483         }
2484     }
2485     else if ( VALID_CSTR( local_path ) )
2486     {
2487         // find the first path info with the given local_path
2488         for ( cnt = 0; cnt < _vfs_mount_info_count; cnt++ )
2489         {
2490             if ( 0 == strncmp( _vfs_mount_info[cnt].full_path, local_path, VFS_MAX_PATH ) )
2491             {
2492                 retval = cnt;
2493                 break;
2494             }
2495         }
2496     }
2497 
2498     return retval;
2499 }
2500 
2501 //--------------------------------------------------------------------------------------------
_vfs_mount_info_add(const char * mount_point,const char * root_path,const char * relative_path)2502 bool_t _vfs_mount_info_add( const char * mount_point, const char * root_path, const char * relative_path )
2503 {
2504     const char * ptmp;
2505 
2506     VFS_PATH     local_path;
2507 
2508     BAIL_IF_NOT_INIT();
2509 
2510     // can we add it?
2511     if ( _vfs_mount_info_count >= MAX_MOUNTINFO ) return bfalse;
2512 
2513     // if the mount point is not a string, do nothing
2514     if ( !VALID_CSTR( mount_point ) ) return bfalse;
2515 
2516     // make a complete version of the pathname
2517     if ( VALID_CSTR( root_path ) && VALID_CSTR( relative_path ) )
2518     {
2519         snprintf( local_path, SDL_arraysize( local_path ), "%s" SLASH_STR "%s", root_path, relative_path );
2520     }
2521     else if ( VALID_CSTR( root_path ) )
2522     {
2523         strncpy( local_path, root_path, SDL_arraysize( local_path ) );
2524     }
2525     else if ( VALID_CSTR( relative_path ) )
2526     {
2527         strncpy( local_path, relative_path, SDL_arraysize( local_path ) );
2528     }
2529     else
2530     {
2531         return bfalse;
2532     }
2533 
2534     // do we want to add it?
2535     if ( !VALID_CSTR( local_path ) ) return bfalse;
2536 
2537     if ( _vfs_mount_info_matches( mount_point, local_path ) >= 0 ) return bfalse;
2538 
2539     // strip any starting slashes
2540     for ( ptmp = mount_point; ptmp < mount_point + VFS_MAX_PATH; ptmp++ )
2541     {
2542         if (( NET_SLASH_CHR != *ptmp && WIN32_SLASH_CHR != *ptmp ) || CSTR_END == *ptmp )
2543         {
2544             break;
2545         }
2546     }
2547 
2548     if ( CSTR_END == *ptmp ) return bfalse;
2549 
2550     // save the mount point in a list for later detection
2551     strncpy( _vfs_mount_info[_vfs_mount_info_count].mount,     ptmp,       VFS_MAX_PATH );
2552     strncpy( _vfs_mount_info[_vfs_mount_info_count].full_path, local_path, VFS_MAX_PATH );
2553 
2554     _vfs_mount_info[_vfs_mount_info_count].root_path[0] = CSTR_END;
2555     if ( VALID_CSTR( root_path ) )
2556     {
2557         strncpy( _vfs_mount_info[_vfs_mount_info_count].root_path, root_path, VFS_MAX_PATH );
2558     }
2559 
2560     _vfs_mount_info[_vfs_mount_info_count].relative_path[0] = CSTR_END;
2561     if ( VALID_CSTR( relative_path ) )
2562     {
2563         strncpy( _vfs_mount_info[_vfs_mount_info_count].relative_path, relative_path, VFS_MAX_PATH );
2564     }
2565 
2566     _vfs_mount_info_count++;
2567 
2568     return btrue;
2569 }
2570 
2571 //--------------------------------------------------------------------------------------------
_vfs_mount_info_remove(int cnt)2572 bool_t _vfs_mount_info_remove( int cnt )
2573 {
2574     BAIL_IF_NOT_INIT();
2575 
2576     // does it exist in the list?
2577     if ( cnt < 0 || cnt > _vfs_mount_info_count ) return bfalse;
2578 
2579     // fill in the hole in the list
2580     if ( _vfs_mount_info_count > 1 )
2581     {
2582         memmove( _vfs_mount_info + cnt, _vfs_mount_info + ( _vfs_mount_info_count - 1 ), sizeof( vfs_path_data_t ) );
2583     }
2584 
2585     // shorten the list
2586     _vfs_mount_info_count--;
2587 
2588     return btrue;
2589 }
2590 
2591 //--------------------------------------------------------------------------------------------
2592 //--------------------------------------------------------------------------------------------
vfs_set_base_search_paths()2593 void vfs_set_base_search_paths()
2594 {
2595     BAIL_IF_NOT_INIT();
2596 
2597     // Put write dir first in search path...
2598     PHYSFS_addToSearchPath( fs_getUserDirectory(), 0 );
2599 
2600     // Put base path on search path...
2601     PHYSFS_addToSearchPath( fs_getDataDirectory(), 1 );
2602 }