Permalink
Fetching contributors…
Cannot retrieve contributors at this time
625 lines (523 sloc) 15 KB
/*
* See Licensing and Copyright notice in naev.h
*/
/**
* @file load.c
*
* @brief Contains stuff to load a pilot or look up information about it.
*/
#include "load.h"
#include "naev.h"
#include "nxml.h"
#include "log.h"
#include "player.h"
#include "nfile.h"
#include "array.h"
#include "space.h"
#include "toolkit.h"
#include "menu.h"
#include "dialogue.h"
#include "event.h"
#include "news.h"
#include "mission.h"
#include "faction.h"
#include "gui.h"
#include "unidiff.h"
#include "nlua_var.h"
#include "land.h"
#include "hook.h"
#include "nstring.h"
#include "outfit.h"
#define LOAD_WIDTH 600 /**< Load window width. */
#define LOAD_HEIGHT 500 /**< Load window height. */
#define BUTTON_WIDTH 80 /**< Button width. */
#define BUTTON_HEIGHT 30 /**< Button height. */
static nsave_t *load_saves = NULL; /**< Array of save.s */
extern int save_loaded; /**< From save.c */
/*
* Prototypes.
*/
/* externs */
/* player.c */
extern Planet* player_load( xmlNodePtr parent ); /**< Loads player related stuff. */
/* mission.c */
extern int missions_loadActive( xmlNodePtr parent ); /**< Loads active missions. */
/* event.c */
extern int events_loadActive( xmlNodePtr parent );
/* news.c */
extern int news_loadArticles( xmlNodePtr parent );
/* nlua_var.c */
extern int var_load( xmlNodePtr parent ); /**< Loads mission variables. */
/* faction.c */
extern int pfaction_load( xmlNodePtr parent ); /**< Loads faction data. */
/* hook.c */
extern int hook_load( xmlNodePtr parent ); /**< Loads hooks. */
/* space.c */
extern int space_sysLoad( xmlNodePtr parent ); /**< Loads the space stuff. */
/* unidiff.c */
extern int diff_load( xmlNodePtr parent ); /**< Loads the universe diffs. */
/* static */
static void load_menu_update( unsigned int wid, char *str );
static void load_menu_close( unsigned int wdw, char *str );
static void load_menu_load( unsigned int wdw, char *str );
static void load_menu_delete( unsigned int wdw, char *str );
static int load_load( nsave_t *save, const char *path );
/**
* @brief Loads an individual save.
*/
static int load_load( nsave_t *save, const char *path )
{
xmlDocPtr doc;
xmlNodePtr root, parent, node, cur;
int scu, stp, stu;
char *version = NULL;
memset( save, 0, sizeof(nsave_t) );
/* Load the XML. */
doc = xmlParseFile(path);
if (doc == NULL) {
WARN("Unable to parse save path '%s'.", path);
return -1;
}
root = doc->xmlChildrenNode; /* base node */
if (root == NULL) {
WARN("Unable to get child node of save '%s'.",path);
xmlFreeDoc(doc);
return -1;
}
/* Save path. */
save->path = strdup(path);
/* Iterate inside the naev_save. */
parent = root->xmlChildrenNode;
do {
xml_onlyNodes(parent);
/* Info. */
if (xml_isNode(parent,"version")) {
node = parent->xmlChildrenNode;
do {
xmlr_strd(node,"naev",version);
xmlr_strd(node,"data",save->data);
} while (xml_nextNode(node));
continue;
}
if (xml_isNode(parent,"player")) {
/* Get name. */
xmlr_attr(parent,"name",save->name);
/* Parse rest. */
node = parent->xmlChildrenNode;
do {
xml_onlyNodes(node);
/* Player info. */
xmlr_strd(node,"location",save->planet);
xmlr_ulong(node,"credits",save->credits);
/* Time. */
if (xml_isNode(node,"time")) {
cur = node->xmlChildrenNode;
scu = stp = stu = 0;
do {
xmlr_int(cur,"SCU",scu);
xmlr_int(cur,"STP",stp);
xmlr_int(cur,"STU",stu);
} while (xml_nextNode(cur));
save->date = ntime_create( scu, stp, stu );
continue;
}
/* Ship info. */
if (xml_isNode(node,"ship")) {
xmlr_attr(node,"name",save->shipname);
xmlr_attr(node,"model",save->shipmodel);
continue;
}
} while (xml_nextNode(node));
continue;
}
} while (xml_nextNode(parent));
/* Handle version. */
if (version != NULL) {
naev_versionParse( save->version, version, strlen(version) );
free(version);
}
/* Clean up. */
xmlFreeDoc(doc);
return 0;
}
/**
* @brief Loads or refreshes saved games.
*/
int load_refresh (void)
{
char **files, buf[PATH_MAX], *tmp;
int nfiles, i, len;
int ok;
nsave_t *ns;
if (load_saves != NULL)
load_free();
load_saves = array_create( nsave_t );
/* load the saves */
files = nfile_readDir( &nfiles, "%ssaves", nfile_dataPath() );
for (i=0; i<nfiles; i++) {
len = strlen(files[i]);
/* no save or backup save extension */
if (((len < 5) || strcmp(&files[i][len-3],".ns")) &&
((len < 12) || strcmp(&files[i][len-10],".ns.backup"))) {
free(files[i]);
memmove( &files[i], &files[i+1], sizeof(char*) * (nfiles-i-1) );
nfiles--;
i--;
}
}
/* Make sure files are none. */
if (files == NULL)
return 0;
/* Make sure backups are after saves. */
for (i=0; i<nfiles-1; i++) {
len = strlen( files[i] );
/* Only interested in swapping backup with file after it if it's not backup. */
if ((len < 12) || strcmp( &files[i][len-10],".ns.backup" ))
continue;
/* Don't match. */
if (strncmp( files[i], files[i+1], (len-10) ))
continue;
/* Swap around. */
tmp = files[i];
files[i] = files[i+1];
files[i+1] = tmp;
}
/* Allocate and parse. */
ok = 0;
ns = NULL;
for (i=0; i<nfiles; i++) {
if (!ok)
ns = &array_grow( &load_saves );
nsnprintf( buf, sizeof(buf), "%ssaves/%s", nfile_dataPath(), files[i] );
ok = load_load( ns, buf );
}
/* If the save was invalid, array is 1 member too large. */
if (ok)
array_resize( &load_saves, array_size(load_saves)-1 );
/* Clean up memory. */
for (i=0; i<nfiles; i++)
free(files[i]);
free(files);
return 0;
}
/**
* @brief Frees loaded save stuff.
*/
void load_free (void)
{
int i;
nsave_t *ns;
if (load_saves != NULL) {
for (i=0; i<array_size(load_saves); i++) {
ns = &load_saves[i];
free(ns->path);
if (ns->name != NULL)
free(ns->name);
if (ns->data != NULL)
free(ns->data);
if (ns->planet != NULL)
free(ns->planet);
if (ns->shipname != NULL)
free(ns->shipname);
if (ns->shipmodel != NULL)
free(ns->shipmodel);
}
array_free( load_saves );
}
load_saves = NULL;
}
/**
* @brief Gets the list of loaded saves.
*/
nsave_t *load_getList( int *n )
{
if (load_saves == NULL) {
*n = 0;
return NULL;
}
*n = array_size( load_saves );
return load_saves;
}
/**
* @brief Opens the load game menu.
*/
void load_loadGameMenu (void)
{
unsigned int wid;
char **names, buf[PATH_MAX];
nsave_t *nslist, *ns;
int i, n, len;
/* window */
wid = window_create( "Load Game", -1, -1, LOAD_WIDTH, LOAD_HEIGHT );
window_setAccept( wid, load_menu_load );
window_setCancel( wid, load_menu_close );
/* Load loads. */
load_refresh();
/* load the saves */
nslist = load_getList( &n );
if (n > 0) {
names = malloc( sizeof(char*)*n );
for (i=0; i<n; i++) {
ns = &nslist[i];
len = strlen(ns->path);
if (strcmp(&ns->path[len-10],".ns.backup")==0) {
nsnprintf( buf, sizeof(buf), "%s \er(Backup)\e0", ns->name );
names[i] = strdup(buf);
}
else
names[i] = strdup( ns->name );
}
}
/* case there are no files */
else {
names = malloc(sizeof(char*));
names[0] = strdup("None");
n = 1;
}
/* Player text. */
window_addText( wid, -20, -40, 200, LOAD_HEIGHT-40-20-2*(BUTTON_HEIGHT+20),
0, "txtPilot", NULL, &cBlack, NULL );
window_addList( wid, 20, -50,
LOAD_WIDTH-200-60, LOAD_HEIGHT-110,
"lstSaves", names, n, 0, load_menu_update );
/* Buttons */
window_addButtonKey( wid, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnBack", "Back", load_menu_close, SDLK_b );
window_addButtonKey( wid, -20, 20 + BUTTON_HEIGHT+20, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnLoad", "Load", load_menu_load, SDLK_l );
window_addButton( wid, 20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnDelete", "Del", load_menu_delete );
}
/**
* @brief Closes the load game menu.
* @param wdw Window triggering function.
* @param str Unused.
*/
static void load_menu_close( unsigned int wdw, char *str )
{
(void)str;
window_destroy( wdw );
}
/**
* @brief Updates the load menu.
* @param wdw Window triggering function.
* @param str Unused.
*/
static void load_menu_update( unsigned int wid, char *str )
{
(void) str;
int pos;
nsave_t *ns;
int n;
char *save;
char buf[256], credits[ECON_CRED_STRLEN], date[64], version[256];
/* Make sure list is ok. */
save = toolkit_getList( wid, "lstSaves" );
if (strcmp(save,"None") == 0)
return;
/* Get position. */
pos = toolkit_getListPos( wid, "lstSaves" );
ns = load_getList( &n );
ns = &ns[pos];
/* Display text. */
credits2str( credits, ns->credits, 2 );
ntime_prettyBuf( date, sizeof(date), ns->date, 2 );
naev_versionString( version, sizeof(version), ns->version[0], ns->version[1], ns->version[2] );
nsnprintf( buf, sizeof(buf),
"\eDName:\n"
"\e0 %s\n"
"\eDVersion:\n"
"\e0 %s\n"
"\eDDate:\n"
"\e0 %s\n"
"\eDPlanet:\n"
"\e0 %s\n"
"\eDCredits:\n"
"\e0 %s\n"
"\eDShip Name:\n"
"\e0 %s\n"
"\eDShip Model:\n"
"\e0 %s",
ns->name, version, date, ns->planet,
credits, ns->shipname, ns->shipmodel );
window_modifyText( wid, "txtPilot", buf );
}
/**
* @brief Loads a new game.
* @param wdw Window triggering function.
* @param str Unused.
*/
static void load_menu_load( unsigned int wdw, char *str )
{
(void)str;
char *save;
int wid, pos;
nsave_t *ns;
int n;
char version[64];
int diff;
wid = window_get( "Load Game" );
save = toolkit_getList( wid, "lstSaves" );
if (strcmp(save,"None") == 0)
return;
pos = toolkit_getListPos( wid, "lstSaves" );
ns = load_getList( &n );
/* Check version. */
diff = naev_versionCompare( ns[pos].version );
if (ABS(diff) >= 2) {
naev_versionString( version, sizeof(version), ns[pos].version[0],
ns[pos].version[1], ns[pos].version[2] );
if (!dialogue_YesNo( "Save game version mismatch",
"Save game '%s' version does not match Naev version:\n"
" Save version: \er%s\e0\n"
" Naev version: \eD%s\e0\n"
"Are you sure you want to load this game? It may lose data.",
save, version, naev_version(0) ))
return;
}
/* Close menus before loading for proper rendering. */
load_menu_close(wdw, NULL);
/* Close the main menu. */
menu_main_close();
/* Try to load the game. */
if (load_game( ns[pos].path, diff )) {
/* Failed so reopen both. */
menu_main();
load_loadGameMenu();
}
}
/**
* @brief Deletes an old game.
* @param wdw Window to delete.
* @param str Unused.
*/
static void load_menu_delete( unsigned int wdw, char *str )
{
(void)str;
char *save;
int wid, pos;
nsave_t *ns;
int n;
wid = window_get( "Load Game" );
save = toolkit_getList( wid, "lstSaves" );
if (strcmp(save,"None") == 0)
return;
if (dialogue_YesNo( "Permanently Delete?",
"Are you sure you want to permanently delete '%s'?", save) == 0)
return;
/* Remove it. */
pos = toolkit_getListPos( wid, "lstSaves" );
ns = load_getList( &n );
remove( ns[pos].path ); /* remove is portable and will call unlink on linux. */
/* need to reload the menu */
load_menu_close(wdw, NULL);
load_loadGameMenu();
}
static void load_compatSlots (void)
{
/* Vars for loading old saves. */
int i,j;
char **sships;
glTexture **tships;
int nships;
Pilot *ship;
ShipOutfitSlot *sslot;
nships = player_nships();
sships = malloc(nships * sizeof(char*));
tships = malloc(nships * sizeof(glTexture*));
nships = player_ships( sships, tships );
ship = player.p;
for (i=-1; i<nships; i++) {
if (i >= 0)
ship = player_getShip( sships[i] );
/* Remove all outfits. */
for (j=0; j<ship->noutfits; j++) {
if (ship->outfits[j]->outfit != NULL) {
player_addOutfit( ship->outfits[j]->outfit, 1 );
pilot_rmOutfitRaw( ship, ship->outfits[j] );
}
/* Add default outfit. */
sslot = ship->outfits[j]->sslot;
if (sslot->data != NULL)
pilot_addOutfitRaw( ship, sslot->data, ship->outfits[j] );
}
pilot_calcStats( ship );
}
/* Clean up. */
for (i=0; i<nships; i++)
free(sships[i]);
free(sships);
free(tships);
}
/**
* @brief Actually loads a new game based on file.
*
* @param file File that contains the new game.
* @return 0 on success.
*/
int load_game( const char* file, int version_diff )
{
xmlNodePtr node;
xmlDocPtr doc;
Planet *pnt;
/* Make sure it exists. */
if (!nfile_fileExists(file)) {
dialogue_alert("Savegame file seems to have been deleted.");
return -1;
}
/* Load the XML. */
doc = xmlParseFile(file);
if (doc == NULL)
goto err;
node = doc->xmlChildrenNode; /* base node */
if (node == NULL)
goto err_doc;
/* Clean up possible stuff that should be cleaned. */
player_cleanup();
/* Welcome message - must be before space_init. */
player_message( "\egWelcome to "APPNAME"!" );
player_message( "\eg v%s", naev_version(0) );
/* Now begin to load. */
diff_load(node); /* Must load first to work properly. */
pfaction_load(node); /* Must be loaded before player so the messages show up properly. */
pnt = player_load(node);
/* Sanitize for new version. */
if (version_diff <= -2) {
WARN("Old version detected. Sanitizing ships for slots");
load_compatSlots();
}
/* Load more stuff. */
var_load(node);
missions_loadActive(node);
events_loadActive(node);
news_loadArticles( node );
hook_load(node);
space_sysLoad(node);
/* Initialize the economy. */
economy_init();
/* Check sanity. */
event_checkSanity();
/* Run the load event trigger. */
events_trigger( EVENT_TRIGGER_LOAD );
/* Create escorts in space. */
player_addEscorts();
/* Land the player. */
land( pnt, 1 );
/* Load the GUI. */
if (gui_load( gui_pick() )) {
if (player.p->ship->gui != NULL)
gui_load( player.p->ship->gui );
}
/* Sanitize the GUI. */
gui_setCargo();
gui_setShip();
xmlFreeDoc(doc);
/* Set loaded. */
save_loaded = 1;
return 0;
err_doc:
xmlFreeDoc(doc);
err:
WARN("Savegame '%s' invalid!", file);
return -1;
}