naev 0.12.5
ndata.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
13#include <stdlib.h>
14#if __WIN32__
15#include <windows.h>
16#endif /* __WIN32__ */
17
18#include "SDL_stdinc.h"
19#include "physfs.h"
20
21#include "naev.h"
23
24#include "ndata.h"
25
26#include "array.h"
27#include "conf.h"
28#include "env.h"
29#if __MACOSX__
30#include "glue_macos.h"
31#endif /* __MACOSX__ */
32#include "log.h"
33#include "nfile.h"
34#include "nstring.h"
35#include "plugin.h"
36
37/*
38 * Prototypes.
39 */
40static void ndata_testVersion( void );
41static int ndata_found( void );
42static int ndata_enumerateCallback( void *data, const char *origdir,
43 const char *fname );
44
48const char *ndata_primaryPath( void )
49{
50 static char *primarypath = NULL;
51 if ( primarypath == NULL ) {
52 char **search_path = PHYSFS_getSearchPath();
53 int n = 0;
54 for ( char **p = search_path; *p != NULL; p++ )
55 n++;
56 for ( int i = n - 1; i >= 0; i-- ) {
57 if ( nfile_dirExists( search_path[i] ) ) {
58 primarypath = strdup( search_path[i] );
59 break;
60 }
61 }
62 PHYSFS_freeList( search_path );
63 }
64 return primarypath;
65}
66
70static int ndata_found( void )
71{
72 /* Verify that we can find VERSION and start.xml.
73 * This is arbitrary, but these are among the hard dependencies to
74 * self-identify and start.
75 */
76 return PHYSFS_exists( "VERSION" ) && PHYSFS_exists( START_DATA_PATH );
77}
78
82static void ndata_testVersion( void )
83{
84 size_t size;
85 char *buf, cbuf[PATH_MAX];
86 int diff;
87
88 if ( !ndata_found() ) {
89 char err[STRMAX];
90 snprintf( err, sizeof( err ),
91 _( "Unable to find game data. You may need to install, specify "
92 "a datapath, or run using %s (if developing)." ),
93 "naev.py" );
94#if SDL_VERSION_ATLEAST( 3, 0, 0 )
95 SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR,
96 _( "Naev Critical Error" ), err,
97 gl_screen.window );
98#endif /* SDL_VERSION_ATLEAST( 3, 0, 0 ) */
99 ERR( "%s", err );
100 }
101
102 /* Parse version. */
103 buf = ndata_read( "VERSION", &size );
104 for ( size_t i = 0; i < MIN( size, PATH_MAX - 1 ); i++ )
105 cbuf[i] = buf[i];
106 cbuf[MIN( size, PATH_MAX - 1 )] = '\0';
107 diff = naev_versionCompare( cbuf );
108 if ( diff != 0 ) {
109 WARN( _( "ndata version inconsistency with this version of Naev!" ) );
110 WARN( _( "Expected ndata version %s got %s." ), naev_version( 0 ), cbuf );
111 if ( ABS( diff ) > 2 ) {
112 char err[STRMAX];
113 snprintf( err, sizeof( err ),
114 _( "Please get a compatible ndata version!" ) );
115#if SDL_VERSION_ATLEAST( 3, 0, 0 )
116 SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR,
117 _( "Naev Critical Error" ), err,
118 gl_screen.window );
119#endif /* SDL_VERSION_ATLEAST( 3, 0, 0 ) */
120 ERR( "%s", err );
121 }
122 if ( ABS( diff ) > 1 )
123 WARN( _( "Naev will probably crash now as the versions are probably "
124 "not compatible." ) );
125 }
126 free( buf );
127}
128
133{
134 /* Global override is set. */
135 if ( conf.datapath ) {
136 PHYSFS_setWriteDir( conf.datapath );
137 return;
138 }
139#if __MACOSX__
140 /* For historical reasons predating physfs adoption, this case is different.
141 */
142 PHYSFS_setWriteDir( PHYSFS_getPrefDir( ".", "org.naev.Naev" ) );
143#else
144 PHYSFS_setWriteDir( PHYSFS_getPrefDir( ".", "naev" ) );
145#endif /* __MACOSX__ */
146 if ( PHYSFS_getWriteDir() == NULL ) {
147 WARN( _( "Cannot determine data path, using current directory." ) );
148 PHYSFS_setWriteDir( "./naev/" );
149 }
150}
151
156{
157 char buf[PATH_MAX];
158
159 if ( conf.ndata != NULL && PHYSFS_mount( conf.ndata, NULL, 1 ) )
160 LOG( _( "Added datapath from conf.lua file: %s" ), conf.ndata );
161
162#if __MACOSX__
163 if ( !ndata_found() && macos_isBundle() &&
164 macos_resourcesPath( buf, PATH_MAX - 4 ) >= 0 &&
165 strncat( buf, "/dat", 4 ) ) {
166 LOG( _( "Trying default datapath: %s" ), buf );
167 PHYSFS_mount( buf, NULL, 1 );
168 }
169#endif /* __MACOSX__ */
170
171#if __LINUX__
172 if ( !ndata_found() && env.isAppImage &&
173 nfile_concatPaths( buf, PATH_MAX, env.appdir, PKGDATADIR, "dat" ) >=
174 0 ) {
175 LOG( _( "Trying default datapath: %s" ), buf );
176 PHYSFS_mount( buf, NULL, 1 );
177 }
178#endif /*__LINUX__ */
179
180 if ( !ndata_found() &&
181 nfile_concatPaths( buf, PATH_MAX, PKGDATADIR, "dat" ) >= 0 ) {
182 LOG( _( "Trying default datapath: %s" ), buf );
183 PHYSFS_mount( buf, NULL, 1 );
184 }
185
186 if ( !ndata_found() &&
187 nfile_concatPaths( buf, PATH_MAX, PHYSFS_getBaseDir(), "dat" ) >= 0 ) {
188 LOG( _( "Trying default datapath: %s" ), buf );
189 PHYSFS_mount( buf, NULL, 1 );
190 }
191
192 PHYSFS_mount( PHYSFS_getWriteDir(), NULL, 0 );
193
194 /* Load plugins I guess. */
195 plugin_init();
196
198}
199
207void *ndata_read( const char *path, size_t *filesize )
208{
209 char *buf;
210 PHYSFS_file *file;
211 PHYSFS_sint64 len, n;
212 PHYSFS_Stat path_stat;
213
214 if ( !PHYSFS_stat( path, &path_stat ) ) {
215 WARN( _( "Error occurred while opening '%s': %s" ), path,
216 _( PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
217 *filesize = 0;
218 return NULL;
219 }
220 if ( path_stat.filetype != PHYSFS_FILETYPE_REGULAR ) {
221 WARN( _( "Error occurred while opening '%s': It is not a regular file" ),
222 path );
223 *filesize = 0;
224 return NULL;
225 }
226
227 /* Open file. */
228 file = PHYSFS_openRead( path );
229 if ( file == NULL ) {
230 WARN( _( "Error occurred while opening '%s': %s" ), path,
231 _( PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
232 *filesize = 0;
233 return NULL;
234 }
235
236 /* Get file size. TODO: Don't assume this is always possible? */
237 len = PHYSFS_fileLength( file );
238 if ( len == -1 ) {
239 WARN( _( "Error occurred while seeking '%s': %s" ), path,
240 _( PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
241 PHYSFS_close( file );
242 *filesize = 0;
243 return NULL;
244 }
245
246 /* Allocate buffer. */
247 buf = malloc( len + 1 );
248 if ( buf == NULL ) {
249 WARN( _( "Out of Memory" ) );
250 PHYSFS_close( file );
251 *filesize = 0;
252 return NULL;
253 }
254 buf[len] = '\0';
255
256 /* Read the file. */
257 n = 0;
258 while ( n < len ) {
259 size_t pos = PHYSFS_readBytes( file, &buf[n], len - n );
260 if ( pos == 0 ) {
261 WARN( _( "Error occurred while reading '%s': %s" ), path,
262 _( PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
263 PHYSFS_close( file );
264 *filesize = 0;
265 free( buf );
266 return NULL;
267 }
268 n += pos;
269 }
270
271 /* Close the file. */
272 PHYSFS_close( file );
273
274 *filesize = len;
275 return buf;
276}
277
286char **ndata_listRecursive( const char *path )
287{
288 char **files = array_create( char * );
289 PHYSFS_enumerate( path, ndata_enumerateCallback, &files );
290 /* Ensure unique. PhysicsFS can enumerate a path twice if it's in multiple
291 * components of a union. */
292 qsort( files, array_size( files ), sizeof( char * ), strsort );
293 for ( int i = 0; i + 1 < array_size( files ); i++ )
294 if ( strcmp( files[i], files[i + 1] ) == 0 ) {
295 free( files[i] );
296 array_erase( &files, &files[i], &files[i + 1] );
297 i--; /* We're not done checking for dups of files[i]. */
298 }
299 return files;
300}
301
305static int ndata_enumerateCallback( void *data, const char *origdir,
306 const char *fname )
307{
308 char *path;
309 const char *fmt;
310 size_t dir_len;
311 PHYSFS_Stat stat;
312
313 dir_len = strlen( origdir );
314 fmt = dir_len && origdir[dir_len - 1] == '/' ? "%s%s" : "%s/%s";
315 SDL_asprintf( &path, fmt, origdir, fname );
316 if ( !PHYSFS_stat( path, &stat ) ) {
317 WARN( _( "PhysicsFS: Cannot stat %s: %s" ), path,
318 _( PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
319 free( path );
320 } else if ( stat.filetype == PHYSFS_FILETYPE_REGULAR )
321 array_push_back( (char ***)data, path );
322 else if ( stat.filetype == PHYSFS_FILETYPE_DIRECTORY ) {
323 PHYSFS_enumerate( path, ndata_enumerateCallback, data );
324 free( path );
325 } else
326 free( path );
327 return PHYSFS_ENUM_OK;
328}
329
336int ndata_backupIfExists( const char *path )
337{
338 char backup[PATH_MAX];
339
340 if ( path == NULL )
341 return -1;
342
343 if ( !PHYSFS_exists( path ) )
344 return 0;
345
346 snprintf( backup, sizeof( backup ), "%s.backup", path );
347
348 return ndata_copyIfExists( path, backup );
349}
350
358int ndata_copyIfExists( const char *file1, const char *file2 )
359{
360 PHYSFS_File *f_in, *f_out;
361 char buf[8 * 1024];
362 PHYSFS_sint64 lr, lw;
363
364 if ( file1 == NULL )
365 return -1;
366
367 /* Check if input file exists */
368 if ( !PHYSFS_exists( file1 ) )
369 return 0;
370
371 /* Open files. */
372 f_in = PHYSFS_openRead( file1 );
373 f_out = PHYSFS_openWrite( file2 );
374 if ( ( f_in == NULL ) || ( f_out == NULL ) ) {
375 WARN( _( "Failure to copy '%s' to '%s': %s" ), file1, file2,
376 _( PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
377 if ( f_in != NULL )
378 PHYSFS_close( f_in );
379 return -1;
380 }
381
382 /* Copy data over. */
383 do {
384 lr = PHYSFS_readBytes( f_in, buf, sizeof( buf ) );
385 if ( lr == -1 )
386 goto err;
387 else if ( !lr ) {
388 if ( PHYSFS_eof( f_in ) )
389 break;
390 goto err;
391 }
392
393 lw = PHYSFS_writeBytes( f_out, buf, lr );
394 if ( lr != lw )
395 goto err;
396 } while ( lr > 0 );
397
398 /* Close files. */
399 PHYSFS_close( f_in );
400 PHYSFS_close( f_out );
401
402 return 0;
403
404err:
405 WARN( _( "Failure to copy '%s' to '%s': %s" ), file1, file2,
406 _( PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
407 PHYSFS_close( f_in );
408 PHYSFS_close( f_out );
409
410 return -1;
411}
412
420int ndata_matchExt( const char *path, const char *ext )
421{
422 int i;
423 /* Find the dot. */
424 for ( i = strlen( path ) - 1; i > 0; i-- )
425 if ( path[i] == '.' )
426 break;
427 if ( i <= 0 )
428 return 0;
429 return strcmp( &path[i + 1], ext ) == 0;
430}
431
442int ndata_getPathDefault( char *path, int len, const char *default_path,
443 const char *filename )
444{
445 PHYSFS_Stat path_stat;
446 snprintf( path, len, "%s%s", default_path, filename );
447 if ( PHYSFS_stat( path, &path_stat ) &&
448 ( path_stat.filetype == PHYSFS_FILETYPE_REGULAR ) )
449 return 1;
450 snprintf( path, len, "%s", filename );
451 if ( PHYSFS_stat( path, &path_stat ) &&
452 ( path_stat.filetype == PHYSFS_FILETYPE_REGULAR ) )
453 return 1;
454 return 0;
455}
Provides macros to work with dynamic arrays.
#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
int macos_isBundle(void)
Determine if we're running from inside an app bundle.
Definition glue_macos.m:28
int naev_versionCompare(const char *version)
Compares the version against the current naev version.
Definition naev.c:1151
Header file with generic functions and naev-specifics.
const char * naev_version(int long_version)
Returns the version in a human readable string.
#define MIN(x, y)
Definition naev.h:39
#define ABS(x)
Definition naev.h:32
#define PATH_MAX
Definition naev.h:57
int ndata_backupIfExists(const char *path)
Backup a file, if it exists.
Definition ndata.c:336
void ndata_setupReadDirs(void)
Sets up the PhysicsFS search path.
Definition ndata.c:155
static void ndata_testVersion(void)
Test version to see if it matches.
Definition ndata.c:82
int ndata_copyIfExists(const char *file1, const char *file2)
Copy a file, if it exists.
Definition ndata.c:358
void ndata_setupWriteDir(void)
Gets Naev's data path (for user data such as saves and screenshots)
Definition ndata.c:132
static int ndata_found(void)
Checks to see if the physfs search path is enough to find game data.
Definition ndata.c:70
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition ndata.c:207
int ndata_matchExt(const char *path, const char *ext)
Sees if a file matches an extension.
Definition ndata.c:420
static int ndata_enumerateCallback(void *data, const char *origdir, const char *fname)
The PHYSFS_EnumerateCallback for ndata_listRecursive.
Definition ndata.c:305
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
Definition ndata.c:286
const char * ndata_primaryPath(void)
Gets the primary path for where the data is.
Definition ndata.c:48
int ndata_getPathDefault(char *path, int len, const char *default_path, const char *filename)
Tries to see if a file is in a default path before seeing if it is an absolute path.
Definition ndata.c:442
int nfile_dirExists(const char *path)
Checks to see if a directory exists.
Definition nfile.c:309
int strsort(const void *p1, const void *p2)
Sort function for sorting strings with qsort().
Definition nstring.c:83
glInfo gl_screen
Definition opengl.c:47
int plugin_init(void)
Initialize and loads all the available plugins.
Definition plugin.c:208