naev 0.10.4
ndata.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
12#include <limits.h>
13#include <stdarg.h>
14#include <stdlib.h>
15#if WIN32
16#include <windows.h>
17#endif /* WIN32 */
18
19#include "physfs.h"
20#include "SDL.h"
21
22#include "naev.h"
25#include "ndata.h"
26
27#include "array.h"
28#include "conf.h"
29#include "env.h"
30#if MACOS
31#include "glue_macos.h"
32#endif /* MACOS */
33#include "log.h"
34#include "nfile.h"
35#include "nstring.h"
36#include "plugin.h"
37
38/*
39 * Prototypes.
40 */
41static void ndata_testVersion (void);
42static int ndata_found (void);
43static int ndata_enumerateCallback( void* data, const char* origdir, const char* fname );
44
48static int ndata_found( void )
49{
50 /* Verify that we can find VERSION and start.xml.
51 * This is arbitrary, but these are among the hard dependencies to self-identify and start.
52 */
53 return PHYSFS_exists( "VERSION" ) && PHYSFS_exists( START_DATA_PATH );
54}
55
59static void ndata_testVersion (void)
60{
61 size_t size;
62 char *buf, cbuf[PATH_MAX];
63 int diff;
64
65 if (!ndata_found())
66 ERR( _("Unable to find game data. You may need to install, specify a datapath, or run using naev.sh (if developing).") );
67
68 /* Parse version. */
69 buf = ndata_read( "VERSION", &size );
70 for (size_t i=0; i<MIN(size,PATH_MAX-1); i++)
71 cbuf[i] = buf[i];
72 cbuf[MIN(size-1,PATH_MAX-1)] = '\0';
73 diff = naev_versionCompare( cbuf );
74 if (diff != 0) {
75 WARN( _("ndata version inconsistency with this version of Naev!") );
76 WARN( _("Expected ndata version %s got %s."), naev_version( 0 ), cbuf );
77 if (ABS(diff) > 2)
78 ERR( _("Please get a compatible ndata version!") );
79 if (ABS(diff) > 1)
80 WARN( _("Naev will probably crash now as the versions are probably not compatible.") );
81 }
82 free( buf );
83}
84
89{
90 /* Global override is set. */
91 if (conf.datapath) {
92 PHYSFS_setWriteDir( conf.datapath );
93 return;
94 }
95#if MACOS
96 /* For historical reasons predating physfs adoption, this case is different. */
97 PHYSFS_setWriteDir( PHYSFS_getPrefDir( ".", "org.naev.Naev" ) );
98#else
99 PHYSFS_setWriteDir( PHYSFS_getPrefDir( ".", "naev" ) );
100#endif /* MACOS */
101 if (PHYSFS_getWriteDir() == NULL) {
102 WARN(_("Cannot determine data path, using current directory."));
103 PHYSFS_setWriteDir( "./naev/" );
104 }
105}
106
111{
112 char buf[ PATH_MAX ];
113
114 if ( conf.ndata != NULL && PHYSFS_mount( conf.ndata, NULL, 1 ) )
115 LOG(_("Added datapath from conf.lua file: %s"), conf.ndata);
116
117#if MACOS
118 if ( !ndata_found() && macos_isBundle() && macos_resourcesPath( buf, PATH_MAX-4 ) >= 0 && strncat( buf, "/dat", 4 ) ) {
119 LOG(_("Trying default datapath: %s"), buf);
120 PHYSFS_mount( buf, NULL, 1 );
121 }
122#endif /* MACOS */
123
124 if ( !ndata_found() && env.isAppImage && nfile_concatPaths( buf, PATH_MAX, env.appdir, PKGDATADIR, "dat" ) >= 0 ) {
125 LOG(_("Trying default datapath: %s"), buf);
126 PHYSFS_mount( buf, NULL, 1 );
127 }
128
129 if (!ndata_found() && nfile_concatPaths( buf, PATH_MAX, PKGDATADIR, "dat" ) >= 0) {
130 LOG(_("Trying default datapath: %s"), buf);
131 PHYSFS_mount( buf, NULL, 1 );
132 }
133
134 if (!ndata_found() && nfile_concatPaths( buf, PATH_MAX, PHYSFS_getBaseDir(), "dat" ) >= 0) {
135 LOG(_("Trying default datapath: %s"), buf);
136 PHYSFS_mount( buf, NULL, 1 );
137 }
138
139 PHYSFS_mount( PHYSFS_getWriteDir(), NULL, 0 );
140
141 /* Load plugins I guess. */
142 plugin_init();
143
145}
146
154void* ndata_read( const char* path, size_t *filesize )
155{
156 char *buf;
157 PHYSFS_file *file;
158 PHYSFS_sint64 len, n;
159 size_t pos;
160 PHYSFS_Stat path_stat;
161
162 if (!PHYSFS_stat( path, &path_stat )) {
163 WARN( _( "Error occurred while opening '%s': %s" ), path,
164 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
165 *filesize = 0;
166 return NULL;
167 }
168 if (path_stat.filetype != PHYSFS_FILETYPE_REGULAR) {
169 WARN( _( "Error occurred while opening '%s': It is not a regular file" ), path );
170 *filesize = 0;
171 return NULL;
172 }
173
174 /* Open file. */
175 file = PHYSFS_openRead( path );
176 if ( file == NULL ) {
177 WARN( _( "Error occurred while opening '%s': %s" ), path,
178 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
179 *filesize = 0;
180 return NULL;
181 }
182
183 /* Get file size. TODO: Don't assume this is always possible? */
184 len = PHYSFS_fileLength( file );
185 if ( len == -1 ) {
186 WARN( _( "Error occurred while seeking '%s': %s" ), path,
187 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
188 PHYSFS_close( file );
189 *filesize = 0;
190 return NULL;
191 }
192
193 /* Allocate buffer. */
194 buf = malloc( len+1 );
195 if (buf == NULL) {
196 WARN(_("Out of Memory"));
197 PHYSFS_close( file );
198 *filesize = 0;
199 return NULL;
200 }
201 buf[len] = '\0';
202
203 /* Read the file. */
204 n = 0;
205 while ( n < len ) {
206 pos = PHYSFS_readBytes( file, &buf[ n ], len - n );
207 if ( pos <= 0 ) {
208 WARN( _( "Error occurred while reading '%s': %s" ), path,
209 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
210 PHYSFS_close( file );
211 *filesize = 0;
212 free(buf);
213 return NULL;
214 }
215 n += pos;
216 }
217
218 /* Close the file. */
219 PHYSFS_close(file);
220
221 *filesize = len;
222 return buf;
223}
224
232char **ndata_listRecursive( const char *path )
233{
234 char **files = array_create( char * );
235 PHYSFS_enumerate( path, ndata_enumerateCallback, &files );
236 /* Ensure unique. PhysicsFS can enumerate a path twice if it's in multiple components of a union. */
237 qsort( files, array_size(files), sizeof(char*), strsort );
238 for (int i=0; i+1<array_size(files); i++)
239 if (strcmp(files[i], files[i+1]) == 0) {
240 free( files[i] );
241 array_erase( &files, &files[i], &files[i+1] );
242 i--; /* We're not done checking for dups of files[i]. */
243 }
244 return files;
245}
246
250static int ndata_enumerateCallback( void* data, const char* origdir, const char* fname )
251{
252 char *path;
253 const char *fmt;
254 size_t dir_len;
255 PHYSFS_Stat stat;
256
257 dir_len = strlen( origdir );
258 fmt = dir_len && origdir[dir_len-1]=='/' ? "%s%s" : "%s/%s";
259 asprintf( &path, fmt, origdir, fname );
260 if (!PHYSFS_stat( path, &stat )) {
261 WARN( _("PhysicsFS: Cannot stat %s: %s"), path,
262 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
263 free( path );
264 }
265 else if (stat.filetype == PHYSFS_FILETYPE_REGULAR)
266 array_push_back( (char***)data, path );
267 else if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY ) {
268 PHYSFS_enumerate( path, ndata_enumerateCallback, data );
269 free( path );
270 }
271 else
272 free( path );
273 return PHYSFS_ENUM_OK;
274}
275
282int ndata_backupIfExists( const char *path )
283{
284 char backup[ PATH_MAX ];
285
286 if (path == NULL)
287 return -1;
288
289 if (!PHYSFS_exists( path ))
290 return 0;
291
292 snprintf(backup, sizeof(backup), "%s.backup", path);
293
294 return ndata_copyIfExists( path, backup );
295}
296
304int ndata_copyIfExists( const char* file1, const char* file2 )
305{
306 PHYSFS_File *f_in, *f_out;
307 char buf[ 8*1024 ];
308 PHYSFS_sint64 lr, lw;
309
310 if (file1 == NULL)
311 return -1;
312
313 /* Check if input file exists */
314 if (!PHYSFS_exists(file1))
315 return 0;
316
317 /* Open files. */
318 f_in = PHYSFS_openRead( file1 );
319 f_out = PHYSFS_openWrite( file2 );
320 if ((f_in==NULL) || (f_out==NULL)) {
321 WARN( _("Failure to copy '%s' to '%s': %s"), file1, file2,
322 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
323 if (f_in!=NULL)
324 PHYSFS_close(f_in);
325 return -1;
326 }
327
328 /* Copy data over. */
329 do {
330 lr = PHYSFS_readBytes( f_in, buf, sizeof(buf) );
331 if (lr == -1)
332 goto err;
333 else if (!lr) {
334 if (PHYSFS_eof( f_in ))
335 break;
336 goto err;
337 }
338
339 lw = PHYSFS_writeBytes( f_out, buf, lr );
340 if (lr != lw)
341 goto err;
342 } while (lr > 0);
343
344 /* Close files. */
345 PHYSFS_close( f_in );
346 PHYSFS_close( f_out );
347
348 return 0;
349
350err:
351 WARN( _("Failure to copy '%s' to '%s': %s"), file1, file2,
352 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
353 PHYSFS_close( f_in );
354 PHYSFS_close( f_out );
355
356 return -1;
357}
358
366int ndata_matchExt( const char *path, const char *ext )
367{
368 int i;
369 /* Find the dot. */
370 for (i=strlen(path)-1; i>0; i--)
371 if (path[i] == '.')
372 break;
373 if (i<=0)
374 return 0;
375 return strcmp( &path[i+1], ext )==0;
376}
377
387int ndata_getPathDefault( char *path, int len, const char *default_path, const char *filename )
388{
389 PHYSFS_Stat path_stat;
390 snprintf( path, len, "%s%s", default_path, filename );
391 if (PHYSFS_stat( path, &path_stat ) && (path_stat.filetype == PHYSFS_FILETYPE_REGULAR))
392 return 1;
393 snprintf( path, len, "%s", filename );
394 if (PHYSFS_stat( path, &path_stat ) && (path_stat.filetype == PHYSFS_FILETYPE_REGULAR))
395 return 1;
396 return 0;
397}
Provides macros to work with dynamic arrays.
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition: array.h:140
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition: array.h:168
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition: array.h:129
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition: array.h:93
int macos_resourcesPath(char *res, size_t n)
Get the path to the bundle resources directory.
Definition: glue_macos.m:42
int macos_isBundle()
Determine if we're running from inside an app bundle.
Definition: glue_macos.m:33
int naev_versionCompare(const char *version)
Compares the version against the current naev version.
Definition: naev.c:1071
Header file with generic functions and naev-specifics.
const char * naev_version(int long_version)
Returns the version in a human readable string.
Definition: naev_version.c:25
#define MIN(x, y)
Definition: naev.h:40
#define ABS(x)
Definition: naev.h:36
#define PATH_MAX
Definition: naev.h:50
int ndata_backupIfExists(const char *path)
Backup a file, if it exists.
Definition: ndata.c:282
void ndata_setupReadDirs(void)
Sets up the PhysicsFS search path.
Definition: ndata.c:110
static void ndata_testVersion(void)
Test version to see if it matches.
Definition: ndata.c:59
int ndata_copyIfExists(const char *file1, const char *file2)
Copy a file, if it exists.
Definition: ndata.c:304
void ndata_setupWriteDir(void)
Gets Naev's data path (for user data such as saves and screenshots)
Definition: ndata.c:88
static int ndata_found(void)
Checks to see if the physfs search path is enough to find game data.
Definition: ndata.c:48
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition: ndata.c:154
int ndata_matchExt(const char *path, const char *ext)
Sees if a file matches an extension.
Definition: ndata.c:366
static int ndata_enumerateCallback(void *data, const char *origdir, const char *fname)
The PHYSFS_EnumerateCallback for ndata_listRecursive.
Definition: ndata.c:250
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
Definition: ndata.c:232
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:387
static char buf[NEWS_MAX_LENGTH]
Definition: news.c:45
int asprintf(char **strp, const char *fmt,...)
Like sprintf(), but it allocates a large-enough string and returns the pointer in the first argument....
Definition: nstring.c:161
int strsort(const void *p1, const void *p2)
Sort function for sorting strings with qsort().
Definition: nstring.c:108
int plugin_init(void)
Initialize and loads all the available plugins.
Definition: plugin.c:115
char * datapath
Definition: conf.h:77
char * ndata
Definition: conf.h:76