naev 0.12.5
nfile.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
12#include <dirent.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <sys/stat.h>
16
17#include "SDL_stdinc.h"
18
19#include <errno.h>
20#include <libgen.h> /* dirname / basename */
21#if HAS_POSIX
22#include <libgen.h>
23#include <sys/types.h>
24#include <unistd.h>
25#endif /* HAS_POSIX */
26#if __WIN32__
27#include <windows.h>
28#endif /* __WIN32__ */
30
31#include "nfile.h"
32
33#include "array.h"
34#include "conf.h"
35#if __MACOSX__
36#include "glue_macos.h"
37#endif /* __MACOSX__ */
38#include "log.h"
39
40#if HAS_UNIX && !__MACOSX__
42
49static char *xdgGetEnv( const char *name )
50{
51 char *env = SDL_getenv( name );
52 if ( ( env != NULL ) && ( env[0] != '\0' ) )
53 return env;
54 /* What errno signifies missing env var? */
55 errno = EINVAL;
56 return NULL;
57}
58
65static char *xdgEnvDup( const char *name )
66{
67 const char *env = xdgGetEnv( name );
68 if ( env != NULL )
69 return strdup( env );
70 return NULL;
71}
72
82static char *xdgGetRelativeHome( const char *envname,
83 const char *relativefallback )
84{
85 char *relhome = xdgEnvDup( envname );
86 if ( ( relhome == NULL ) && ( errno != ENOMEM ) ) {
87 const char *home;
88 unsigned int homelen;
89 errno = 0;
90 home = xdgGetEnv( "HOME" );
91 if ( home == NULL )
92 return NULL;
93 homelen = strlen( home );
94 unsigned int fallbacklength;
95 fallbacklength = strlen( relativefallback );
96 relhome = malloc( homelen + fallbacklength + 1 );
97 if ( relhome == NULL )
98 return NULL;
99 memcpy( relhome, home, homelen );
100 memcpy( &relhome[homelen], relativefallback, fallbacklength + 1 );
101 relhome[homelen + fallbacklength] = '\0'; /* Just in case. */
102 }
103 return relhome;
104}
105#endif /* HAS_UNIX && !__MACOSX__ */
106
107static char naev_configPath[PATH_MAX] = "\0";
113const char *nfile_configPath( void )
114{
115 if ( naev_configPath[0] == '\0' ) {
116 /* Global override is set. */
117 if ( conf.datapath ) {
118 snprintf( naev_configPath, sizeof( naev_configPath ), "%s/",
119 conf.datapath );
120 return naev_configPath;
121 }
122#if __MACOSX__
123 if ( macos_configPath( naev_configPath, sizeof( naev_configPath ) ) !=
124 0 ) {
125 WARN( _( "Cannot determine config path, using current directory." ) );
126 snprintf( naev_configPath, sizeof( naev_configPath ), "./naev/" );
127 }
128#elif HAS_UNIX
129 char *path = xdgGetRelativeHome( "XDG_CONFIG_HOME", "/.config" );
130 if ( path == NULL ) {
131 WARN( _( "$XDG_CONFIG_HOME isn't set, using current directory." ) );
132 path = strdup( "." );
133 }
134
135 snprintf( naev_configPath, sizeof( naev_configPath ), "%s/naev/", path );
136 free( path );
137#elif __WIN32__
138 char *path = SDL_getenv( "APPDATA" );
139 if ( path == NULL ) {
140 WARN( _( "%%APPDATA%% isn't set, using current directory." ) );
141 path = ".";
142 }
143 snprintf( naev_configPath, sizeof( naev_configPath ), "%s/naev/", path );
144#elif SDL_VERSION_ATLEAST( 3, 0, 0 )
145 char *prefpath = SDL_GetPrefPath( "Naev DevTeam", "Naev" );
146 strncpy( naev_configPath, sizeof( naev_configPath ), prefpath );
147 SDL_free( prefpath );
148#else
149#error "Feature needs implementation on this Operating System for Naev to work."
150#endif
151 }
152
153 return naev_configPath;
154}
155
156static char naev_cachePath[PATH_MAX] = "\0";
162const char *nfile_cachePath( void )
163{
164 if ( naev_cachePath[0] == '\0' ) {
165 /* Global override is set. */
166 if ( conf.datapath ) {
167 snprintf( naev_cachePath, sizeof( naev_cachePath ), "%s/",
168 conf.datapath );
169 return naev_cachePath;
170 }
171#if __MACOSX__
172 if ( macos_cachePath( naev_cachePath, sizeof( naev_cachePath ) ) != 0 ) {
173 WARN( _( "Cannot determine cache path, using current directory." ) );
174 snprintf( naev_cachePath, sizeof( naev_cachePath ), "./naev/" );
175 }
176#elif HAS_UNIX
177 char *path = xdgGetRelativeHome( "XDG_CACHE_HOME", "/.cache" );
178 if ( path == NULL ) {
179 WARN( _( "$XDG_CACHE_HOME isn't set, using current directory." ) );
180 path = strdup( "." );
181 }
182
183 snprintf( naev_cachePath, sizeof( naev_cachePath ), "%s/naev/", path );
184 free( path );
185#elif __WIN32__
186 char *path = SDL_getenv( "APPDATA" );
187 if ( path == NULL ) {
188 WARN( _( "%%APPDATA%% isn't set, using current directory." ) );
189 path = ".";
190 }
191 snprintf( naev_cachePath, sizeof( naev_cachePath ), "%s/naev/", path );
192#elif SDL_VERSION_ATLEAST( 3, 0, 0 )
193 char *prefpath = SDL_GetPrefPath( "Naev DevTeam", "Naev" );
194 strncpy( naev_cachePath, sizeof( naev_cachePath ), prefpath );
195 SDL_free( prefpath );
196#else
197#error "Feature needs implementation on this Operating System for Naev to work."
198#endif
199 }
200
201 return naev_cachePath;
202}
203
204#if __WIN32__
205#define MKDIR !CreateDirectory( opath, NULL )
206static int mkpath( const char *path )
207#elif HAS_POSIX
208#define MKDIR mkdir( opath, mode )
209static int mkpath( const char *path, mode_t mode )
210#elif SDL_VERSION_ATLEAST( 3, 0, 0 )
211#define MKDIR SDL_CreateDirectory( opath )
212#else
213#error "Feature needs implementation on this Operating System for Naev to work."
214#endif
215{
216 char opath[PATH_MAX];
217 char *p;
218 size_t len;
219 int ret;
220
221 if ( path == NULL )
222 return 0;
223
224 strncpy( opath, path, sizeof( opath ) - 1 );
225 opath[sizeof( opath ) - 1] = '\0';
226 len = strlen( opath );
227
228 p = &opath[len - 1];
229 if ( nfile_isSeparator( p[0] ) ) {
230 p[0] = '\0';
231 p--;
232 }
233
234 // Traverse up until we find a directory that exists.
235 for ( ; p >= opath; p-- ) {
236 if ( nfile_isSeparator( p[0] ) ) {
237 p[0] = '\0';
238 if ( nfile_dirExists( opath ) ) {
239 p[0] = '/';
240 break;
241 }
242 p[0] = '/';
243 }
244 }
245 // This skips the directory that exists, or puts us
246 // back at the start if the loop fell through.
247 p++;
248
249 // Traverse down, creating directories.
250 for ( ; p[0] != '\0'; p++ ) {
251 if ( nfile_isSeparator( p[0] ) ) {
252 p[0] = '\0';
253 ret = MKDIR;
254 if ( ret && ( errno != EEXIST ) )
255 return ret;
256 p[0] = '/';
257 }
258 }
259
260 // Create the final directory.
261 if ( !nfile_dirExists( opath ) ) {
262 ret = MKDIR;
263 if ( ret )
264 return ret;
265 }
266
267 return 0;
268}
269#undef MKDIR
270
277int nfile_dirMakeExist( const char *path )
278{
279 if ( path == NULL )
280 return -1;
281
282 /* Check if it exists. */
283 if ( nfile_dirExists( path ) )
284 return 0;
285
286#if HAS_POSIX
287 if ( mkpath( path, S_IRWXU | S_IRWXG | S_IRWXO ) < 0 ) {
288#elif __WIN32__
289 if ( mkpath( path ) < 0 ) {
290#elif SDL_VERSION_ATLEAST( 3, 0, 0 )
291 if ( SDL_CreateDirectory( path ) < 0 ) {
292#else
293#error "Feature needs implementation on this Operating System for Naev to work."
294#endif
295 WARN( _( "Dir '%s' does not exist and unable to create: %s" ), path,
296 strerror( errno ) );
297 return -1;
298 }
299
300 return 0;
301}
302
309int nfile_dirExists( const char *path )
310{
311 DIR *d;
312
313 if ( path == NULL )
314 return -1;
315
316 d = opendir( path );
317 if ( d == NULL )
318 return 0;
319 closedir( d );
320 return 1;
321}
322
329int nfile_fileExists( const char *path )
330{
331 struct stat buf;
332
333 if ( path == NULL )
334 return -1;
335
336 if ( stat( path, &buf ) == 0 ) /* stat worked, file must exist */
337 return 1;
338
339 /* ANSI C89 compliant method here for reference. Not as precise as stat.
340 FILE *f = fopen(file, "rb");
341 if (f != NULL) {
342 fclose(f);
343 return 1;
344 }
345 */
346
347 return 0;
348}
349
356int nfile_backupIfExists( const char *path )
357{
358 char backup[PATH_MAX];
359
360 if ( path == NULL )
361 return -1;
362
363 if ( !nfile_fileExists( path ) )
364 return 0;
365
366 snprintf( backup, sizeof( backup ), "%s.backup", path );
367
368 return nfile_copyIfExists( path, backup );
369}
370
378int nfile_copyIfExists( const char *file1, const char *file2 )
379{
380 FILE *f_in, *f_out;
381 char buf[8 * 1024];
382 size_t lr, lw;
383
384 if ( file1 == NULL )
385 return -1;
386
387 /* Check if input file exists */
388 if ( !nfile_fileExists( file1 ) )
389 return 0;
390
391 /* Open files. */
392 f_in = fopen( file1, "rb" );
393 f_out = fopen( file2, "wb" );
394 if ( ( f_in == NULL ) || ( f_out == NULL ) ) {
395 WARN( _( "Failure to copy '%s' to '%s': %s" ), file1, file2,
396 strerror( errno ) );
397 if ( f_in != NULL )
398 fclose( f_in );
399 if ( f_out != NULL )
400 fclose( f_out );
401 return -1;
402 }
403
404 /* Copy data over. */
405 do {
406 lr = fread( buf, 1, sizeof( buf ), f_in );
407 if ( ferror( f_in ) )
408 goto err;
409 else if ( !lr ) {
410 if ( feof( f_in ) )
411 break;
412 goto err;
413 }
414
415 lw = fwrite( buf, 1, lr, f_out );
416 if ( ferror( f_out ) || ( lr != lw ) )
417 goto err;
418 } while ( lr > 0 );
419
420 /* Close files. */
421 fclose( f_in );
422 fclose( f_out );
423
424 return 0;
425
426err:
427 WARN( _( "Failure to copy '%s' to '%s': %s" ), file1, file2,
428 strerror( errno ) );
429 fclose( f_in );
430 fclose( f_out );
431
432 return -1;
433}
434
442char *nfile_readFile( size_t *filesize, const char *path )
443{
444 int n;
445 char *buf;
446 FILE *file;
447 int len;
448 size_t pos;
449 struct stat path_stat;
450
451 if ( path == NULL ) {
452 *filesize = 0;
453 return NULL;
454 }
455
456 if ( stat( path, &path_stat ) ) {
457 WARN( _( "Error occurred while opening '%s': %s" ), path,
458 strerror( errno ) );
459 *filesize = 0;
460 return NULL;
461 }
462
463 if ( !S_ISREG( path_stat.st_mode ) ) {
464 WARN( _( "Error occurred while opening '%s': It is not a regular file" ),
465 path );
466 *filesize = 0;
467 return NULL;
468 }
469
470 /* Open file. */
471 file = fopen( path, "rb" );
472 if ( file == NULL ) {
473 WARN( _( "Error occurred while opening '%s': %s" ), path,
474 strerror( errno ) );
475 *filesize = 0;
476 return NULL;
477 }
478
479 /* Get file size. */
480 if ( fseek( file, 0L, SEEK_END ) == -1 ) {
481 WARN( _( "Error occurred while seeking '%s': %s" ), path,
482 strerror( errno ) );
483 fclose( file );
484 *filesize = 0;
485 return NULL;
486 }
487 len = ftell( file );
488 if ( fseek( file, 0L, SEEK_SET ) == -1 ) {
489 WARN( _( "Error occurred while seeking '%s': %s" ), path,
490 strerror( errno ) );
491 fclose( file );
492 *filesize = 0;
493 return NULL;
494 }
495
496 /* Allocate buffer. */
497 buf = malloc( len + 1 );
498 if ( buf == NULL ) {
499 WARN( _( "Out of Memory" ) );
500 fclose( file );
501 *filesize = 0;
502 return NULL;
503 }
504 buf[len] = '\0';
505
506 /* Read the file. */
507 n = 0;
508 while ( n < len ) {
509 pos = fread( &buf[n], 1, len - n, file );
510 if ( pos <= 0 ) {
511 WARN( _( "Error occurred while reading '%s': %s" ), path,
512 strerror( errno ) );
513 fclose( file );
514 *filesize = 0;
515 free( buf );
516 return NULL;
517 }
518 n += pos;
519 }
520
521 /* Close the file. */
522 fclose( file );
523
524 *filesize = len;
525 return buf;
526}
527
533int nfile_touch( const char *path )
534{
535 FILE *f;
536
537 if ( path == NULL )
538 return -1;
539
540 /* Try to open the file, C89 compliant, but not as precise as stat. */
541 f = fopen( path, "a+b" );
542 if ( f == NULL ) {
543 WARN( _( "Unable to touch file '%s': %s" ), path, strerror( errno ) );
544 return -1;
545 }
546
547 fclose( f );
548 return 0;
549}
550
559int nfile_writeFile( const char *data, size_t len, const char *path )
560{
561 size_t n;
562 FILE *file;
563 size_t pos;
564
565 if ( path == NULL )
566 return -1;
567
568 /* Open file. */
569 file = fopen( path, "wb" );
570 if ( file == NULL ) {
571 WARN( _( "Error occurred while opening '%s': %s" ), path,
572 strerror( errno ) );
573 return -1;
574 }
575
576 /* Write the file. */
577 n = 0;
578 while ( n < len ) {
579 pos = fwrite( &data[n], 1, len - n, file );
580 if ( pos <= 0 ) {
581 WARN( _( "Error occurred while writing '%s': %s" ), path,
582 strerror( errno ) );
583 fclose( file ); /* don't care about further errors */
584 return -1;
585 }
586 n += pos;
587 }
588
589 /* Close the file. */
590 if ( fclose( file ) == EOF ) {
591 WARN( _( "Error occurred while closing '%s': %s" ), path,
592 strerror( errno ) );
593 return -1;
594 }
595
596 return 0;
597}
598
605int nfile_isSeparator( uint32_t c )
606{
607 if ( c == '/' )
608 return 1;
609#if __WIN32__
610 else if ( c == '\\' )
611 return 1;
612#endif /* __WIN32__ */
613 return 0;
614}
615
616int _nfile_concatPaths( char buf[static 1], int maxLength,
617 const char path[static 1], ... )
618{
619 char *bufPos;
620 char *bufEnd;
621 const char *section;
622 va_list ap;
623
624 bufPos = buf;
625 bufEnd = buf + maxLength;
626 va_start( ap, path );
627 section = path;
628
629#if DEBUGGING
630 if ( section == NULL )
631 WARN( _( "First argument to nfile_concatPaths was NULL. This is probably "
632 "an error." ) );
633#endif /* DEBUGGING */
634
635 do {
636 // End of arg list?
637 if ( section == NULL )
638 break;
639
640 if ( bufPos > buf ) {
641 // Make sure there's a path seperator.
642 if ( bufPos[-1] != '/' ) {
643 bufPos[0] = '/';
644 bufPos += 1;
645 }
646 // But not too many path seperators.
647 if ( *section == '/' )
648 section += 1;
649 }
650
651 // Copy this section
652 bufPos = memccpy( bufPos, section, '\0', bufEnd - bufPos );
653 if ( bufPos == NULL )
654 break;
655
656 // Next path section
657 section = va_arg( ap, char * );
658 } while (
659 bufPos-- <
660 bufEnd ); // Rewind after compare so we're pointing at the NULL character.
661 va_end( ap );
662
663 // Did we run out of space?
664 if ( section != NULL )
665 return -1;
666
667 return bufPos - buf;
668}
669
676int nfile_simplifyPath( char path[static 1] )
677{
678 char **dirnames = array_create( char * );
679 char *saveptr = NULL;
680 size_t n = strlen( path );
681 int absolute = ( path[0] == '/' );
682 char *token = SDL_strtokr( path, "/", &saveptr );
683
684 while ( token != NULL ) {
685 /* Skip noop. */
686 if ( ( strcmp( token, "" ) == 0 ) || ( strcmp( token, "." ) == 0 ) ) {
687 token = SDL_strtokr( NULL, "/", &saveptr );
688 continue;
689 }
690
691 /* Go up if ".." */
692 if ( strcmp( token, ".." ) == 0 ) {
693 int dn = array_size( dirnames );
694 if ( dn > 0 ) {
695 free( dirnames[dn - 1] );
696 array_erase( &dirnames, &dirnames[dn - 1], &dirnames[dn] );
697 }
698 } else {
699 array_push_back( &dirnames, strdup( token ) );
700 }
701
702 /* On to the next one. */
703 token = SDL_strtokr( NULL, "/", &saveptr );
704 }
705
706 /* If nothing, we're empty. */
707 if ( array_size( dirnames ) <= 0 ) {
708 array_free( dirnames );
709 path[0] = '\0';
710 return 0;
711 }
712
713 /* Build back the path, assume 'path' is smaller than simplified one. */
714 size_t s = 0;
715 for ( int i = 0; i < array_size( dirnames ); i++ ) {
716 char *ds = dirnames[i];
717 if ( ( absolute || ( s > 0 ) ) && ( s < n ) )
718 path[s++] = '/';
719 for ( size_t j = 0; j < strlen( ds ); j++ )
720 if ( s < n )
721 path[s++] = ds[j];
722 free( ds );
723 }
724 path[s] = '\0';
725 array_free( dirnames );
726 return 0;
727}
728
729#if !SDL_VERSION_ATLEAST( 3, 0, 0 )
730#include "nfd.h"
731void SDL_ShowOpenFileDialog( SDL_DialogFileCallback callback, void *userdata,
732 SDL_Window *window,
733 const SDL_DialogFileFilter *filters,
734 const char *default_location, SDL_bool allow_many )
735{
736 (void)allow_many;
737 (void)window;
738 char *filelist[2] = { NULL, NULL };
739
740 int n;
741 for ( n = 0; filters[n].name != NULL; n++ )
742 ;
743
744 nfdfilteritem_t *fitem = calloc( n, sizeof( nfdfilteritem_t ) );
745 for ( int i = 0; i < n; i++ ) {
746 fitem[i].name = filters[i].name;
747 fitem[i].spec = filters[i].pattern;
748 }
749
750 NFD_Init();
751
752 nfdchar_t *outPath;
753#if __WIN32__
754 (void)default_location;
755 nfdresult_t result = NFD_OpenDialog( &outPath, fitem, n, NULL );
756#else /* __WIN32__ */
757 nfdresult_t result = NFD_OpenDialog( &outPath, fitem, n, default_location );
758#endif /* __WIN32__ */
759 switch ( result ) {
760 case NFD_OKAY:
761 filelist[0] = outPath;
762 callback( userdata, (const char **)filelist, 0 );
763 NFD_FreePath( outPath );
764 break;
765
766 case NFD_CANCEL:
767 callback( userdata, (const char **)filelist, 0 );
768 break;
769
770 default:
771 SDL_SetError( "%s", NFD_GetError() );
772 callback( userdata, NULL, -1 );
773 break;
774 }
775
776 NFD_Quit();
777
778 free( fitem );
779}
780
781void SDL_ShowOpenFolderDialog( SDL_DialogFileCallback callback, void *userdata,
782 SDL_Window *window, const char *default_location,
783 SDL_bool allow_many )
784{
785 (void)allow_many;
786 (void)window;
787 char *filelist[2] = { NULL, NULL };
788
789 NFD_Init();
790
791 nfdchar_t *outPath;
792#if __WIN32__
793 (void)default_location;
794 nfdresult_t result = NFD_PickFolder( &outPath, NULL );
795#else /* __WIN32__ */
796 nfdresult_t result = NFD_PickFolder( &outPath, default_location );
797#endif /* __WIN32__ */
798 switch ( result ) {
799 case NFD_OKAY:
800 filelist[0] = outPath;
801 callback( userdata, (const char **)filelist, 0 );
802 NFD_FreePath( outPath );
803 break;
804
805 case NFD_CANCEL:
806 callback( userdata, (const char **)filelist, 0 );
807 break;
808
809 default:
810 SDL_SetError( "%s", NFD_GetError() );
811 callback( userdata, NULL, -1 );
812 break;
813 }
814
815 NFD_Quit();
816}
817
818void SDL_ShowSaveFileDialog( SDL_DialogFileCallback callback, void *userdata,
819 SDL_Window *window,
820 const SDL_DialogFileFilter *filters,
821 const char *default_location )
822{
823 (void)window;
824 char *filelist[2] = { NULL, NULL };
825 char *tmp, *bname, *dname;
826
827 int n;
828 for ( n = 0; filters[n].name != NULL; n++ )
829 ;
830
831 nfdfilteritem_t *fitem = calloc( n, sizeof( nfdfilteritem_t ) );
832 for ( int i = 0; i < n; i++ ) {
833 fitem[i].name = filters[i].name;
834 fitem[i].spec = filters[i].pattern;
835 }
836
837 /* Get paths. */
838 tmp = strdup( default_location );
839 dname = strdup( dirname( tmp ) );
840 free( tmp );
841 tmp = strdup( default_location );
842 bname = strdup( basename( tmp ) );
843 free( tmp );
844
845 NFD_Init();
846
847 nfdchar_t *outPath;
848#if __WIN32__
849 nfdresult_t result = NFD_SaveDialog( &outPath, fitem, n, NULL, bname );
850#else /* __WIN32__ */
851 nfdresult_t result = NFD_SaveDialog( &outPath, fitem, n, dname, bname );
852#endif /* __WIN32__ */
853 switch ( result ) {
854 case NFD_OKAY:
855 filelist[0] = outPath;
856 callback( userdata, (const char **)filelist, 0 );
857 NFD_FreePath( outPath );
858 break;
859
860 case NFD_CANCEL:
861 callback( userdata, (const char **)filelist, 0 );
862 break;
863
864 default:
865 SDL_SetError( "%s", NFD_GetError() );
866 callback( userdata, NULL, -1 );
867 break;
868 }
869
870 NFD_Quit();
871
872 free( dname );
873 free( bname );
874 free( fitem );
875}
876#endif /* ! SDL_VERSION_ATLEAST(3,0,0) */
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition array.h:148
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:134
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
#define PATH_MAX
Definition naev.h:57
int nfile_simplifyPath(char path[static 1])
Simplifies the path removing things like ".." or consecutive "/".
Definition nfile.c:676
int nfile_copyIfExists(const char *file1, const char *file2)
Copy a file, if it exists.
Definition nfile.c:378
const char * nfile_configPath(void)
Gets Naev's config path (for user preferences such as conf.lua)
Definition nfile.c:113
static char naev_configPath[PATH_MAX]
Definition nfile.c:107
int nfile_writeFile(const char *data, size_t len, const char *path)
Tries to write a file.
Definition nfile.c:559
static char naev_cachePath[PATH_MAX]
Definition nfile.c:156
char * nfile_readFile(size_t *filesize, const char *path)
Tries to read a file.
Definition nfile.c:442
int nfile_dirMakeExist(const char *path)
Creates a directory if it doesn't exist.
Definition nfile.c:277
int nfile_backupIfExists(const char *path)
Backup a file, if it exists.
Definition nfile.c:356
const char * nfile_cachePath(void)
Gets Naev's cache path (for cached data such as generated textures)
Definition nfile.c:162
int nfile_fileExists(const char *path)
Checks to see if a file exists.
Definition nfile.c:329
int nfile_touch(const char *path)
Tries to create the file if it doesn't exist.
Definition nfile.c:533
int nfile_isSeparator(uint32_t c)
Checks to see if a character is used to separate files in a path.
Definition nfile.c:605
int nfile_dirExists(const char *path)
Checks to see if a directory exists.
Definition nfile.c:309
static const double c[]
Definition rng.c:256
static const double d[]
Definition rng.c:263