naev 0.11.5
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 __MACOSX__
31#include "glue_macos.h"
32#endif /* __MACOSX__ */
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 __MACOSX__
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 /* __MACOSX__ */
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 __MACOSX__
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 /* __MACOSX__ */
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 PHYSFS_Stat path_stat;
160
161 if (!PHYSFS_stat( path, &path_stat )) {
162 WARN( _( "Error occurred while opening '%s': %s" ), path,
163 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
164 *filesize = 0;
165 return NULL;
166 }
167 if (path_stat.filetype != PHYSFS_FILETYPE_REGULAR) {
168 WARN( _( "Error occurred while opening '%s': It is not a regular file" ), path );
169 *filesize = 0;
170 return NULL;
171 }
172
173 /* Open file. */
174 file = PHYSFS_openRead( path );
175 if ( file == NULL ) {
176 WARN( _( "Error occurred while opening '%s': %s" ), path,
177 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
178 *filesize = 0;
179 return NULL;
180 }
181
182 /* Get file size. TODO: Don't assume this is always possible? */
183 len = PHYSFS_fileLength( file );
184 if ( len == -1 ) {
185 WARN( _( "Error occurred while seeking '%s': %s" ), path,
186 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
187 PHYSFS_close( file );
188 *filesize = 0;
189 return NULL;
190 }
191
192 /* Allocate buffer. */
193 buf = malloc( len+1 );
194 if (buf == NULL) {
195 WARN(_("Out of Memory"));
196 PHYSFS_close( file );
197 *filesize = 0;
198 return NULL;
199 }
200 buf[len] = '\0';
201
202 /* Read the file. */
203 n = 0;
204 while (n < len) {
205 size_t pos = PHYSFS_readBytes( file, &buf[ n ], len - n );
206 if (pos == 0) {
207 WARN( _( "Error occurred while reading '%s': %s" ), path,
208 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
209 PHYSFS_close( file );
210 *filesize = 0;
211 free(buf);
212 return NULL;
213 }
214 n += pos;
215 }
216
217 /* Close the file. */
218 PHYSFS_close(file);
219
220 *filesize = len;
221 return buf;
222}
223
231char **ndata_listRecursive( const char *path )
232{
233 char **files = array_create( char * );
234 PHYSFS_enumerate( path, ndata_enumerateCallback, &files );
235 /* Ensure unique. PhysicsFS can enumerate a path twice if it's in multiple components of a union. */
236 qsort( files, array_size(files), sizeof(char*), strsort );
237 for (int i=0; i+1<array_size(files); i++)
238 if (strcmp(files[i], files[i+1]) == 0) {
239 free( files[i] );
240 array_erase( &files, &files[i], &files[i+1] );
241 i--; /* We're not done checking for dups of files[i]. */
242 }
243 return files;
244}
245
249static int ndata_enumerateCallback( void* data, const char* origdir, const char* fname )
250{
251 char *path;
252 const char *fmt;
253 size_t dir_len;
254 PHYSFS_Stat stat;
255
256 dir_len = strlen( origdir );
257 fmt = dir_len && origdir[dir_len-1]=='/' ? "%s%s" : "%s/%s";
258 SDL_asprintf( &path, fmt, origdir, fname );
259 if (!PHYSFS_stat( path, &stat )) {
260 WARN( _("PhysicsFS: Cannot stat %s: %s"), path,
261 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
262 free( path );
263 }
264 else if (stat.filetype == PHYSFS_FILETYPE_REGULAR)
265 array_push_back( (char***)data, path );
266 else if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY ) {
267 PHYSFS_enumerate( path, ndata_enumerateCallback, data );
268 free( path );
269 }
270 else
271 free( path );
272 return PHYSFS_ENUM_OK;
273}
274
281int ndata_backupIfExists( const char *path )
282{
283 char backup[ PATH_MAX ];
284
285 if (path == NULL)
286 return -1;
287
288 if (!PHYSFS_exists( path ))
289 return 0;
290
291 snprintf(backup, sizeof(backup), "%s.backup", path);
292
293 return ndata_copyIfExists( path, backup );
294}
295
303int ndata_copyIfExists( const char* file1, const char* file2 )
304{
305 PHYSFS_File *f_in, *f_out;
306 char buf[ 8*1024 ];
307 PHYSFS_sint64 lr, lw;
308
309 if (file1 == NULL)
310 return -1;
311
312 /* Check if input file exists */
313 if (!PHYSFS_exists(file1))
314 return 0;
315
316 /* Open files. */
317 f_in = PHYSFS_openRead( file1 );
318 f_out = PHYSFS_openWrite( file2 );
319 if ((f_in==NULL) || (f_out==NULL)) {
320 WARN( _("Failure to copy '%s' to '%s': %s"), file1, file2,
321 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
322 if (f_in!=NULL)
323 PHYSFS_close(f_in);
324 return -1;
325 }
326
327 /* Copy data over. */
328 do {
329 lr = PHYSFS_readBytes( f_in, buf, sizeof(buf) );
330 if (lr == -1)
331 goto err;
332 else if (!lr) {
333 if (PHYSFS_eof( f_in ))
334 break;
335 goto err;
336 }
337
338 lw = PHYSFS_writeBytes( f_out, buf, lr );
339 if (lr != lw)
340 goto err;
341 } while (lr > 0);
342
343 /* Close files. */
344 PHYSFS_close( f_in );
345 PHYSFS_close( f_out );
346
347 return 0;
348
349err:
350 WARN( _("Failure to copy '%s' to '%s': %s"), file1, file2,
351 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
352 PHYSFS_close( f_in );
353 PHYSFS_close( f_out );
354
355 return -1;
356}
357
365int ndata_matchExt( const char *path, const char *ext )
366{
367 int i;
368 /* Find the dot. */
369 for (i=strlen(path)-1; i>0; i--)
370 if (path[i] == '.')
371 break;
372 if (i<=0)
373 return 0;
374 return strcmp( &path[i+1], ext )==0;
375}
376
386int ndata_getPathDefault( char *path, int len, const char *default_path, const char *filename )
387{
388 PHYSFS_Stat path_stat;
389 snprintf( path, len, "%s%s", default_path, filename );
390 if (PHYSFS_stat( path, &path_stat ) && (path_stat.filetype == PHYSFS_FILETYPE_REGULAR))
391 return 1;
392 snprintf( path, len, "%s", filename );
393 if (PHYSFS_stat( path, &path_stat ) && (path_stat.filetype == PHYSFS_FILETYPE_REGULAR))
394 return 1;
395 return 0;
396}
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 naev_versionCompare(const char *version)
Compares the version against the current naev version.
Definition naev.c:1120
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: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:281
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:303
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:365
static int ndata_enumerateCallback(void *data, const char *origdir, const char *fname)
The PHYSFS_EnumerateCallback for ndata_listRecursive.
Definition ndata.c:249
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
Definition ndata.c:231
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:386
int strsort(const void *p1, const void *p2)
Sort function for sorting strings with qsort().
Definition nstring.c:81
int plugin_init(void)
Initialize and loads all the available plugins.
Definition plugin.c:115
char * datapath
Definition conf.h:76
char * ndata
Definition conf.h:75