23#include "difficulty.h"
47#define LOAD_HEIGHT 530
49#define BUTTON_WIDTH 120
50#define BUTTON_HEIGHT 30
52typedef struct player_saves_s {
59static int old_saves_detected = 0, player_warned = 0;
60static char *selected_player = NULL;
74extern int var_load( xmlNodePtr parent );
89static void load_menu_delete(
unsigned int wdw,
const char *str );
93static void load_snapshot_menu_onClose(
unsigned int wid,
const char *str );
98static void move_old_save(
const char *path,
const char *fname,
const char *ext,
const char *new_name );
104static int load_enumerateCallbackPlayer(
void* data,
const char* origdir,
const char* fname );
105static int load_compatibilityTest(
const nsave_t *ns );
106static const char* load_compatibilityString(
const nsave_t *ns );
121 xmlNodePtr root, parent, node, cur;
122 int cycles, periods, seconds;
124 memset( save, 0,
sizeof(
nsave_t) );
129 WARN( _(
"Unable to parse save path '%s'."), path);
132 root = doc->xmlChildrenNode;
134 WARN( _(
"Unable to get child node of save '%s'."), path);
140 save->
path = strdup(path);
143 parent = root->xmlChildrenNode;
145 xml_onlyNodes(parent);
148 if (xml_isNode(parent,
"version")) {
149 node = parent->xmlChildrenNode;
151 xmlr_strd(node,
"naev", save->
version);
152 xmlr_strd(node,
"data", save->
data);
153 }
while (xml_nextNode(node));
157 else if (xml_isNode(parent,
"player")) {
161 node = parent->xmlChildrenNode;
166 xmlr_strd(node,
"location", save->
spob);
167 xmlr_ulong(node,
"credits", save->
credits);
168 xmlr_strd(node,
"chapter", save->
chapter);
169 xmlr_strd(node,
"difficulty", save->
difficulty);
172 if (xml_isNode(node,
"time")) {
173 cur = node->xmlChildrenNode;
174 cycles = periods = seconds = 0;
176 xmlr_int(cur,
"SCU", cycles);
177 xmlr_int(cur,
"STP", periods);
178 xmlr_int(cur,
"STU", seconds);
179 }
while (xml_nextNode(cur));
185 if (xml_isNode(node,
"ship")) {
186 xmlr_attr_strd(node,
"name", save->
shipname);
187 xmlr_attr_strd(node,
"model", save->
shipmodel);
190 }
while (xml_nextNode(node));
193 else if (xml_isNode(parent,
"plugins")) {
196 node = parent->xmlChildrenNode;
200 if (xml_isNode(node,
"plugin")) {
201 const char *name = xml_get(node);
205 WARN(_(
"Save '%s' has unnamed plugin node!"), path);
207 }
while (xml_nextNode(node));
210 }
while (xml_nextNode(parent));
240static int load_enumerateCallbackPlayer(
void* data,
const char* origdir,
const char* fname )
247 dir_len = strlen( origdir );
249 fmt = dir_len && origdir[dir_len-1]==
'/' ?
"%s%s" :
"%s/%s";
250 SDL_asprintf( &path, fmt, origdir, fname );
251 if (!PHYSFS_stat( path, &stat ))
252 WARN( _(
"PhysicsFS: Cannot stat %s: %s"), path,
253 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
255 else if (stat.filetype == PHYSFS_FILETYPE_REGULAR) {
260 ns.save_name = strdup( fname );
261 ns.save_name[ strlen(ns.save_name)-3 ] =
'\0';
264 if (ps->name == NULL)
270 return PHYSFS_ENUM_OK;
279 char *path, *backup_path;
281 size_t dir_len, name_len;
284 dir_len = strlen( origdir );
285 name_len = strlen( fname );
287 fmt = dir_len && origdir[dir_len-1]==
'/' ?
"%s%s" :
"%s/%s";
288 SDL_asprintf( &path, fmt, origdir, fname );
289 if (!PHYSFS_stat( path, &stat ))
290 WARN( _(
"PhysicsFS: Cannot stat %s: %s"), path,
291 _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
293 else if (stat.filetype == PHYSFS_FILETYPE_REGULAR) {
294 if ((name_len < 4 || strcmp( &fname[name_len-3],
".ns" )) && (name_len < 11 || strcmp( &fname[name_len-10],
".ns.backup" ))) {
296 return PHYSFS_ENUM_OK;
298 if (!PHYSFS_exists(
"saves-pre-0.10.0" ))
299 PHYSFS_mkdir(
"saves-pre-0.10.0" );
300 SDL_asprintf( &backup_path,
"saves-pre-0.10.0/%s", fname );
302 old_saves_detected = 1;
307 else if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY) {
311 PHYSFS_enumerate( path, load_enumerateCallbackPlayer, &psave );
312 if (psave.name!=NULL) {
321 return PHYSFS_ENUM_OK;
324static int load_compatibilityTest(
const nsave_t *ns )
326 char buf[STRMAX], buf2[STRMAX];
331 case SAVE_COMPATIBILITY_NAEV_VERSION:
333 _(
"Save game '%s' version does not match Naev version:\n"
334 " Save version: #r%s#0\n"
335 " Naev version: %s\n"
336 "Are you sure you want to load this game? It may lose data."),
341 case SAVE_COMPATIBILITY_PLUGINS:
345 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"%s%s", (l>0)?p_(
"plugins",
", "):
"#r", ns->
plugins[i] );
346 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"#0" );
350 l +=
scnprintf( &buf2[l],
sizeof(buf2)-l,
"%s%s", (l>0)?p_(
"plugins",
", "):
"",
plugin_name(&plugins[i]) );
352 _(
"Save game '%s' plugins do not match loaded plugins:\n"
353 " Save plugins: %s\n"
354 " Naev plugins: %s\n"
355 "Are you sure you want to load this game? It may lose data."),
360 case SAVE_COMPATIBILITY_OK:
367static const char* load_compatibilityString(
const nsave_t *ns )
370 case SAVE_COMPATIBILITY_NAEV_VERSION:
371 return _(
"version mismatch");
373 case SAVE_COMPATIBILITY_PLUGINS:
374 return _(
"plugins mismatch");
376 case SAVE_COMPATIBILITY_OK:
377 return _(
"compatible");
391 return SAVE_COMPATIBILITY_NAEV_VERSION;
402 return SAVE_COMPATIBILITY_PLUGINS;
405 return SAVE_COMPATIBILITY_OK;
441 return strcmp( ns1->save_name, ns2->save_name );
508 names = malloc(
sizeof(
char*)*n );
509 for (
int i=0; i<n; i++) {
512 char buf[STRMAX_SHORT];
513 scnprintf( buf,
sizeof(buf), _(
"%s (#r%s#0)"),
515 names[i] = strdup( buf );
519 if (selected_player != NULL && !strcmp( names[i], selected_player ))
525 names = malloc(
sizeof(
char*));
526 names[0] = strdup(_(
"None"));
534 window_addList( wid, 20, -40,
546 "btnDelete", _(
"Delete"), load_menu_delete );
548 if (old_saves_detected && !player_warned) {
549 char buf[STRMAX_SHORT];
550 snprintf( buf,
sizeof(buf),
"%s%s", PHYSFS_getRealDir(
"saves-pre-0.10.0"),
"saves-pre-0.10.0" );
551 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'."), buf );
556static void load_snapshot_menu_onClose(
unsigned int wid,
const char *str )
584 WARN(_(
"Player '%s' not found in list of saves!"),name);
590 free( selected_player );
598 data = malloc(
sizeof(
int ) );
606 names = malloc(
sizeof(
char*)*n );
607 for (
int i=0; i<n; i++) {
610 char buf[STRMAX_SHORT];
611 scnprintf( buf,
sizeof(buf), _(
"%s (#r%s#0)"),
612 ns->save_name, load_compatibilityString( ns ) );
613 names[i] = strdup( buf );
616 names[i] = strdup( ns->save_name );
621 names = malloc(
sizeof(
char*));
622 names[0] = strdup(_(
"None"));
630 window_addList( wid, 20, -40,
645 window_disableButton( wid,
"btnSave" );
647 int can_save =
landed && !player_isFlag(PLAYER_NOSAVE);
649 window_disableButton( wid,
"btnSave" );
663 pos = toolkit_getListPos( wdw,
"lstNames" );
676 char *save_name =
dialogue_input( _(
"Save game"), 1, 60, _(
"Please give the new snapshot a name:") );
677 if (save_name == NULL)
680 snprintf(path,
sizeof(path),
"saves/%s/%s.ns",
player.
name, save_name);
681 if (PHYSFS_exists( path )) {
683 _(
"You already have a snapshot named '%s'. Overwrite?"), save_name);
691 dialogue_alert( _(
"Failed to save the game! You should exit and check the log to see what happened and then file a bug report!") );
730 char buf[STRMAX_SHORT], credits[ECON_CRED_STRLEN], date[64], difficulty[STRMAX_SHORT];
735 snprintf( difficulty,
sizeof(difficulty), _(
"%s (options)"), _(
d->name) );
738 snprintf( difficulty,
sizeof(difficulty), _(
"%s (this save)"), _(ns->
difficulty) );
742 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"#n%s", _(
"Name:") );
744 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Version:") );
745 if (ns->
compatible == SAVE_COMPATIBILITY_NAEV_VERSION)
749 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Difficulty:") );
750 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#0 %s", difficulty );
751 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Date:") );
752 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#0 %s", date );
753 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Chapter:") );
755 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Space Object:") );
756 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#0 %s", _(ns->
spob) );
757 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Credits:") );
758 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#0 %s", credits );
759 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Ship Name:") );
761 l +=
scnprintf( &buf[l],
sizeof(buf)-l,
"\n#n%s", _(
"Ship Model:") );
763 window_modifyText( wid,
"txtPilot", buf );
773static void move_old_save(
const char *path,
const char *fname,
const char *ext,
const char *new_name )
775 size_t name_len = strlen(fname);
776 size_t ext_len = strlen(ext);
777 if (name_len >= ext_len + 1 && !strcmp( &fname[name_len - ext_len], ext )) {
778 char *dirname = strdup( fname );
779 dirname[name_len - ext_len] =
'\0';
781 SDL_asprintf( &new_path,
"saves/%s", dirname );
782 if (!PHYSFS_exists( new_path ))
783 PHYSFS_mkdir( new_path );
785 SDL_asprintf( &new_path,
"saves/%s/%s", dirname, new_name );
787 if (PHYSFS_exists( new_path )) {
790 SDL_asprintf( &bkp_path,
"%s.bkp", new_path );
791 while (PHYSFS_exists(bkp_path) && (tries++ < 10)) {
793 SDL_asprintf( &bkp_bkp_path,
"%s.bkp", bkp_path );
795 bkp_path = bkp_bkp_path;
802 if (!PHYSFS_delete( path ))
825 pos = toolkit_getListPos( wid,
"lstNames" );
828 if (selected_player != NULL)
829 free( selected_player );
830 selected_player = strdup(
load_saves[pos].name );
851 pos = toolkit_getListPos( wid,
"lstSaves" );
870 pos = toolkit_getListPos( wid,
"lstNames" );
878 if (load_compatibilityTest( ns ))
910 pos = toolkit_getListPos( wid,
"lstSaves" );
913 if (load_compatibilityTest( &
load_player->saves[pos] ))
936static void load_menu_delete(
unsigned int wdw,
const char *str )
943 pos = toolkit_getListPos( wid,
"lstNames" );
949 _(
"Are you sure you want to permanently delete the character '%s'?\n#rThis is an undoable action!#0"),
load_saves[pos].name) == 0)
954 for (
int i = 0; i < n; i++)
955 if (!PHYSFS_delete(
load_saves[pos].saves[i].path ))
957 snprintf(path,
sizeof(path),
"saves/%s",
load_saves[pos].name);
958 if (!PHYSFS_delete( path ))
983 pos = toolkit_getListPos( wid,
"lstSaves" );
986 _(
"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)
997 snprintf(path,
sizeof(path),
"saves/%s",
load_player->name);
998 if (!PHYSFS_delete( path ))
1016static void load_compatSlots (
void)
1025 sships = malloc(nships *
sizeof(
char*));
1026 tships = malloc(nships *
sizeof(
glTexture*));
1029 for (
int i=-1; i<nships; i++) {
1043 if (sslot->
data != NULL)
1051 for (
int i=0; i<nships; i++)
1069 if (!PHYSFS_exists( file )) {
1070 dialogue_alert( _(
"Saved game file seems to have been deleted.") );
1078 node = doc->xmlChildrenNode;
1093 WARN( _(
"Saved game '%s' invalid!"), file);
1131 if (!PHYSFS_exists( file )) {
1132 dialogue_alert( _(
"Saved game file seems to have been deleted.") );
1140 data = malloc(
sizeof(
const char*) * 2 );
1144 if ((
player.
p == NULL) || player_isFlag(PLAYER_DESTROYED))
1149 SDL_memset( &event, 0,
sizeof(event) );
1151 SDL_PushEvent( &event );
1167 int misn_failed = 0, evt_failed = 0;
1168 const char **sdata = data;
1169 const char *file = sdata[0];
1170 const char *version = sdata[1];
1178 node = doc->xmlChildrenNode;
1185 render_postprocessCleanup();
1200 if (version_diff <= -2) {
1201 WARN( _(
"Old version detected. Sanitizing ships for slots") );
1243 if (misn_failed || evt_failed) {
1246 const char **misn_failed_str = mission_loadFailed();
1247 l +=
scnprintf( &buf[l],
sizeof(buf)-l, _(
"Saved game '%s' failed to load some missions/events properly!"), file);
1249 l +=
scnprintf( &buf[l],
sizeof(buf)-l, _(
"\nIn particular, the following missions have failed to load and been removed:"));
1250 for (
int i=0; i<
array_size(misn_failed_str); i++)
1251 l +=
scnprintf( &buf[l],
sizeof(buf)-l, _(
"\n #r%s#0"), misn_failed_str[i]);
1253 scnprintf( &buf[l],
sizeof(buf)-l, _(
"\nNote that, in general, you should be able to find the missions/events again and start them without penalty."));
1276 snprintf( buf,
sizeof(buf),
"%s/%s", PHYSFS_getWriteDir(), filename );
1277 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 credits2str(char *str, credits_t credits, int decimals)
Converts credits to a usable string for displaying.
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.
void dialogue_alertRaw(const char *msg)
Displays an alert popup with only an ok button and 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.
const char * gui_pick(void)
Determines which GUI should be used.
int gui_load(const char *name)
Attempts to load the actual GUI.
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_clear(void)
Clears the hooks.
unsigned int hook_addFunc(int(*func)(void *), void *data, const char *stack)
Adds a function hook to be run.
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_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 int load_game(const nsave_t *ns)
Actually loads a new game based on save structure.
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 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.
const char * start_gui(void)
Gets the module's starting ship was acquired.
The representation of an in-game pilot.
PilotOutfitSlot ** outfits
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Abstraction for rendering sprite sheets.
SaveCompatibility compatible
void unidiff_universeDefer(int enable)
Sets whether or not to defer universe change stuff.