naev 0.12.6
load.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
9
11#include "physfs.h"
12
13#include "naev.h"
15
16#include "load.h"
17
18#include "array.h"
19#include "dialogue.h"
20#include "difficulty.h"
21#include "economy.h"
22#include "event.h"
23#include "gui.h"
24#include "hook.h"
25#include "land.h"
26#include "log.h"
27#include "menu.h"
28#include "mission.h"
29#include "ndata.h"
30#include "nstring.h"
31#include "nxml.h"
32#include "player.h"
33#include "plugin.h"
34#include "render.h"
35#include "save.h"
36#include "shiplog.h"
37#include "space.h"
38#include "start.h"
39#include "threadpool.h"
40#include "toolkit.h"
41#include "unidiff.h"
42#if HAVE_TRACY
43#include "ntracing.h"
44#endif /* HAVE_TRACY */
45
46#define LOAD_WIDTH 600
47#define LOAD_HEIGHT 530
48
49#define BUTTON_WIDTH 120
50#define BUTTON_HEIGHT 30
51
52typedef struct player_saves_s {
53 char *name;
54 nsave_t *saves;
56
57static player_saves_t *load_saves = NULL;
59 NULL;
60static int old_saves_detected = 0, player_warned = 0;
61static char *selected_player = NULL;
62extern int save_loaded;
63
64/*
65 * Prototypes.
66 */
67/* externs */
68/* player.c */
69extern Spob *
70player_load( xmlNodePtr parent );
71/* event.c */
72extern int events_loadActive( xmlNodePtr parent );
73/* news.c */
74extern int news_loadArticles( xmlNodePtr parent );
75/* nlua_var.c */
76extern int var_load( xmlNodePtr parent );
77/* faction.c */
78extern int pfaction_load( xmlNodePtr parent );
79/* hook.c */
80extern int hook_load( xmlNodePtr parent );
81/* economy.c */
82extern int
83economy_sysLoad( xmlNodePtr parent );
84/* unidiff.c */
85extern int diff_load( xmlNodePtr parent );
86/* static */
87static void load_menu_update( unsigned int wid, const char *str );
88static void load_menu_close( unsigned int wdw, const char *str );
89static void load_menu_load( unsigned int wdw, const char *str );
90static void load_menu_delete( unsigned int wdw, const char *str );
91static void load_menu_snapshots( unsigned int wdw, const char *str );
92static void load_snapshot_menu_update( unsigned int wid, const char *str );
93static void load_snapshot_menu_close( unsigned int wdw, const char *str );
94static void load_snapshot_menu_onClose( unsigned int wid, const char *str );
95static void load_snapshot_menu_load( unsigned int wdw, const char *str );
96static void load_snapshot_menu_delete( unsigned int wdw, const char *str );
97static void load_snapshot_menu_save( unsigned int wdw, const char *str );
98static void display_save_info( unsigned int wid, const nsave_t *ns );
99static void move_old_save( const char *path, const char *fname, const char *ext,
100 const char *new_name );
101static int load_load( nsave_t *save );
102static int load_gameInternalHook( void *data );
103static int load_enumerateCallback( void *data, const char *origdir,
104 const char *fname );
105static int load_enumerateCallbackPlayer( void *data, const char *origdir,
106 const char *fname );
107static int load_compatibilityTest( const nsave_t *ns );
108static const char *load_compatibilityString( const nsave_t *ns );
109static int has_plugin( const char *plugin );
110static SaveCompatibility load_compatibility( const nsave_t *ns );
111static int load_sortComparePlayersName( const void *p1, const void *p2 );
112static int load_sortComparePlayers( const void *p1, const void *p2 );
113static int load_sortCompareName( const void *p1, const void *p2 );
114static int load_sortCompare( const void *p1, const void *p2 );
115static xmlDocPtr load_xml_parsePhysFS( const char *filename );
116static void load_freeSave( nsave_t *ns );
117
123static int load_load( nsave_t *save )
124{
125 xmlDocPtr doc;
126 xmlNodePtr root, parent;
127
128 /* Load the XML. */
129 doc = load_xml_parsePhysFS( save->path );
130 if ( doc == NULL ) {
131 WARN( _( "Unable to parse save path '%s'." ), save->path );
132 return -1;
133 }
134 root = doc->xmlChildrenNode; /* base node */
135 if ( root == NULL ) {
136 WARN( _( "Unable to get child node of save '%s'." ), save->path );
137 xmlFreeDoc( doc );
138 return -1;
139 }
140
141 /* Iterate inside the naev_save. */
142 parent = root->xmlChildrenNode;
143 do {
144 xml_onlyNodes( parent );
145
146 /* Info. */
147 if ( xml_isNode( parent, "version" ) ) {
148 xmlNodePtr node = parent->xmlChildrenNode;
149 do {
150 xmlr_strd( node, "naev", save->version );
151 xmlr_strd( node, "data", save->data );
152 } while ( xml_nextNode( node ) );
153 continue;
154 }
155
156 else if ( xml_isNode( parent, "player" ) ) {
157 /* Get name. */
158 xmlr_attr_strd( parent, "name", save->player_name );
159 /* Parse rest. */
160 xmlNodePtr node = parent->xmlChildrenNode;
161 do {
162 xml_onlyNodes( node );
163
164 /* Player info. */
165 xmlr_strd( node, "location", save->spob );
166 xmlr_ulong( node, "credits", save->credits );
167 xmlr_strd( node, "chapter", save->chapter );
168 xmlr_strd( node, "difficulty", save->difficulty );
169
170 /* Time. */
171 if ( xml_isNode( node, "time" ) ) {
172 int cycles, periods, seconds;
173 xmlNodePtr cur = node->xmlChildrenNode;
174 cycles = periods = seconds = 0;
175 do {
176 xmlr_int( cur, "SCU", cycles );
177 xmlr_int( cur, "STP", periods );
178 xmlr_int( cur, "STU", seconds );
179 } while ( xml_nextNode( cur ) );
180 save->date = ntime_create( cycles, periods, seconds );
181 continue;
182 }
183
184 /* Ship info. */
185 if ( xml_isNode( node, "ship" ) ) {
186 xmlr_attr_strd( node, "name", save->shipname );
187 xmlr_attr_strd( node, "model", save->shipmodel );
188 continue;
189 }
190 } while ( xml_nextNode( node ) );
191 continue;
192 } else if ( xml_isNode( parent, "plugins" ) ) {
193 save->plugins = array_create( char * );
194 /* Parse rest. */
195 xmlNodePtr node = parent->xmlChildrenNode;
196 do {
197 xml_onlyNodes( node );
198
199 if ( xml_isNode( node, "plugin" ) ) {
200 const char *name = xml_get( node );
201 if ( name != NULL )
202 array_push_back( &save->plugins, strdup( name ) );
203 else
204 WARN( _( "Save '%s' has unnamed plugin node!" ), save->path );
205 }
206 } while ( xml_nextNode( node ) );
207 continue;
208 }
209 } while ( xml_nextNode( parent ) );
210
211 /* Defaults. */
212 if ( save->chapter == NULL )
213 save->chapter = strdup( start_chapter() );
214
215 save->compatible = load_compatibility( save );
216
217 /* Clean up. */
218 xmlFreeDoc( doc );
219
220 return 0;
221}
222
223static int load_loadThread( void *ptr )
224{
225 nsave_t *ns = ptr;
226 ns->ret = load_load( ns );
227 return ns->ret;
228}
229
233int load_refresh( void )
234{
235 ThreadQueue *tq = vpool_create();
236
237 if ( load_saves != NULL )
238 load_free();
239
240 /* Load the saves candidates. */
242 PHYSFS_enumerate( "saves", load_enumerateCallback, NULL );
243
244 /* Set up threads and load. */
245 for ( int i = 0; i < array_size( load_saves ); i++ ) {
246 player_saves_t *ps = &load_saves[i];
247 for ( int j = 0; j < array_size( ps->saves ); j++ ) {
248 nsave_t *ns = &ps->saves[j];
249 vpool_enqueue( tq, load_loadThread, ns );
250 }
251 }
252 vpool_wait( tq );
253 vpool_cleanup( tq );
254
255 /* Load the saves. */
256 for ( int i = array_size( load_saves ) - 1; i >= 0; i-- ) {
257 player_saves_t *ps = &load_saves[i];
258 for ( int j = array_size( ps->saves ) - 1; j >= 0; j-- ) {
259 const nsave_t *ns = &ps->saves[j];
260 if ( ns->ret != 0 ) {
261 free( ns->path );
262 free( ns->save_name );
263 array_erase( &ps->saves, &ps->saves[j], &ps->saves[j + 1] );
264 continue;
265 }
266 if ( ps->name == NULL )
267 ps->name = strdup( ns->player_name );
268 }
269 if ( ps->name == NULL )
271 }
272
273 /* Sort and done. */
274 for ( int i = 0; i < array_size( load_saves ); i++ ) {
275 player_saves_t *ps = &load_saves[i];
276 qsort( ps->saves, array_size( ps->saves ), sizeof( nsave_t ),
278 }
279
280 /* Dedup as necessary, this can be caused by some OS secretly renaming files
281 * when creating. In particular, windows seems to dislike directory names
282 * ending with a '.' which can cause the player save directory to mismatch
283 * the player save name.. */
284 qsort( load_saves, array_size( load_saves ), sizeof( player_saves_t ),
286 for ( int i = array_size( load_saves ) - 1; i > 0; i-- ) {
287 player_saves_t *ps = &load_saves[i];
288 if ( strcmp( ps->name, ps[-1].name ) != 0 )
289 continue;
290
291 /* Copy saves over. */
292 for ( int j = 0; j < array_size( ps->saves ); j++ ) {
293 const nsave_t *ns = &ps->saves[j];
294 array_push_back( &ps[-1].saves, *ns );
295 }
296
297 /* Now have to dedup saves. */
298 qsort( ps[-1].saves, array_size( ps[-1].saves ), sizeof( nsave_t ),
299 load_sortCompareName );
300 for ( int j = array_size( ps[-1].saves ) - 1; j > 0; j-- ) {
301 nsave_t *ns = &ps[-1].saves[j];
302 if ( strcmp( ns->save_name, ns[-1].save_name ) != 0 )
303 continue;
304
305 load_freeSave( ns );
306 array_erase( &ps[-1].saves, ns, &ns[1] );
307 }
308
309 /* Properly resort saves. */
310 qsort( ps[-1].saves, array_size( ps[-1].saves ), sizeof( nsave_t ),
312
313 /* Erase current iterator. */
314 array_free( ps->saves );
315 free( ps->name );
316 array_erase( &load_saves, ps, &ps[1] );
317 }
318
319 /* Resort based on time. */
320 qsort( load_saves, array_size( load_saves ), sizeof( player_saves_t ),
322
323 return 0;
324}
325
326static int load_enumerateCallbackPlayer( void *data, const char *origdir,
327 const char *fname )
328{
329 char *path;
330 const char *fmt;
331 size_t dir_len;
332 PHYSFS_Stat stat;
333
334 /* Want .ns extensions. */
335 size_t name_len = strlen( fname );
336 if ( name_len < 4 || strcmp( &fname[name_len - 3], ".ns" ) )
337 return PHYSFS_ENUM_OK;
338
339 dir_len = strlen( origdir );
340
341 fmt = dir_len && origdir[dir_len - 1] == '/' ? "%s%s" : "%s/%s";
342 SDL_asprintf( &path, fmt, origdir, fname );
343 if ( !PHYSFS_stat( path, &stat ) ) {
344 WARN( _( "PhysicsFS: Cannot stat %s: %s" ), path,
345 _( PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
346 free( path );
347 } else if ( stat.filetype == PHYSFS_FILETYPE_REGULAR ) {
348 player_saves_t *ps = (player_saves_t *)data;
349 nsave_t ns;
350 memset( &ns, 0, sizeof( ns ) );
351 ns.path = path;
352 ns.save_name = strdup( fname );
353 ns.save_name[strlen( ns.save_name ) - 3] = '\0';
354 ns.modtime = stat.modtime;
355 array_push_back( &ps->saves, ns );
356 } else
357 free( path );
358
359 return PHYSFS_ENUM_OK;
360}
361
365static int load_enumerateCallback( void *data, const char *origdir,
366 const char *fname )
367{
368 (void)data;
369 char *path, *backup_path;
370 const char *fmt;
371 size_t dir_len, name_len;
372 PHYSFS_Stat stat;
373
374 dir_len = strlen( origdir );
375 name_len = strlen( fname );
376
377 fmt = dir_len && origdir[dir_len - 1] == '/' ? "%s%s" : "%s/%s";
378 SDL_asprintf( &path, fmt, origdir, fname );
379 if ( !PHYSFS_stat( path, &stat ) )
380 WARN( _( "PhysicsFS: Cannot stat %s: %s" ), path,
381 _( PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
382 /* TODO remove this for 0.13.0 */
383 else if ( stat.filetype == PHYSFS_FILETYPE_REGULAR ) {
384 if ( ( name_len < 4 || strcmp( &fname[name_len - 3], ".ns" ) ) &&
385 ( name_len < 11 ||
386 strcmp( &fname[name_len - 10], ".ns.backup" ) ) ) {
387 free( path );
388 return PHYSFS_ENUM_OK;
389 }
390 if ( !PHYSFS_exists( "saves-pre-0.10.0" ) )
391 PHYSFS_mkdir( "saves-pre-0.10.0" );
392 SDL_asprintf( &backup_path, "saves-pre-0.10.0/%s", fname );
393 if ( !ndata_copyIfExists( path, backup_path ) )
394 old_saves_detected = 1;
395 free( backup_path );
396 move_old_save( path, fname, ".ns", "autosave.ns" );
397 move_old_save( path, fname, ".ns.backup", "backup.ns" );
398 } else if ( stat.filetype == PHYSFS_FILETYPE_DIRECTORY ) {
399 player_saves_t psave;
400 psave.name = NULL;
401 psave.saves = array_create( nsave_t );
402 PHYSFS_enumerate( path, load_enumerateCallbackPlayer, &psave );
403 array_push_back( &load_saves, psave );
404 }
405
406 free( path );
407 return PHYSFS_ENUM_OK;
408}
409
410static int load_compatibilityTest( const nsave_t *ns )
411{
412 char buf[STRMAX], buf2[STRMAX];
413 const plugin_t *plugins = plugin_list();
414 int l;
415
416 switch ( ns->compatible ) {
417 case SAVE_COMPATIBILITY_NAEV_VERSION:
418 if ( !dialogue_YesNo(
419 _( "Save game version mismatch" ),
420 _( "Save game '%s' version does not match Naev version:\n"
421 " Save version: #r%s#0\n"
422 " Naev version: %s\n"
423 "Are you sure you want to load this game? It may lose data." ),
424 ns->player_name, ns->version, naev_version( 0 ) ) )
425 return -1;
426 break;
427
428 case SAVE_COMPATIBILITY_PLUGINS:
429 buf[0] = '\0';
430 l = 0;
431 for ( int i = 0; i < array_size( ns->plugins ); i++ )
432 l += scnprintf( &buf[l], sizeof( buf ) - l, "%s%s",
433 ( l > 0 ) ? p_( "plugins", ", " ) : "#r",
434 ns->plugins[i] );
435 l += scnprintf( &buf[l], sizeof( buf ) - l, "#0" );
436 buf2[0] = '\0';
437 l = 0;
438 for ( int i = 0; i < array_size( plugins ); i++ )
439 l += scnprintf( &buf2[l], sizeof( buf2 ) - l, "%s%s",
440 ( l > 0 ) ? p_( "plugins", ", " ) : "",
441 plugin_name( &plugins[i] ) );
442 if ( !dialogue_YesNo(
443 _( "Save game plugin mismatch" ),
444 _( "Save game '%s' plugins do not match loaded plugins:\n"
445 " Save plugins: %s\n"
446 " Naev plugins: %s\n"
447 "Are you sure you want to load this game? It may lose data." ),
448 ns->player_name, buf, buf2 ) )
449 return -1;
450 break;
451
452 case SAVE_COMPATIBILITY_OK:
453 break;
454 }
455
456 return 0;
457}
458
459static const char *load_compatibilityString( const nsave_t *ns )
460{
461 switch ( ns->compatible ) {
462 case SAVE_COMPATIBILITY_NAEV_VERSION:
463 return _( "version mismatch" );
464
465 case SAVE_COMPATIBILITY_PLUGINS:
466 return _( "plugins mismatch" );
467
468 case SAVE_COMPATIBILITY_OK:
469 return _( "compatible" );
470 }
471 return NULL;
472}
473
477static int has_plugin( const char *plugin )
478{
479 const plugin_t *plugins = plugin_list();
480 for ( int j = 0; j < array_size( plugins ); j++ )
481 if ( strcmp( plugin, plugin_name( &plugins[j] ) ) == 0 )
482 return 1;
483 return 0;
484}
485
489static SaveCompatibility load_compatibility( const nsave_t *ns )
490{
491 int diff = naev_versionCompare( ns->version );
492
493 if ( ABS( diff ) >= 2 )
494 return SAVE_COMPATIBILITY_NAEV_VERSION;
495
496 for ( int i = 0; i < array_size( ns->plugins ); i++ ) {
497 if ( !has_plugin( ns->plugins[i] ) )
498 return SAVE_COMPATIBILITY_PLUGINS;
499 }
500
501 return SAVE_COMPATIBILITY_OK;
502}
503
507static int load_sortComparePlayersName( const void *p1, const void *p2 )
508{
509 const player_saves_t *ps1, *ps2;
510 ps1 = (const player_saves_t *)p1;
511 ps2 = (const player_saves_t *)p2;
512 return strcmp( ps1->name, ps2->name );
513}
514
518static int load_sortComparePlayers( const void *p1, const void *p2 )
519{
520 const player_saves_t *ps1, *ps2;
521 ps1 = (const player_saves_t *)p1;
522 ps2 = (const player_saves_t *)p2;
523 return load_sortCompare( &ps1->saves[0], &ps2->saves[0] );
524}
525
526static int load_sortCompareName( const void *p1, const void *p2 )
527{
528 int ret;
529 const nsave_t *ns1 = (const nsave_t *)p1;
530 const nsave_t *ns2 = (const nsave_t *)p2;
531 ret = strcmp( ns1->save_name, ns2->save_name );
532 if ( ret )
533 return ret;
534
535 /* Sort by compatibility first. */
536 if ( !ns1->compatible && ns2->compatible )
537 return -1;
538 else if ( ns1->compatible && !ns2->compatible )
539 return +1;
540
541 /* Search by file modification date. */
542 if ( ns1->modtime > ns2->modtime )
543 return -1;
544 else if ( ns1->modtime < ns2->modtime )
545 return +1;
546
547 return 0;
548}
549
553static int load_sortCompare( const void *p1, const void *p2 )
554{
555 const nsave_t *ns1, *ns2;
556 ns1 = (const nsave_t *)p1;
557 ns2 = (const nsave_t *)p2;
558
559 /* Sort by compatibility first. */
560 if ( !ns1->compatible && ns2->compatible )
561 return -1;
562 else if ( ns1->compatible && !ns2->compatible )
563 return +1;
564
565 /* Search by file modification date. */
566 if ( ns1->modtime > ns2->modtime )
567 return -1;
568 else if ( ns1->modtime < ns2->modtime )
569 return +1;
570
571 /* Finally sort by name. */
572 return strcmp( ns1->save_name, ns2->save_name );
573}
574
575static void load_freeSave( nsave_t *ns )
576{
577 for ( int k = 0; k < array_size( ns->plugins ); k++ )
578 free( ns->plugins[k] );
579 array_free( ns->plugins );
580 free( ns->save_name );
581 free( ns->player_name );
582 free( ns->path );
583 free( ns->version );
584 free( ns->data );
585 free( ns->spob );
586 free( ns->chapter );
587 free( ns->difficulty );
588 free( ns->shipname );
589 free( ns->shipmodel );
590}
591
595void load_free( void )
596{
597 for ( int i = 0; i < array_size( load_saves ); i++ ) {
598 player_saves_t *ps = &load_saves[i];
599 free( ps->name );
600 for ( int j = 0; j < array_size( ps->saves ); j++ ) {
601 load_freeSave( &ps->saves[j] );
602 }
603 array_free( ps->saves );
604 }
606 load_saves = NULL;
607}
608
612const nsave_t *load_getList( const char *name )
613{
614 if ( !array_size( load_saves ) )
615 return NULL;
616 if ( name == NULL )
617 return load_saves[0].saves;
618 for ( int i = 0; i < array_size( load_saves ); i++ )
619 if ( strcmp( load_saves[i].name, name ) == 0 )
620 return load_saves[i].saves;
621 return NULL;
622}
623
628{
629 unsigned int wid;
630 char **names;
631 int n;
632 int pos = 0;
633
634 /* window */
635 wid = window_create( "wdwLoadGameMenu", _( "Load Pilot" ), -1, -1,
639
640 load_refresh();
641
642 n = array_size( load_saves );
643 if ( n > 0 ) {
644 names = malloc( sizeof( char * ) * n );
645 for ( int i = 0; i < n; i++ ) {
646 nsave_t *ns = &load_saves[i].saves[0];
647 if ( ns->compatible ) {
648 char buf[STRMAX_SHORT];
649 scnprintf( buf, sizeof( buf ), _( "%s (#r%s#0)" ), ns->player_name,
650 load_compatibilityString( ns ) );
651 names[i] = strdup( buf );
652 } else
653 names[i] = strdup( ns->player_name );
654 if ( selected_player != NULL && !strcmp( names[i], selected_player ) )
655 pos = i;
656 }
657 }
658 /* case there are no players */
659 else {
660 names = malloc( sizeof( char *) );
661 names[0] = strdup( _( "None" ) );
662 n = 1;
663 }
664
665 /* Player text. */
666 window_addText( wid, -20, -40, BUTTON_WIDTH * 2 + 20,
667 LOAD_HEIGHT - 40 - 20 - 2 * ( BUTTON_HEIGHT + 20 ), 0,
668 "txtPilot", &gl_smallFont, NULL, NULL );
669
670 window_addList( wid, 20, -40, LOAD_WIDTH - BUTTON_WIDTH * 2 - 80,
671 LOAD_HEIGHT - 110, "lstNames", names, n, pos,
673
674 /* Buttons */
675 window_addButtonKey( wid, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT, "btnBack",
676 _( "Back" ), load_menu_close, SDLK_b );
677 window_addButtonKey( wid, -20 - BUTTON_WIDTH - 20, 20 + BUTTON_HEIGHT + 15,
678 BUTTON_WIDTH, BUTTON_HEIGHT, "btnSnapshots",
679 _( "Snapshots" ), load_menu_snapshots, SDLK_s );
680 window_addButtonKey( wid, -20, 20 + BUTTON_HEIGHT + 15, BUTTON_WIDTH,
681 BUTTON_HEIGHT, "btnLoad", _( "Load" ), load_menu_load,
682 SDLK_l );
683 window_addButtonKey( wid, 20, 20, BUTTON_WIDTH, BUTTON_HEIGHT, "btnDelete",
684 _( "Delete" ), load_menu_delete, SDLK_d );
685
686 if ( old_saves_detected && !player_warned ) {
687 char buf[STRMAX_SHORT];
688 snprintf( buf, sizeof( buf ), "%s%s",
689 PHYSFS_getRealDir( "saves-pre-0.10.0" ), "saves-pre-0.10.0" );
690 dialogue_alert( _( "Naev has detected saves in pre-0.10.0 format, and "
691 "has automatically migrated them to the new format. "
692 "Old saves have been backed up at '%s'." ),
693 buf );
694 player_warned = 1;
695 }
696}
697
698static void load_snapshot_menu_onClose( unsigned int wid, const char *str )
699{
700 (void)str;
701 free( window_getData( wid ) );
702}
703
709void load_loadSnapshotMenu( const char *name, int disablesave )
710{
711 unsigned int wid;
712 char **names;
713 player_saves_t *ps;
714 int n;
715 char *t;
716 int *data;
717
718 ps = NULL;
719 for ( int i = 0; i < array_size( load_saves ); i++ ) {
720 if ( strcmp( load_saves[i].name, name ) == 0 ) {
721 ps = &load_saves[i];
722 break;
723 }
724 }
725 if ( ps == NULL ) {
726 WARN( _( "Player '%s' not found in list of saves!" ), name );
727 return;
728 }
729 load_player = ps;
730
731 t = strdup( name );
732 free( selected_player );
733 selected_player = t;
734
735 /* window */
736 wid = window_create( "wdwLoadSnapshotMenu", _( "Load Snapshot" ), -1, -1,
740
741 data = malloc( sizeof( int ) );
742 *data = disablesave;
743 window_setData( wid, data );
744 window_onClose( wid, load_snapshot_menu_onClose );
745
746 /* load the saves */
747 n = array_size( ps->saves );
748 if ( n > 0 ) {
749 names = malloc( sizeof( char * ) * n );
750 for ( int i = 0; i < n; i++ ) {
751 nsave_t *ns = &ps->saves[i];
752 if ( ns->compatible ) {
753 char buf[STRMAX_SHORT];
754 scnprintf( buf, sizeof( buf ), _( "%s (#r%s#0)" ), ns->save_name,
755 load_compatibilityString( ns ) );
756 names[i] = strdup( buf );
757 } else
758 names[i] = strdup( ns->save_name );
759 }
760 }
761 /* case there are no files */
762 else {
763 names = malloc( sizeof( char *) );
764 names[0] = strdup( _( "None" ) );
765 n = 1;
766 }
767
768 /* Player text. */
769 window_addText( wid, -20, -40, BUTTON_WIDTH * 2 + 20,
770 LOAD_HEIGHT - 40 - 20 - 2 * ( BUTTON_HEIGHT + 20 ), 0,
771 "txtPilot", &gl_smallFont, NULL, NULL );
772
773 window_addList( wid, 20, -40, LOAD_WIDTH - BUTTON_WIDTH * 2 - 80,
774 LOAD_HEIGHT - 110, "lstSaves", names, n, 0,
776
777 /* Buttons */
778 window_addButtonKey( wid, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT, "btnBack",
779 _( "Back" ), load_snapshot_menu_close, SDLK_b );
780 window_addButtonKey( wid, -20 - BUTTON_WIDTH - 20, 20 + BUTTON_HEIGHT + 15,
781 BUTTON_WIDTH, BUTTON_HEIGHT, "btnSave", _( "Save As" ),
782 load_snapshot_menu_save, SDLK_s );
783 window_addButtonKey( wid, -20, 20 + BUTTON_HEIGHT + 15, BUTTON_WIDTH,
784 BUTTON_HEIGHT, "btnLoad", _( "Load" ),
785 load_snapshot_menu_load, SDLK_l );
786 window_addButtonKey( wid, 20, 20, BUTTON_WIDTH, BUTTON_HEIGHT, "btnDelete",
787 _( "Delete" ), load_snapshot_menu_delete, SDLK_d );
788
789 if ( disablesave || window_exists( "wdwLoadGameMenu" ) )
790 window_disableButton( wid, "btnSave" );
791 else {
792 int can_save = landed && !player_isFlag( PLAYER_NOSAVE );
793 if ( !can_save )
794 window_disableButton( wid, "btnSave" );
795 }
796}
797
803static void load_menu_snapshots( unsigned int wdw, const char *str )
804{
805 (void)str;
806 int pos = toolkit_getListPos( wdw, "lstNames" );
807 if ( array_size( load_saves ) <= 0 )
808 return;
809 load_loadSnapshotMenu( load_saves[pos].name, 1 );
810}
811
817static void load_snapshot_menu_save( unsigned int wdw, const char *str )
818{
819 char *save_name = dialogue_input(
820 _( "Save game" ), 1, 60, _( "Please give the new snapshot a name:" ) );
821 if ( save_name == NULL )
822 return;
823 char path[PATH_MAX];
824 snprintf( path, sizeof( path ), "saves/%s/%s.ns", player.name, save_name );
825 if ( PHYSFS_exists( path ) ) {
826 int r = dialogue_YesNo(
827 _( "Overwrite" ),
828 _( "You already have a snapshot named '%s'. Overwrite?" ), save_name );
829 if ( r == 0 ) {
830 load_snapshot_menu_save( wdw, str );
831 free( save_name );
832 return;
833 }
834 }
835 if ( save_all_with_name( save_name ) < 0 )
837 _( "Failed to save the game! You should exit and check the log to see "
838 "what happened and then file a bug report!" ) );
839 else {
840 load_refresh();
841 int disablesave = *(int *)window_getData( wdw );
842 load_snapshot_menu_close( wdw, str );
843 load_loadSnapshotMenu( player.name, disablesave );
844 }
845 free( save_name );
846}
847
853static void load_menu_close( unsigned int wdw, const char *str )
854{
855 (void)str;
856 window_destroy( wdw );
857}
858
864static void load_snapshot_menu_close( unsigned int wdw, const char *str )
865{
866 (void)str;
867 window_destroy( wdw );
868}
869
875static void display_save_info( unsigned int wid, const nsave_t *ns )
876{
877 char buf[STRMAX_SHORT], credits[ECON_CRED_STRLEN], date[64],
878 difficulty[STRMAX_SHORT];
879 size_t l = 0;
880
881 if ( ns->difficulty == NULL ) {
882 const Difficulty *d = difficulty_cur();
883 snprintf( difficulty, sizeof( difficulty ), _( "%s (options)" ),
884 _( d->name ) );
885 } else
886 snprintf( difficulty, sizeof( difficulty ), _( "%s (this save)" ),
887 _( ns->difficulty ) );
888
889 credits2str( credits, ns->credits, 2 );
890 ntime_prettyBuf( date, sizeof( date ), ns->date, 2 );
891 l += scnprintf( &buf[l], sizeof( buf ) - l, "#n%s", _( "Name:" ) );
892 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#0 %s", ns->player_name );
893 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#n%s", _( "Version:" ) );
894 if ( ns->compatible == SAVE_COMPATIBILITY_NAEV_VERSION )
895 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n #r%s#0", ns->version );
896 else
897 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#0 %s", ns->version );
898 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#n%s", _( "Difficulty:" ) );
899 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#0 %s", difficulty );
900 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#n%s", _( "Date:" ) );
901 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#0 %s", date );
902 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#n%s", _( "Chapter:" ) );
903 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#0 %s", ns->chapter );
904 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#n%s", _( "Space Object:" ) );
905 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#0 %s", _( ns->spob ) );
906 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#n%s", _( "Credits:" ) );
907 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#0 %s", credits );
908 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#n%s", _( "Ship Name:" ) );
909 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#0 %s", ns->shipname );
910 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#n%s", _( "Ship Model:" ) );
911 l +=
912 scnprintf( &buf[l], sizeof( buf ) - l, "\n#0 %s", _( ns->shipmodel ) );
913 if ( array_size( ns->plugins ) > 0 ) {
914 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n#n%s", _( "Plugins:" ) );
915 l +=
916 scnprintf( &buf[l], sizeof( buf ) - l, "\n#0 #%c%s#0",
917 has_plugin( ns->plugins[0] ) ? '0' : 'r', ns->plugins[0] );
918 for ( int i = 1; i < array_size( ns->plugins ); i++ ) {
919 l += scnprintf(
920 &buf[l], sizeof( buf ) - l, p_( "plugins list", ", #%c%s#0" ),
921 has_plugin( ns->plugins[i] ) ? '0' : 'r', ns->plugins[i] );
922 }
923 }
924 window_modifyText( wid, "txtPilot", buf );
925}
926
934static void move_old_save( const char *path, const char *fname, const char *ext,
935 const char *new_name )
936{
937 size_t name_len = strlen( fname );
938 size_t ext_len = strlen( ext );
939 if ( name_len >= ext_len + 1 &&
940 !strcmp( &fname[name_len - ext_len], ext ) ) {
941 char *new_path;
942 char *dirname = strdup( fname );
943 dirname[name_len - ext_len] = '\0';
944 SDL_asprintf( &new_path, "saves/%s", dirname );
945 if ( !PHYSFS_exists( new_path ) )
946 PHYSFS_mkdir( new_path );
947 free( new_path );
948 SDL_asprintf( &new_path, "saves/%s/%s", dirname, new_name );
949 /* If it's going to overwrite a file, try to back it up. */
950 if ( PHYSFS_exists( new_path ) ) {
951 int tries = 0;
952 char *bkp_path;
953 SDL_asprintf( &bkp_path, "%s.bkp", new_path );
954 while ( PHYSFS_exists( bkp_path ) && ( tries++ < 10 ) ) {
955 char *bkp_bkp_path;
956 SDL_asprintf( &bkp_bkp_path, "%s.bkp", bkp_path );
957 free( bkp_path );
958 bkp_path = bkp_bkp_path;
959 }
960 ndata_copyIfExists( new_path, bkp_path );
961 free( bkp_path );
962 }
963 /* Copy over the old file. */
964 if ( !ndata_copyIfExists( path, new_path ) )
965 if ( !PHYSFS_delete( path ) )
966 dialogue_alert( _( "Unable to delete %s" ), path );
967 free( new_path );
968 free( dirname );
969 }
970}
971
977static void load_menu_update( unsigned int wid, const char *str )
978{
979 (void)str;
980 int pos;
981 const nsave_t *ns;
982
983 /* Make sure list is ok. */
984 if ( array_size( load_saves ) <= 0 )
985 return;
986
987 /* Get position. */
988 pos = toolkit_getListPos( wid, "lstNames" );
989 ns = &load_saves[pos].saves[0];
990 if ( selected_player != NULL )
991 free( selected_player );
992 selected_player = strdup( load_saves[pos].name );
993
994 display_save_info( wid, ns );
995}
996
1002static void load_snapshot_menu_update( unsigned int wid, const char *str )
1003{
1004 (void)str;
1005 int pos;
1006 nsave_t *ns;
1007
1008 /* Make sure list is ok. */
1009 if ( array_size( load_player->saves ) <= 0 )
1010 return;
1011
1012 /* Get position. */
1013 pos = toolkit_getListPos( wid, "lstSaves" );
1014 ns = &load_player->saves[pos];
1015
1016 display_save_info( wid, ns );
1017}
1018
1024static void load_menu_load( unsigned int wdw, const char *str )
1025{
1026 (void)str;
1027 int pos;
1028 unsigned int wid;
1029 nsave_t *ns;
1030
1031 wid = window_get( "wdwLoadGameMenu" );
1032 pos = toolkit_getListPos( wid, "lstNames" );
1033
1034 if ( array_size( load_saves ) <= 0 )
1035 return;
1036
1037 ns = &load_saves[pos].saves[0];
1038
1039 /* Check version. */
1040 if ( load_compatibilityTest( ns ) )
1041 return;
1042
1043 /* Close menus before loading for proper rendering. */
1044 load_menu_close( wdw, NULL );
1045
1046 /* Close the main menu. */
1048
1049 /* Try to load the game. */
1050 if ( load_game( ns ) ) {
1051 /* Failed so reopen both. */
1052 menu_main();
1054 }
1055}
1056
1062static void load_snapshot_menu_load( unsigned int wdw, const char *str )
1063{
1064 (void)str;
1065 int wid, pos;
1066
1067 wid = window_get( "wdwLoadSnapshotMenu" );
1068
1069 if ( array_size( load_player->saves ) <= 0 )
1070 return;
1071
1072 pos = toolkit_getListPos( wid, "lstSaves" );
1073
1074 /* Check version. */
1075 if ( load_compatibilityTest( &load_player->saves[pos] ) )
1076 return;
1077
1078 /* Close menus before loading for proper rendering. */
1079 load_snapshot_menu_close( wdw, NULL );
1080
1081 if ( window_exists( "wdwLoadGameMenu" ) ) {
1082 /* Close the main and the load menu. */
1083 load_menu_close( window_get( "wdwLoadGameMenu" ), NULL );
1085 } else
1087
1088 /* Try to load the game. */
1089 if ( load_game( &load_player->saves[pos] ) ) {
1090 /* Failed so reopen both. */
1091 /* TODO how to handle failure here? It can happen at many different points
1092 * now. */
1093 /*menu_main();
1094 load_loadGameMenu();*/
1095 }
1096}
1097
1098static void load_menu_delete( unsigned int wdw, const char *str )
1099{
1100 unsigned int wid;
1101 int n, pos;
1102 char path[PATH_MAX];
1103
1104 wid = window_get( "wdwLoadGameMenu" );
1105 pos = toolkit_getListPos( wid, "lstNames" );
1106
1107 if ( array_size( load_saves ) <= 0 )
1108 return;
1109
1110 if ( dialogue_YesNo(
1111 _( "Permanently Delete?" ),
1112 _( "Are you sure you want to permanently delete the "
1113 "character '%s'?\n#rThis action is irreversible!#0" ),
1114 load_saves[pos].name ) == 0 )
1115 return;
1116
1117 /* Remove it. */
1118 n = array_size( load_saves[pos].saves );
1119 for ( int i = 0; i < n; i++ )
1120 if ( !PHYSFS_delete( load_saves[pos].saves[i].path ) )
1121 dialogue_alert( _( "Unable to delete %s" ),
1122 load_saves[pos].saves[i].path );
1123 snprintf( path, sizeof( path ), "saves/%s", load_saves[pos].name );
1124 if ( !PHYSFS_delete( path ) )
1125 dialogue_alert( _( "Unable to delete '%s' directory" ),
1126 load_saves[pos].name );
1127
1128 load_refresh();
1129
1130 /* need to reload the menu */
1131 load_menu_close( wdw, str );
1133}
1134
1140static void load_snapshot_menu_delete( unsigned int wdw, const char *str )
1141{
1142 int pos, last_save;
1143 unsigned int wid = window_get( "wdwLoadSnapshotMenu" );
1144
1145 if ( array_size( load_player->saves ) <= 0 )
1146 return;
1147
1148 pos = toolkit_getListPos( wid, "lstSaves" );
1149
1150 if ( dialogue_YesNo( _( "Permanently Delete?" ),
1151 _( "Are you sure you want to permanently delete the "
1152 "snapshot '%s'?\n#rThis action is irreversible!#0" ),
1153 load_player->saves[pos].save_name ) == 0 )
1154 return;
1155
1156 /* Remove it. */
1157 if ( !PHYSFS_delete( load_player->saves[pos].path ) )
1158 dialogue_alert( _( "Unable to delete %s" ),
1159 load_player->saves[pos].path );
1160 last_save = ( array_size( load_player->saves ) <= 1 );
1161
1162 /* Delete directory if all are gone. */
1163 if ( last_save ) {
1164 char path[PATH_MAX];
1165 snprintf( path, sizeof( path ), "saves/%s", load_player->name );
1166 if ( !PHYSFS_delete( path ) )
1167 dialogue_alert( _( "Unable to delete '%s' directory" ),
1168 load_player->name );
1169 }
1170
1171 load_refresh();
1172
1173 /* need to reload the menu */
1174 int disablesave = *(int *)window_getData( wdw );
1175 load_snapshot_menu_close( wdw, str );
1176 if ( window_exists( "wdwLoadGameMenu" ) ) {
1177 wid = window_get( "wdwLoadGameMenu" );
1178 load_menu_close( wid, str );
1180 }
1181 if ( !last_save )
1182 load_loadSnapshotMenu( selected_player, disablesave );
1183}
1184
1185static void load_compatSlots( void )
1186{
1187 /* Vars for loading old saves. */
1188 char **sships;
1189 glTexture **tships;
1190 int nships;
1191 Pilot *ship;
1192
1193 nships = player_nships();
1194 sships = malloc( nships * sizeof( char * ) );
1195 tships = malloc( nships * sizeof( glTexture * ) );
1196 nships = player_ships( sships, tships );
1197 ship = player.p;
1198 for ( int i = -1; i < nships; i++ ) {
1199 if ( i >= 0 )
1200 ship = player_getShip( sships[i] );
1201 /* Remove all outfits. */
1202 for ( int j = 0; j < array_size( ship->outfits ); j++ ) {
1203 ShipOutfitSlot *sslot;
1204
1205 if ( ship->outfits[j]->outfit != NULL ) {
1206 player_addOutfit( ship->outfits[j]->outfit, 1 );
1207 pilot_rmOutfitRaw( ship, ship->outfits[j] );
1208 }
1209
1210 /* Add default outfit. */
1211 sslot = ship->outfits[j]->sslot;
1212 if ( sslot->data != NULL )
1213 pilot_addOutfitRaw( ship, sslot->data, ship->outfits[j] );
1214 }
1215
1216 pilot_calcStats( ship );
1217 }
1218
1219 /* Clean up. */
1220 for ( int i = 0; i < nships; i++ )
1221 free( sships[i] );
1222 free( sships );
1223 free( tships );
1224}
1225
1232int load_gameDiff( const char *file )
1233{
1234 xmlNodePtr node;
1235 xmlDocPtr doc;
1236
1237 /* Make sure it exists. */
1238 if ( !PHYSFS_exists( file ) ) {
1239 dialogue_alertRaw( _( "Saved game file seems to have been deleted." ) );
1240 return -1;
1241 }
1242
1243 /* Load the XML. */
1244 doc = load_xml_parsePhysFS( file );
1245 if ( doc == NULL )
1246 goto err;
1247 node = doc->xmlChildrenNode; /* base node */
1248 if ( node == NULL )
1249 goto err_doc;
1250
1251 /* Diffs should be cleared automatically first. */
1252 diff_load( node );
1253
1254 /* Free. */
1255 xmlFreeDoc( doc );
1256
1257 return 0;
1258
1259err_doc:
1260 xmlFreeDoc( doc );
1261err:
1262 WARN( _( "Saved game '%s' invalid!" ), file );
1263 return -1;
1264}
1265
1272int load_game( const nsave_t *ns )
1273{
1274 const char **data;
1275 const char *file = ns->path;
1276 const char *version = ns->version;
1277
1278 /* Make sure it exists. */
1279 if ( !PHYSFS_exists( file ) ) {
1280 dialogue_alertRaw( _( "Saved game file seems to have been deleted." ) );
1281 return -1;
1282 }
1283
1284#if HAVE_TRACY
1285 char buf[STRMAX_SHORT];
1286 size_t l = snprintf( buf, sizeof( buf ), "Loading save '%s'", file );
1287 NTracingMessage( buf, l );
1288#endif /* TRACY */
1289
1290 /* Some global cleaning has to be done here. */
1292 hook_clear();
1293
1294 data = malloc( sizeof( const char *) * 2 );
1295 data[0] = file;
1296 data[1] = version;
1297 /* If the player isn't loaded, hooks aren't run so we can just go right away.
1298 */
1299 if ( ( player.p == NULL ) ||
1300 player_isFlag( PLAYER_DESTROYED ) ) /* same condition in hook.c */
1301 return load_gameInternalHook( data );
1302 else {
1303 /* Break out of potential inner loops. */
1304 SDL_Event event;
1305 SDL_memset( &event, 0, sizeof( event ) );
1306 event.type = SDL_LOOPDONE;
1307 SDL_PushEvent( &event );
1308
1309 /* Delay one frame. */
1310 hook_addFunc( load_gameInternalHook, data, "safe" );
1311 }
1312 return 0;
1313}
1314
1318static int load_gameInternalHook( void *data )
1319{
1320 xmlNodePtr node;
1321 xmlDocPtr doc;
1322 Spob *pnt;
1323 int misn_failed = 0, evt_failed = 0;
1324 const char **sdata = data;
1325 const char *file = sdata[0];
1326 const char *version = sdata[1];
1327 int version_diff = ( version != NULL ) ? naev_versionCompare( version ) : 0;
1328 free( data );
1329
1330 /* Load the XML. */
1331 doc = load_xml_parsePhysFS( file );
1332 if ( doc == NULL )
1333 goto err;
1334 node = doc->xmlChildrenNode; /* base node */
1335 if ( node == NULL )
1336 goto err_doc;
1337
1338 /* Clean up possible stuff that should be cleaned. */
1341 render_postprocessCleanup();
1342
1343 /* Welcome message - must be before space_init. */
1344 player_message( _( "#gWelcome to %s!" ), APPNAME );
1345 player_message( "#g v%s", naev_version( 0 ) );
1346
1347 /* We simulate landing, because it makes the player go over
1348 available limits and closes an exploit where the player
1349 can cheat the cargo limits by using outfits that increase
1350 the limit, accept missions, and then reduce it under the
1351 limit.
1352 TODO do this in a less hack way. */
1353 landed = 1;
1354
1355 /* Now begin to load. */
1356 diff_load( node ); /* Must load first to work properly. */
1358 missions_loadCommodity( node ); /* Must be loaded before player. */
1359 pfaction_load( node ); /* Must be loaded before player so the messages show
1360 up properly. Also before space_playerLoad. */
1361 pnt = player_load( node );
1362 player.loaded_version =
1363 strdup( ( version != NULL ) ? version : naev_version( 0 ) );
1364
1365 /* Sanitize for new version. */
1366 if ( version_diff <= -2 ) {
1367 WARN( _( "Old version detected. Sanitizing ships for slots" ) );
1368 load_compatSlots();
1369 }
1370
1371 /* Load more stuff. */
1372 space_playerLoad( node, player.loaded_version );
1373 var_load( node );
1374 misn_failed = missions_loadActive( node );
1375 evt_failed = events_loadActive( node );
1376 news_loadArticles( node );
1377 hook_load( node );
1378
1379 /* Initialize the economy. */
1380 economy_init();
1381 economy_sysLoad( node );
1382
1383 /* Initialise the ship log */
1384 shiplog_new();
1385 shiplog_load( node );
1386
1387 /* Check validity. */
1389
1390 /* Run the load event trigger. */
1391 events_trigger( EVENT_TRIGGER_LOAD );
1392
1393 /* Create escorts in space. */
1395
1396 landed =
1397 0; /* TODO remove this hack (causes crash and fails to land otherwise). */
1398
1399 /* Load the GUI. */
1400 if ( gui_load( gui_pick() ) )
1401 gui_load( start_gui() ); /* Failed so use fallback... */
1402
1403 /* Land the player. */
1404 land( pnt, 1 );
1405
1406 /* Sanitize the GUI. */
1407 gui_setCargo();
1408 gui_setShip();
1409
1410 xmlFreeDoc( doc );
1411
1412 if ( misn_failed || evt_failed ) {
1413 char buf[STRMAX];
1414 unsigned int l = 0;
1415 const char **misn_failed_str = mission_loadFailed();
1416 l += scnprintf(
1417 &buf[l], sizeof( buf ) - l,
1418 _( "Saved game '%s' failed to load some missions/events properly!" ),
1419 file );
1420 if ( misn_failed ) {
1421 l += scnprintf( &buf[l], sizeof( buf ) - l,
1422 _( "\nIn particular, the following missions have "
1423 "failed to load and been removed:" ) );
1424 for ( int i = 0; i < array_size( misn_failed_str ); i++ )
1425 l += scnprintf( &buf[l], sizeof( buf ) - l, _( "\n #r%s#0" ),
1426 misn_failed_str[i] );
1427 }
1428 /*l +=*/scnprintf(
1429 &buf[l], sizeof( buf ) - l,
1430 _( "\nNote that, in general, you should be able to find the "
1431 "missions/events again and start them without penalty." ) );
1432 dialogue_alertRaw( buf );
1433 }
1434
1435 /* Set loaded. */
1436 save_loaded = 1;
1437
1438 return 0;
1439
1440err_doc:
1441 xmlFreeDoc( doc );
1442err:
1443 dialogue_alert( _( "Saved game '%s' invalid!" ), file );
1444 menu_main();
1445 return -1;
1446}
1447
1452static xmlDocPtr load_xml_parsePhysFS( const char *filename )
1453{
1454 char buf[PATH_MAX];
1455 snprintf( buf, sizeof( buf ), "%s/%s", PHYSFS_getWriteDir(), filename );
1456 return xmlParseFile( buf );
1457}
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 BUTTON_HEIGHT
Definition board.c:28
#define BUTTON_WIDTH
Definition board.c:27
void credits2str(char *str, credits_t credits, int decimals)
Converts credits to a usable string for displaying.
Definition commodity.c:75
void dialogue_alert(const char *fmt,...)
Displays an alert popup with only an ok button and a message.
Definition dialogue.c:130
char * dialogue_input(const char *title, int min, int max, const char *fmt,...)
Creates a dialogue that allows the player to write a message.
Definition dialogue.c:441
void dialogue_alertRaw(const char *msg)
Displays an alert popup with only an ok button and a message.
Definition dialogue.c:149
int dialogue_YesNo(const char *caption, const char *fmt,...)
Runs a dialogue with both yes and no options.
Definition dialogue.c:352
int economy_init(void)
Initializes the economy.
Definition economy.c:438
void event_checkValidity(void)
Checks the event validity and cleans up after them.
Definition event.c:814
void events_trigger(EventTrigger_t trigger)
Runs all the events matching a trigger.
Definition event.c:319
glFont gl_smallFont
Definition font.c:159
const char * gui_pick(void)
Determines which GUI should be used.
Definition gui.c:1933
int gui_load(const char *name)
Attempts to load the actual GUI.
Definition gui.c:1965
void gui_setShip(void)
Player just upgraded their ship or modified it.
Definition gui.c:1880
void player_message(const char *fmt,...)
Adds a mesg to the queue to be displayed on screen.
Definition gui.c:353
void gui_setCargo(void)
Player just changed their cargo.
Definition gui.c:1856
void hook_clear(void)
Clears the hooks.
Definition hook.c:1187
unsigned int hook_addFunc(int(*func)(void *), void *data, const char *stack)
Adds a function hook to be run.
Definition hook.c:634
int landed
Definition land.c:78
void land(Spob *p, int load)
Opens up all the land dialogue stuff.
Definition land.c:1413
#define LOAD_WIDTH
Definition load.c:46
static void load_menu_update(unsigned int wid, const char *str)
Updates the load menu.
Definition load.c:977
static int load_load(nsave_t *save)
Loads an individual save.
Definition load.c:123
int load_game(const nsave_t *ns)
Actually loads a new game based on save structure.
Definition load.c:1272
int news_loadArticles(xmlNodePtr parent)
Loads the player's active articles from a save, initilizes news.
Definition news.c:487
static void load_menu_close(unsigned int wdw, const char *str)
Closes the load game menu.
Definition load.c:853
static void load_menu_load(unsigned int wdw, const char *str)
Loads a new game.
Definition load.c:1024
#define LOAD_HEIGHT
Definition load.c:47
static int has_plugin(const char *plugin)
Checks to see if has a plugin.
Definition load.c:477
int diff_load(xmlNodePtr parent)
Loads the diffs.
Definition unidiff.c:1979
int events_loadActive(xmlNodePtr parent)
Loads the player's active events from a save.
Definition event.c:877
static int load_gameInternalHook(void *data)
Loads a game. Meant to be run in a function hook.
Definition load.c:1318
int economy_sysLoad(xmlNodePtr parent)
Loads player's economy properties from an XML node.
Definition economy.c:1019
int hook_load(xmlNodePtr parent)
Loads hooks for a player.
Definition hook.c:1310
static int load_sortCompare(const void *p1, const void *p2)
qsort compare function for files.
Definition load.c:553
int var_load(xmlNodePtr parent)
Loads the vars from XML file.
Definition nlua_var.c:79
static SaveCompatibility load_compatibility(const nsave_t *ns)
Checks to see if a save is compatible with current Naev.
Definition load.c:489
static void move_old_save(const char *path, const char *fname, const char *ext, const char *new_name)
Moves old Naev saves to subdirectories.
Definition load.c:934
void load_loadSnapshotMenu(const char *name, int disablesave)
Opens the load snapshot menu.
Definition load.c:709
int load_refresh(void)
Loads or refreshes saved games for the player.
Definition load.c:233
static void display_save_info(unsigned int wid, const nsave_t *ns)
Displays Naev save info.
Definition load.c:875
static void load_snapshot_menu_save(unsigned int wdw, const char *str)
Creates new custom snapshot.
Definition load.c:817
static int load_enumerateCallback(void *data, const char *origdir, const char *fname)
The PHYSFS_EnumerateCallback for load_refresh.
Definition load.c:365
static void load_menu_snapshots(unsigned int wdw, const char *str)
Opens the load snapshot menu.
Definition load.c:803
void load_loadGameMenu(void)
Opens the load game menu.
Definition load.c:627
static player_saves_t * load_saves
Definition load.c:57
static void load_snapshot_menu_load(unsigned int wdw, const char *str)
Loads a new game.
Definition load.c:1062
void load_free(void)
Frees loaded save stuff.
Definition load.c:595
const nsave_t * load_getList(const char *name)
Gets the array (array.h) of loaded saves.
Definition load.c:612
static player_saves_t * load_player
Definition load.c:58
static void load_snapshot_menu_delete(unsigned int wdw, const char *str)
Deletes an old game.
Definition load.c:1140
int pfaction_load(xmlNodePtr parent)
Loads the player's faction standings.
Definition faction.c:2242
static void load_snapshot_menu_close(unsigned int wdw, const char *str)
Closes the load snapshot menu.
Definition load.c:864
Spob * player_load(xmlNodePtr parent)
Loads the player stuff.
Definition player.c:3736
static xmlDocPtr load_xml_parsePhysFS(const char *filename)
Temporary (hopefully) wrapper around xml_parsePhysFS in support of gzipped XML (like ....
Definition load.c:1452
int load_gameDiff(const char *file)
Loads the diffs from game file.
Definition load.c:1232
static void load_snapshot_menu_update(unsigned int wid, const char *str)
Updates the load snapshot menu.
Definition load.c:1002
static int load_sortComparePlayersName(const void *p1, const void *p2)
qsort compare function for files.
Definition load.c:507
int save_loaded
Definition save.c:30
static int load_sortComparePlayers(const void *p1, const void *p2)
qsort compare function for files.
Definition load.c:518
Handles the important game menus.
void menu_small_close(void)
Closes the small menu.
Definition menu.c:499
void menu_main(void)
Opens the main menu (titlescreen).
Definition menu.c:170
void menu_main_close(void)
Closes the main menu.
Definition menu.c:333
int missions_loadActive(xmlNodePtr parent)
Loads the player's active missions from a save.
Definition mission.c:1535
int missions_loadCommodity(xmlNodePtr parent)
Loads the player's special mission commodities.
Definition mission.c:1459
int naev_versionCompare(const char *version)
Compares the version against the current naev version.
Definition naev.c:1151
Uint32 SDL_LOOPDONE
Definition naev.c:99
Header file with generic functions and naev-specifics.
#define APPNAME
Definition naev.h:30
const char * naev_version(int long_version)
Returns the version in a human readable string.
#define ABS(x)
Definition naev.h:32
#define PATH_MAX
Definition naev.h:57
int ndata_copyIfExists(const char *file1, const char *file2)
Copy a file, if it exists.
Definition ndata.c:358
int scnprintf(char *text, size_t maxlen, const char *fmt,...)
Like snprintf(), but returns the number of characters ACTUALLY "printed" into the buffer....
Definition nstring.c:102
ntime_t ntime_create(int scu, int stp, int stu)
Creates a time structure.
Definition ntime.c:99
void ntime_prettyBuf(char *str, int max, ntime_t t, int d)
Gets the time in a pretty human readable format filling a preset buffer.
Definition ntime.c:194
void pilot_calcStats(Pilot *pilot)
Recalculates the pilot's stats based on his outfits.
int pilot_rmOutfitRaw(Pilot *pilot, PilotOutfitSlot *s)
Removes an outfit from the pilot without doing any checks.
int pilot_addOutfitRaw(Pilot *pilot, const Outfit *outfit, PilotOutfitSlot *s)
Adds an outfit to the pilot, ignoring CPU or other limits.
int player_nships(void)
Gets the amount of ships player has in storage.
Definition player.c:2814
int player_ships(char **sships, glTexture **tships)
Returns a buffer with all the player's ships names.
Definition player.c:2787
int player_addOutfit(const Outfit *o, int quantity)
Adds an outfit to the player outfit stack.
Definition player.c:2988
void player_cleanup(void)
Cleans up player stuff like player_stack.
Definition player.c:800
int player_addEscorts(void)
Adds the player's escorts.
Definition player.c:3288
Player_t player
Definition player.c:77
Pilot * player_getShip(const char *shipname)
Gets a specific ship.
Definition player.c:2844
const char * plugin_name(const plugin_t *plg)
Tries to tget the name of a plugin.
Definition plugin.c:315
static plugin_t * plugins
Definition plugin.c:26
const plugin_t * plugin_list(void)
Returns the list of all the plugins.
Definition plugin.c:396
static const double d[]
Definition rng.c:263
int save_all_with_name(const char *name)
Saves the current game.
Definition save.c:108
void shiplog_new(void)
Set up the shiplog.
Definition shiplog.c:382
int shiplog_load(xmlNodePtr parent)
Loads the logfiile.
Definition shiplog.c:434
int space_playerLoad(xmlNodePtr parent, const char *version)
Loads player's space properties from an XML node.
Definition space.c:4221
const char * start_chapter(void)
Gets the player's starting chapter.
Definition start.c:283
const char * start_gui(void)
Gets the module's starting ship was acquired.
Definition start.c:218
ShipOutfitSlot * sslot
Definition pilot.h:151
const Outfit * outfit
Definition pilot.h:149
The representation of an in-game pilot.
Definition pilot.h:263
PilotOutfitSlot ** outfits
Definition pilot.h:354
Ship outfit slot.
Definition ship.h:71
const Outfit * data
Definition ship.h:78
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition space.h:102
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:43
A naev save.
Definition load.h:25
uint64_t credits
Definition load.h:42
char * shipmodel
Definition load.h:48
char * difficulty
Definition load.h:44
PHYSFS_sint64 modtime
Definition load.h:29
char * shipname
Definition load.h:47
char * player_name
Definition load.h:27
ntime_t date
Definition load.h:41
SaveCompatibility compatible
Definition load.h:37
char * path
Definition load.h:28
char * spob
Definition load.h:40
char * chapter
Definition load.h:43
char ** plugins
Definition load.h:36
int ret
Definition load.h:50
char * version
Definition load.h:32
char * data
Definition load.h:33
unsigned int window_create(const char *name, const char *displayname, const int x, const int y, const int w, const int h)
Creates a window.
Definition toolkit.c:688
void window_setAccept(unsigned int wid, void(*accept)(unsigned int, const char *))
Sets the default accept function of the window.
Definition toolkit.c:846
void window_setCancel(unsigned int wid, void(*cancel)(unsigned int, const char *))
Sets the default cancel function of the window.
Definition toolkit.c:868
void toolkit_closeAll(void)
Closes all open toolkit windows.
Definition toolkit.c:1016
void window_onClose(unsigned int wid, void(*fptr)(unsigned int, const char *))
Sets the default close function of the window.
Definition toolkit.c:824
unsigned int window_get(const char *wdwname)
Gets the ID of a window.
Definition toolkit.c:662
void * window_getData(unsigned int wid)
Gets the custom data of a window.
Definition toolkit.c:923
void window_setData(unsigned int wid, void *data)
Sets custom data for a window.
Definition toolkit.c:906
int window_exists(const char *wdwname)
Checks to see if a window exists.
Definition toolkit.c:591
void window_destroy(unsigned int wid)
Kills the window.
Definition toolkit.c:1039
void unidiff_universeDefer(int enable)
Sets whether or not to defer universe change stuff.
Definition unidiff.c:2107