23#include "difficulty.h"
46#define LOAD_HEIGHT 530
48#define BUTTON_WIDTH 120
49#define BUTTON_HEIGHT 30
54typedef struct filedata {
59typedef struct player_saves_s {
66static int old_saves_detected = 0, player_warned = 0;
67static char *selected_player = NULL;
81extern int var_load( xmlNodePtr parent );
96static void load_menu_delete(
unsigned int wdw,
const char *str );
104static void move_old_save(
const char *path,
const char *fname,
const char *ext,
const char *new_name );
110static int load_enumerateCallbackPlayer(
void* data,
const char* origdir,
const char* fname );
111static int load_compatibilityTest(
const nsave_t *ns );
112static const char* load_compatibilityString(
const nsave_t *ns );
127 xmlNodePtr root, parent, node, cur;
128 int cycles, periods, seconds;
130 memset( save, 0,
sizeof(
nsave_t) );
135 WARN( _(
"Unable to parse save path '%s'."), path);
138 root = doc->xmlChildrenNode;
140 WARN( _(
"Unable to get child node of save '%s'."), path);
146 save->
path = strdup(path);
149 parent = root->xmlChildrenNode;
151 xml_onlyNodes(parent);
154 if (xml_isNode(parent,
"version")) {
155 node = parent->xmlChildrenNode;
157 xmlr_strd(node,
"naev", save->
version);
158 xmlr_strd(node,
"data", save->
data);
159 }
while (xml_nextNode(node));
163 else if (xml_isNode(parent,
"player")) {
167 node = parent->xmlChildrenNode;
172 xmlr_strd(node,
"location", save->
spob);
173 xmlr_ulong(node,
"credits", save->
credits);
174 xmlr_strd(node,
"chapter", save->
chapter);
175 xmlr_strd(node,
"difficulty", save->
difficulty);
178 if (xml_isNode(node,
"time")) {
179 cur = node->xmlChildrenNode;
180 cycles = periods = seconds = 0;
182 xmlr_int(cur,
"SCU", cycles);
183 xmlr_int(cur,
"STP", periods);
184 xmlr_int(cur,
"STU", seconds);
185 }
while (xml_nextNode(cur));
191 if (xml_isNode(node,
"ship")) {
192 xmlr_attr_strd(node,
"name", save->
shipname);
193 xmlr_attr_strd(node,
"model", save->
shipmodel);
196 }
while (xml_nextNode(node));
199 else if (xml_isNode(parent,
"plugins")) {
202 node = parent->xmlChildrenNode;
206 if (xml_isNode(node,
"plugin")) {
207 const char *name = xml_get(node);
211 WARN(_(
"Save '%s' has unnamed plugin node!"), path);
213 }
while (xml_nextNode(node));
216 }
while (xml_nextNode(parent));
246static int load_enumerateCallbackPlayer(
void* data,
const char* origdir,
const char* fname )
253 dir_len = strlen( origdir );
255 fmt = dir_len && origdir[dir_len-1]==
'/' ?
"%s%s" :
"%s/%s";
256 asprintf( &path, fmt, origdir, fname );
257 if (!PHYSFS_stat( path, &stat ))
258 WARN( _(
"PhysicsFS: Cannot stat %s: %s"), path,
259 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
261 else if (stat.filetype == PHYSFS_FILETYPE_REGULAR) {
266 ns.save_name = strdup( fname );
267 ns.save_name[ strlen(ns.save_name)-3 ] =
'\0';
268 ns.modtime = stat.modtime;
270 if (ps->name == NULL)
276 return PHYSFS_ENUM_OK;
285 char *path, *backup_path;
287 size_t dir_len, name_len;
290 dir_len = strlen( origdir );
291 name_len = strlen( fname );
293 fmt = dir_len && origdir[dir_len-1]==
'/' ?
"%s%s" :
"%s/%s";
294 asprintf( &path, fmt, origdir, fname );
295 if (!PHYSFS_stat( path, &stat ))
296 WARN( _(
"PhysicsFS: Cannot stat %s: %s"), path,
297 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
299 else if (stat.filetype == PHYSFS_FILETYPE_REGULAR) {
300 if ((name_len < 4 || strcmp( &fname[name_len-3],
".ns" )) && (name_len < 11 || strcmp( &fname[name_len-10],
".ns.backup" ))) {
302 return PHYSFS_ENUM_OK;
304 if (!PHYSFS_exists(
"saves-pre-0.10.0" ))
305 PHYSFS_mkdir(
"saves-pre-0.10.0" );
306 asprintf( &backup_path,
"saves-pre-0.10.0/%s", fname );
308 old_saves_detected = 1;
313 else if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY) {
317 PHYSFS_enumerate( path, load_enumerateCallbackPlayer, &psave );
318 if (psave.name!=NULL) {
327 return PHYSFS_ENUM_OK;
330static int load_compatibilityTest(
const nsave_t *ns )
332 char buf[STRMAX], buf2[STRMAX];
337 case SAVE_COMPATIBILITY_NAEV_VERSION:
339 _(
"Save game '%s' version does not match Naev version:\n"
340 " Save version: #r%s#0\n"
341 " Naev version: %s\n"
342 "Are you sure you want to load this game? It may lose data."),
347 case SAVE_COMPATIBILITY_PLUGINS:
351 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"%s%s", (l>0)?_(
", "):
"#r", ns->plugins[i] );
352 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"#0" );
358 _(
"Save game '%s' plugins do not match loaded plugins:\n"
359 " Save plugins: %s\n"
360 " Naev plugins: %s\n"
361 "Are you sure you want to load this game? It may lose data."),
366 case SAVE_COMPATIBILITY_OK:
373static const char* load_compatibilityString(
const nsave_t *ns )
376 case SAVE_COMPATIBILITY_NAEV_VERSION:
377 return _(
"version mismatch");
379 case SAVE_COMPATIBILITY_PLUGINS:
380 return _(
"plugins mismatch");
382 case SAVE_COMPATIBILITY_OK:
383 return _(
"compatible");
397 return SAVE_COMPATIBILITY_NAEV_VERSION;
408 return SAVE_COMPATIBILITY_PLUGINS;
411 return SAVE_COMPATIBILITY_OK;
441 if (ns1->modtime > ns2->modtime)
443 else if (ns1->modtime < ns2->modtime)
447 return strcmp( ns1->save_name, ns2->save_name );
514 names = malloc(
sizeof(
char*)*n );
515 for (
int i=0; i<n; i++) {
518 char buf[STRMAX_SHORT];
520 l +=
scnprintf( &buf[l],
sizeof(buf)-l, _(
"%s (#r%s#0)"),
522 names[i] = strdup( buf );
526 if (selected_player != NULL && !strcmp( names[i], selected_player ))
532 names = malloc(
sizeof(
char*));
533 names[0] = strdup(_(
"None"));
541 window_addList( wid, 20, -40,
553 "btnDelete", _(
"Delete"), load_menu_delete );
555 if (old_saves_detected && !player_warned) {
557 dialogue_alert( _(
"Naev has detected saves in pre-0.10.0 format, and has automatically migrated them to the new format. Old saves have been backed up at '%s'."),
"saves-pre-0.10.0");
582 WARN(_(
"Player '%s' not found in list of saves!"),name);
595 names = malloc(
sizeof(
char*)*n );
596 for (
int i=0; i<n; i++) {
599 char buf[STRMAX_SHORT];
601 l +=
scnprintf( &buf[l],
sizeof(buf)-l, _(
"%s (#r%s#0)"),
602 ns->save_name, load_compatibilityString( ns ) );
603 names[i] = strdup( buf );
606 names[i] = strdup( ns->save_name );
611 names = malloc(
sizeof(
char*));
612 names[0] = strdup(_(
"None"));
620 window_addList( wid, 20, -40,
635 window_disableButton( wid,
"btnSave" );
637 can_save =
landed && !player_isFlag(PLAYER_NOSAVE);
639 window_disableButton( wid,
"btnSave" );
653 pos = toolkit_getListPos( wdw,
"lstNames" );
666 char *save_name =
dialogue_input( _(
"Save game"), 1, 60, _(
"Please give the new snapshot a name:") );
667 if (save_name == NULL)
670 snprintf(path,
sizeof(path),
"saves/%s/%s.ns",
player.
name, save_name);
671 if (PHYSFS_exists( path )) {
673 _(
"You already have a snapshot named '%s'. Overwrite?"), save_name);
681 dialogue_alert( _(
"Failed to save the game! You should exit and check the log to see what happened and then file a bug report!") );
719 char buf[STRMAX_SHORT], credits[ECON_CRED_STRLEN], date[64], difficulty[STRMAX_SHORT];
724 snprintf( difficulty,
sizeof(difficulty), _(
"%s (options)"), _(
d->name) );
727 snprintf( difficulty,
sizeof(difficulty), _(
"%s (this save)"), _(ns->
difficulty) );
729 credits2str( credits, ns->
credits, 2 );
731 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"#n%s", _(
"Name:") );
733 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Version:") );
734 if (ns->
compatible == SAVE_COMPATIBILITY_NAEV_VERSION)
738 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Difficulty:") );
739 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#0 %s", difficulty );
740 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Date:") );
741 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#0 %s", date );
742 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Chapter:") );
744 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Spob:") );
745 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#0 %s", _(ns->
spob) );
746 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Credits:") );
747 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#0 %s", credits );
748 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Ship Name:") );
750 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Ship Model:") );
752 window_modifyText( wid,
"txtPilot", buf );
762static void move_old_save(
const char *path,
const char *fname,
const char *ext,
const char *new_name )
764 size_t name_len = strlen(fname);
765 size_t ext_len = strlen(ext);
766 if (name_len >= ext_len + 1 && !strcmp( &fname[name_len - ext_len], ext )) {
767 char *dirname = strdup( fname );
768 dirname[name_len - ext_len] =
'\0';
770 asprintf( &new_path,
"saves/%s", dirname );
771 if (!PHYSFS_exists( new_path ))
772 PHYSFS_mkdir( new_path );
774 asprintf( &new_path,
"saves/%s/%s", dirname, new_name );
776 if (PHYSFS_exists( new_path )) {
779 asprintf( &bkp_path,
"%s.bkp", new_path );
780 while (PHYSFS_exists(bkp_path) && (tries++ < 10)) {
782 asprintf( &bkp_bkp_path,
"%s.bkp", bkp_path );
784 bkp_path = bkp_bkp_path;
791 if (!PHYSFS_delete( path ))
814 pos = toolkit_getListPos( wid,
"lstNames" );
817 if (selected_player != NULL)
818 free( selected_player );
819 selected_player = strdup(
load_saves[pos].name );
840 pos = toolkit_getListPos( wid,
"lstSaves" );
859 pos = toolkit_getListPos( wid,
"lstNames" );
867 if (load_compatibilityTest( ns ))
899 pos = toolkit_getListPos( wid,
"lstSaves" );
902 if (load_compatibilityTest( &
load_player->saves[pos] ))
928static void load_menu_delete(
unsigned int wdw,
const char *str )
935 pos = toolkit_getListPos( wid,
"lstNames" );
941 _(
"Are you sure you want to permanently delete the character '%s'?\n#rThis is an undoable action!#0"),
load_saves[pos].name) == 0)
946 for (
int i = 0; i < n; i++)
947 if (!PHYSFS_delete(
load_saves[pos].saves[i].path ))
949 snprintf(path,
sizeof(path),
"saves/%s",
load_saves[pos].name);
950 if (!PHYSFS_delete( path ))
975 pos = toolkit_getListPos( wid,
"lstSaves" );
978 _(
"Are you sure you want to permanently delete the snapshot '%s'?\n#rThis is an undoable action!#0"),
load_player->saves[pos].save_name) == 0)
989 snprintf(path,
sizeof(path),
"saves/%s",
load_player->name);
990 if (!PHYSFS_delete( path ))
1007static void load_compatSlots (
void)
1016 sships = malloc(nships *
sizeof(
char*));
1017 tships = malloc(nships *
sizeof(
glTexture*));
1020 for (
int i=-1; i<nships; i++) {
1034 if (sslot->
data != NULL)
1042 for (
int i=0; i<nships; i++)
1060 if (!PHYSFS_exists( file )) {
1061 dialogue_alert( _(
"Saved game file seems to have been deleted.") );
1069 node = doc->xmlChildrenNode;
1084 WARN( _(
"Saved game '%s' invalid!"), file);
1122 if (!PHYSFS_exists( file )) {
1123 dialogue_alert( _(
"Saved game file seems to have been deleted.") );
1131 data = malloc(
sizeof(
const char*) * 2 );
1135 if ((
player.
p == NULL) || player_isFlag(PLAYER_DESTROYED))
1140 SDL_memset( &event, 0,
sizeof(event) );
1142 SDL_PushEvent( &event );
1158 const char **sdata = data;
1159 const char *file = sdata[0];
1160 const char *version = sdata[1];
1168 node = doc->xmlChildrenNode;
1189 if (version_diff <= -2) {
1190 WARN( _(
"Old version detected. Sanitizing ships for slots") );
1253 snprintf( buf,
sizeof(buf),
"%s/%s", PHYSFS_getWriteDir(), filename );
1254 return xmlParseFile( buf );
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
void dialogue_alert(const char *fmt,...)
Displays an alert popup with only an ok button and a message.
char * dialogue_input(const char *title, int min, int max, const char *fmt,...)
Creates a dialogue that allows the player to write a message.
int dialogue_YesNo(const char *caption, const char *fmt,...)
Runs a dialogue with both yes and no options.
int economy_init(void)
Initializes the economy.
void event_checkValidity(void)
Checks the event validity and cleans up after them.
void events_trigger(EventTrigger_t trigger)
Runs all the events matching a trigger.
int gui_load(const char *name)
Attempts to load the actual GUI.
char * gui_pick(void)
Determines which GUI should be used.
void gui_setShip(void)
Player just upgraded their ship or modified it.
void player_message(const char *fmt,...)
Adds a mesg to the queue to be displayed on screen.
void gui_setCargo(void)
Player just changed their cargo.
void hook_cleanup(void)
Gets rid of all current hooks.
unsigned int hook_addFunc(int(*func)(void *), void *data, const char *stack)
Adds a function hook to be run.
void land_cleanup(void)
Cleans up some land-related variables.
void land(Spob *p, int load)
Opens up all the land dialogue stuff.
static void load_menu_update(unsigned int wid, const char *str)
Updates the load menu.
int news_loadArticles(xmlNodePtr parent)
Loads the player's active articles from a save, initilizes news.
static void load_menu_close(unsigned int wdw, const char *str)
Closes the load game menu.
static void load_menu_load(unsigned int wdw, const char *str)
Loads a new game.
int space_sysLoad(xmlNodePtr parent)
Loads player's space properties from an XML node.
static int load_gameInternal(const char *file, const char *version)
Actually loads a new game.
int diff_load(xmlNodePtr parent)
Loads the diffs.
int events_loadActive(xmlNodePtr parent)
Loads the player's active events from a save.
static int load_game(nsave_t *ns)
Actually loads a new game based on save structure.
static int load_gameInternalHook(void *data)
Loads a game .Meant to be run in a function hook.
int economy_sysLoad(xmlNodePtr parent)
Loads player's economy properties from an XML node.
int hook_load(xmlNodePtr parent)
Loads hooks for a player.
static int load_sortCompare(const void *p1, const void *p2)
qsort compare function for files.
static int load_load(nsave_t *save, const char *path)
Loads an individual save.
int var_load(xmlNodePtr parent)
Loads the vars from XML file.
static SaveCompatibility load_compatibility(const nsave_t *ns)
Checks to see if a save is compatible with current Naev.
static void move_old_save(const char *path, const char *fname, const char *ext, const char *new_name)
Moves old Naev saves to subdirectories.
void load_loadSnapshotMenu(const char *name, int disablesave)
Opens the load snapshot menu.
int load_refresh(void)
Loads or refreshes saved games for the player.
static void display_save_info(unsigned int wid, const nsave_t *ns)
Displays Naev save info.
static void load_snapshot_menu_save(unsigned int wdw, const char *str)
Creates new custom snapshot.
static int load_enumerateCallback(void *data, const char *origdir, const char *fname)
The PHYSFS_EnumerateCallback for load_refresh.
static void load_menu_snapshots(unsigned int wdw, const char *str)
Opens the load snapshot menu.
void load_loadGameMenu(void)
Opens the load game menu.
static player_saves_t * load_saves
static void load_snapshot_menu_load(unsigned int wdw, const char *str)
Loads a new game.
void load_free(void)
Frees loaded save stuff.
const nsave_t * load_getList(const char *name)
Gets the array (array.h) of loaded saves.
static player_saves_t * load_player
static void load_snapshot_menu_delete(unsigned int wdw, const char *str)
Deletes an old game.
int pfaction_load(xmlNodePtr parent)
Loads the player's faction standings.
static void load_snapshot_menu_close(unsigned int wdw, const char *str)
Closes the load snapshot menu.
Spob * player_load(xmlNodePtr parent)
Loads the player stuff.
static xmlDocPtr load_xml_parsePhysFS(const char *filename)
Temporary (hopefully) wrapper around xml_parsePhysFS in support of gzipped XML (like ....
int load_gameFile(const char *file)
Loads the game from a file.
int load_gameDiff(const char *file)
Loads the diffs from game file.
static void load_snapshot_menu_update(unsigned int wid, const char *str)
Updates the load snapshot menu.
static int load_sortComparePlayers(const void *p1, const void *p2)
qsort compare function for files.
int missions_loadActive(xmlNodePtr parent)
Loads the player's active missions from a save.
int missions_loadCommodity(xmlNodePtr parent)
Loads the player's special mission commodities.
int naev_versionCompare(const char *version)
Compares the version against the current naev version.
Header file with generic functions and naev-specifics.
const char * naev_version(int long_version)
Returns the version in a human readable string.
int ndata_copyIfExists(const char *file1, const char *file2)
Copy a file, if it exists.
int asprintf(char **strp, const char *fmt,...)
Like sprintf(), but it allocates a large-enough string and returns the pointer in the first argument....
int scnprintf(char *text, size_t maxlen, const char *fmt,...)
Like snprintf(), but returns the number of characters ACTUALLY "printed" into the buffer....
ntime_t ntime_create(int scu, int stp, int stu)
Creates a time structure.
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.
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.
int player_ships(char **sships, glTexture **tships)
Returns a buffer with all the player's ships names.
int player_addOutfit(const Outfit *o, int quantity)
Adds an outfit to the player outfit stack.
void player_cleanup(void)
Cleans up player stuff like player_stack.
int player_addEscorts(void)
Adds the player's escorts.
Pilot * player_getShip(const char *shipname)
Gets a specific ship.
const char * plugin_name(const plugin_t *plg)
Tries to tget the name of a plugin.
const plugin_t * plugin_list(void)
Returns the list of all the plugins.
int save_all_with_name(const char *name)
Saves the current game.
void shiplog_new(void)
Set up the shiplog.
int shiplog_load(xmlNodePtr parent)
Loads the logfiile.
const char * start_chapter(void)
Gets the player's starting chapter.
The representation of an in-game pilot.
PilotOutfitSlot ** outfits
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Struct containing a file's name and stat structure.
Abstraction for rendering sprite sheets.
SaveCompatibility compatible
void unidiff_universeDefer(int enable)
Sets whether or not to defer universe change stuff.