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 }