/*
 * static char *rcsid_login_c =
 *   "$Id: login.c,v 1.49 1998/01/05 10:51:51 master Exp master $";
 */

/*
    CrossFire, A Multiplayer game for X-windows

    Copyright (C) 1992 Frank Tore Johansen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    The author can be reached via e-mail to frankj@ifi.uio.no.
*/

#include <global.h>
#ifndef __CEXTRACT__
#include <sproto.h>
#endif
#include <spells.h>

extern spell spells[NROFREALSPELLS];
extern void sub_weight (object *, signed long);
extern void add_weight (object *, signed long);
extern char *uncomp[NROF_COMPRESS_METHODS][3];
extern char *range_name[range_size];
extern long pticks;

/* If flag is non zero, it means that we want to try and save everyone, but
 * keep the game running.  Thus, we don't want to free any information.
 */
void emergency_save(int flag) {
  player *pl;
#ifndef NO_EMERGENCY_SAVE
  trying_emergency_save = 1;
  if(editor)
    return;
  LOG(llevError,"Emergency save:  ");
  for(pl=first_player;pl!=NULL;pl=pl->next) {
    if(!pl->ob) {
      LOG(llevError, "No name, ignoring this.\n");
      continue;
    }
    LOG(llevError,"%s ",pl->ob->name);
    new_draw_info(NDI_UNIQUE, 0,pl->ob,"Emergency save...");

/* If we are not exiting the game (ie, this is sort of a backup save), then
 * don't change the location back to the village.  Note that there are other
 * options to have backup saves be done at the starting village
 */
    if (!flag) {
	strcpy(pl->maplevel, first_map_path);
	if(pl->ob->map!=NULL)
	    pl->ob->map = NULL;
	pl->ob->x = -1;
	pl->ob->y = -1;
    }
    if(!save_player(pl->ob,flag)) {
      LOG(llevError, "(failed) ");
      new_draw_info(NDI_UNIQUE, 0,pl->ob,"Emergency save failed, checking score...");
    }
    check_score(pl->ob);
  }
  LOG(llevError,"\n");
#else
  LOG(llevError,"Emergency saves disabled, no save attempted\n");
#endif
  /* After we complete the emergency saves, then try and free
   * the pixmaps.  Without this effort, some X-Terminals (and maybe
   * even workstations) XServers will not free the space used by
   * the pixmaps.  This is more of a problem for the X-Terminals,
   * because they don't have a lot of memory.  The pointer is set
   * to NULL after we are finished to prevent another effort later.
   */
  if (!flag) {
    for(pl=first_player;pl!=NULL;pl=pl->next) {
      if(!pl->ob) {
	LOG(llevError, "No name, ignoring this.\n");
	continue;
      }
      if (pl->pixmaps) {
	free_pixmaps(pl->gdisp, pl->pixmaps);
	pl->pixmaps=NULL;
      }
      if (pl->masks) {
	free_pixmaps(pl->gdisp, pl->masks);
	pl->masks =NULL;
      }
    }
  }
}

void delete_character(char *name) {
  char buf[MAX_BUF];

#ifdef SAVE_HOMEDIR
  sprintf(buf,"%s/%s/%s.pl",(char *) getenv("HOME"),settings.playerdir,name);
#else
  sprintf(buf,"%s/%s/%s.pl",settings.libdir,settings.playerdir,name);
#endif
  if(unlink(buf)== -1 && settings.debug)
    perror("crossfire (delete character)");
}

void remove_lock(player *pl) {
#ifdef LOCK_PLAYER
  char buf[MAX_BUF];

  if(pl->ob == NULL)
    return;
#ifdef SAVE_HOMEDIR
  sprintf(buf, "%s/%s", (char *) getenv("HOME"), settings.playerdir);
  (void) create_savedir_if_needed(buf);
  
  sprintf(buf,"%s/%s/%s.lock",(char *) getenv("HOME"),settings.playerdir,pl->ob->name);
#else
  sprintf(buf,"%s/%s/%s.lock",settings.libdir,settings.playerdir,pl->ob->name);
#endif
  if(!rmdir(buf)) {
#ifdef DEBUG
    perror("Couldn't remove lockfile(dir)");
#endif
  }
#endif
}

int lock_player(char *name) {
#ifdef LOCK_PLAYER
  char buf[MAX_BUF];

#ifdef SAVE_HOMEDIR
  sprintf(buf, "%s/%s", (char *) getenv("HOME"), settings.playerdir);
  (void) create_savedir_if_needed(buf);
  
  sprintf(buf,"%s/%s/%s.lock",(char *) getenv("HOME"),settings.playerdir,name);
#else
  sprintf(buf,"%s/%s/%s.lock",settings.libdir,settings.playerdir,name);
#endif
  if(!mkdir(buf,0770))
    return 0;
  if(errno != EEXIST) {
    perror("Couldn't create lockfile(dir)");
    return 1;
  }
  return 1;
#else
  return 0;
#endif
}

int check_name(player *me,char *name) {
  player *pl;

#ifdef ONE_PLAYER_PR_UID
  if (me->username == NULL)
    LOG(llevError, "Player had no username.\n");
  else
    for (pl = first_player; pl != NULL; pl = pl->next)
      if (pl != me && pl->username != NULL &&
          !strcmp(pl->username, me->username)) {
        new_draw_info(NDI_UNIQUE, 0,me->ob, "You are already playing the game.");
        return 0;
      }
#endif
  for(pl=first_player;pl!=NULL;pl=pl->next)
    if(pl!=me&&pl->ob->name!=NULL&&!strcmp(pl->ob->name,name)) {
      new_draw_info(NDI_UNIQUE, 0,me->ob,"That name is already in use.");
      return 0;
    }
  if(lock_player(name)) {
    new_draw_info(NDI_UNIQUE, 0,me->ob,"That name is already in use.");
    return 0;
  }
  if(!playername_ok(name)) {
    new_draw_info(NDI_UNIQUE, 0,me->ob,"That name contains illegal characters.");
    remove_lock(me);
    return 0;
  }
  return 1;
}

int create_savedir_if_needed(char *savedir)
{
  struct stat *buf;

  if ((buf = (struct stat *) malloc(sizeof(struct stat))) == NULL) {
    perror("Unable to save playerfile... out of memory.");
    return 0;
  } else {
    stat(savedir, buf);
    if ((buf->st_mode & S_IFDIR) == 0)
#if defined(_IBMR2) || defined(___IBMR2)
      if (mkdir(savedir, S_ISUID|S_ISGID|S_IRUSR|S_IWUSR|S_IXUSR))
#else
      if (mkdir(savedir, S_ISUID|S_ISGID|S_IREAD|S_IWRITE|S_IEXEC))
#endif
	{
	perror("Unable to create player savedir,");
	perror(savedir);
	return 0;
      }
    free(buf);
  }
 return 1;
}

void destroy_object (object *op)
{
    object *tmp;
    while ((tmp = op->inv))
	destroy_object (tmp);

    if (!QUERY_FLAG(op, FLAG_REMOVED))
	remove_ob(op);
    free_object(op);
}

/*
 * If flag is set, it's only backup, ie dont remove objects from inventory
 * If BACKUP_SAVE_AT_HOME is set, and the flag is set, then the player
 * will be saved at the emergency save location.
 * Returns non zero if successful.
 */

int save_player(object *op, int flag) {
  FILE *fp;
  char filename[MAX_BUF], *tmpfilename,backupfile[MAX_BUF];
  object *tmp, *container=NULL;
  player *pl = op->contr;
  int i,wiz=QUERY_FLAG(op,FLAG_WIZ);
  long checksum;
#ifdef BACKUP_SAVE_AT_HOME
  sint16 backup_x, backup_y;
#endif

  flag&=1;

  if(!pl->name_changed||(!flag&&!op->stats.exp)) {
    if(!flag) {
      new_draw_info(NDI_UNIQUE, 0,op,"Your game is not valid,");
      new_draw_info(NDI_UNIQUE, 0,op,"Game not saved.");
    }
    return 0;
  }

  if (flag == 0)
    terminate_all_pets(op);

#ifdef SAVE_HOMEDIR
  sprintf(filename,"%s/%s", (char *) getenv("HOME"), settings.playerdir);
  (void) create_savedir_if_needed(filename);
  
  sprintf(filename,"%s/%s/%s.pl",(char *) getenv("HOME"),settings.playerdir,op->name);
#else
  sprintf(filename,"%s/%s/%s.pl",settings.libdir,settings.playerdir,op->name);
#endif
  tmpfilename = tempnam_local(settings.tmpdir,NULL);
  fp=fopen(tmpfilename, "w");
  if(!fp) {
    new_draw_info(NDI_UNIQUE, 0,op, "Can't open file for save.");
    LOG(llevDebug,"Can't open file for save.");
    free(tmpfilename);
    return 0;
  }

/* Eneq(@csd.uu.se): If we have an open container hide it. */
   if (op->container)  {
     container=op->container;
       op->container=NULL;
   }

  fprintf(fp,"password %s\n",pl->password);
#ifdef SET_TITLE
  if(pl->own_title[0]!='\0')
    fprintf(fp,"title %s\n",pl->own_title);
#endif /* SET_TITLE */
#ifdef SAVE_WINDOW_POSITIONS
  if(pl->valid_save_positions) {
    fprintf(fp,"win_pos");
    for(i=0;i<6;i++)
      fprintf(fp," %d %d %u %u",
	      pl->win_pos[i].x,pl->win_pos[i].y,
	      pl->win_pos[i].w,pl->win_pos[i].h);
    fprintf(fp,"\n");
  }
#endif /* SAVE_WINDOW_POSITIONS */
#ifdef EXPLORE_MODE
  fprintf(fp,"explore %d\n",pl->explore);
#endif
  fprintf(fp,"gen_hp %d\n",pl->gen_hp);
  fprintf(fp,"gen_sp %d\n",pl->gen_sp);
  fprintf(fp,"gen_grace %d\n",pl->gen_grace);
  fprintf(fp,"listening %d\n",pl->listening);
  fprintf(fp,"spell %d\n",pl->chosen_spell);
  fprintf(fp,"shoottype %d\n",pl->shoottype);
  fprintf(fp,"berzerk %d\n",pl->berzerk);
  fprintf(fp,"peaceful %d\n",pl->peaceful);
  fprintf(fp,"scroll %d\n",pl->scroll);
  fprintf(fp,"digestion %d\n",pl->digestion);
  fprintf(fp,"pickup %d\n", pl->mode);
  fprintf(fp,"keyboard_flush %d\n", pl->keyboard_flush);
  fprintf(fp,"show_inv_icon %d\n", pl->show_inv_icon);
  fprintf(fp,"outputs_sync %d\n", pl->outputs_sync);
  fprintf(fp,"outputs_count %d\n", pl->outputs_count);
  fprintf(fp,"sort_inventory %d\n", pl->sort_inventory);
  fprintf(fp,"show_buttons %d\n", pl->show_buttons);

#ifdef BACKUP_SAVE_AT_HOME
  if (op->map!=NULL && flag==0)
#else
  if (op->map!=NULL)
#endif
    fprintf(fp,"map %s\n",op->map->path);
  else
    fprintf(fp,"map %s\n",first_map_path);
  fprintf(fp,"weapon_sp %f\n",pl->weapon_sp);
  fprintf(fp,"Str %d\n",pl->orig_stats.Str);
  fprintf(fp,"Dex %d\n",pl->orig_stats.Dex);
  fprintf(fp,"Con %d\n",pl->orig_stats.Con);
  fprintf(fp,"Int %d\n",pl->orig_stats.Int);
  fprintf(fp,"Pow %d\n",pl->orig_stats.Pow);
  fprintf(fp,"Wis %d\n",pl->orig_stats.Wis);
  fprintf(fp,"Cha %d\n",pl->orig_stats.Cha);

  dump_keys(pl, fp);

  fprintf(fp,"lev_array %d\n",op->level>10?10:op->level);
  for(i=1;i<=pl->last_level&&i<=10;i++) {
    fprintf(fp,"%d\n",pl->levhp[i]);
    fprintf(fp,"%d\n",pl->levsp[i]);
	 fprintf(fp,"%d\n",pl->levgrace[i]);
  }
  for(i=0;i<pl->nrofknownspells;i++)
    fprintf(fp,"known_spell %s\n",spells[pl->known_spells[i]].name);
  fprintf(fp,"endplst\n");

  pl->freeze_inv = 1;
  SET_FLAG(op, FLAG_NO_FIX_PLAYER);
  CLEAR_FLAG(op, FLAG_WIZ);
#ifdef BACKUP_SAVE_AT_HOME
  if (flag) {
    backup_x = op->x;
    backup_y = op->y;
    op->x = -1;
    op->y = -1;
  }
  /* Save objects, but not unpaid objects.  Don't remove objects from
   * inventory.
   */
  save_object(fp, op, 2);
  if (flag) {
    op->x = backup_x;
    op->y = backup_y;
  }
#else
  save_object(fp, op, 3); /* don't check and don't remove */
#endif

  CLEAR_FLAG(op, FLAG_NO_FIX_PLAYER);

  if(!flag)
      while ((tmp = op->inv))
	  destroy_object (tmp);

  if (fclose(fp) == EOF) {	/* make sure the write succeeded */
	new_draw_info(NDI_UNIQUE, 0,op, "Can't save character.");
	unlink(tmpfilename);
	free(tmpfilename);
	return 0;
  }
  checksum = calculate_checksum(tmpfilename, 0);
  sprintf(backupfile, "%s.tmp", filename);
  rename(filename, backupfile);
  fp = fopen(filename,"w");
  if(!fp) {
    new_draw_info(NDI_UNIQUE, 0,op, "Can't open file for save.");
    unlink(tmpfilename);
    free(tmpfilename);
    return 0;
  }
  fprintf(fp,"checksum %lx\n",checksum);
  copy_file(tmpfilename, fp);
  unlink(tmpfilename);
  free(tmpfilename);
  if (fclose(fp) == EOF) {	/* got write error */
	new_draw_info(NDI_UNIQUE, 0,op, "Can't close file for save.");
	rename(backupfile, filename); /* Restore the original */
	return 0;
  }
  else
	unlink(backupfile);

  pl->freeze_inv=0;

  /* Eneq(@csd.uu.se): Reveal the container if we have one. */
  if (flag&&container!=NULL) 
    op->container = container;

  if (wiz) SET_FLAG(op,FLAG_WIZ);
  if(!flag)
    if (op->contr->eric_server > 0)
	esrv_send_inventory(op, op);
    else
	draw_all_inventory(op);

  chmod(filename,SAVE_MODE);
  return 1;
}

/*
 * calculate_checksum:
 * Evil scheme to avoid tampering with the player-files 8)
 * The cheat-flag will be set if the file has been changed.
 */

long calculate_checksum(char *filename, int checkdouble) {
#ifdef USE_CHECKSUM
  long checksum = 0;
  int offset = 0;
  FILE *fp;
  char buf[MAX_BUF], *cp;
  if ((fp = fopen(filename,"r")) == NULL)
    return 0;
  while(fgets(buf,MAX_BUF,fp)) {
    if(checkdouble && !strncmp(buf,"checksum",8))
      continue;
    for(cp=buf;*cp;cp++) {
      if(++offset>28)
        offset = 0;
      checksum^=(*cp<<offset);
    }
  }
  fclose(fp);
  return checksum;
#else
  return 0;
#endif
}

void copy_file(char *filename, FILE *fpout) {
  FILE *fp;
  char buf[MAX_BUF];
  if((fp = fopen(filename,"r")) == NULL)
    return;
  while(fgets(buf,MAX_BUF,fp)!=NULL)
    fputs(buf,fpout);
  fclose(fp);
}

#if 1
static int spell_sort(const void *a1,const void *a2)
{
  return strcmp(spells[(int)*(sint16 *)a1].name,spells[(int)*(sint16 *)a2].name);
}
#else
static int spell_sort(const char *a1,const char *a2)
{
   fprintf(stderr, "spell1=%d, spell2=%d\n", *(sint16*)a1, *(sint16*)a2);
  return strcmp(spells[(int )*a1].name,spells[(int )*a2].name);
}
#endif

void check_login(object *op) {
  FILE *fp;
  char filename[MAX_BUF];
  char buf[MAX_BUF],bufall[MAX_BUF];
  int i,value,x,y,comp;
  long checksum = 0;
  player *pl = op->contr;

  strcpy (pl->maplevel, first_map_path);
#ifdef SAVE_HOMEDIR
  sprintf(filename,"%s/%s/%s.pl",(char *) getenv("HOME"),settings.playerdir,op->name);
#else
  sprintf(filename,"%s/%s/%s.pl",settings.libdir,settings.playerdir,op->name);
#endif
  if ((fp=open_and_uncompress(filename,1,&comp)) == NULL) {
    confirm_password(op);
  } else {
    int correct = 0;
    if(fgets(bufall,MAX_BUF,fp) != NULL) {
      if(!strncmp(bufall,"checksum ",9)) {
        checksum = strtol_local(bufall+9,(char **) NULL, 16);
        (void) fgets(bufall,MAX_BUF,fp);
      }
      if(sscanf(bufall,"password %s\n",buf))
        /* New password scheme: */
        correct=check_password(pl->write_buf+1,buf);
      else { /* Old scheme when passwords were stored in ascii */
        bufall[strlen(bufall)-1]='\0';
        correct= !strcmp(bufall,pl->write_buf+1);
      }
    }
    if (!correct) {
      new_draw_info(NDI_UNIQUE, 0,op," ");
      new_draw_info(NDI_UNIQUE, 0,op,"Wrong Password!");
      new_draw_info(NDI_UNIQUE, 0,op," ");
      remove_lock(pl);
      if(op->name!=NULL)
        free_string(op->name);
      op->name=add_string("noname");
      pl->last_value= -1;
      draw_stats(op);
      get_name(op);
    } else {
#ifdef SAVE_INTERVAL
      pl->last_save_time=time(NULL);
#endif /* SAVE_INTERVAL */
#ifdef SIMPLE_PARTY_SYSTEM
      pl->party_number = (-1);
#endif /* SIMPLE_PARTY_SYSTEM */
#ifdef SAVE_WINDOW_POSITIONS
      pl->valid_save_positions=0;
#endif /* SAVE_WINDOW_POSITIONS */
#ifdef SEARCH_ITEMS
      pl->search_str[0]='\0';
#endif /* SEARCH_ITEMS */
      pl->name_changed=1;
      pl->orig_stats.Str=0;
      pl->orig_stats.Dex=0;
      pl->orig_stats.Con=0;
      pl->orig_stats.Int=0;
      pl->orig_stats.Pow=0;
      pl->orig_stats.Wis=0;
      pl->orig_stats.Cha=0;
      load_default_keys(op->contr);
      while (fgets(bufall,MAX_BUF,fp)!=NULL) {
        sscanf(bufall,"%s %d\n",buf,&value);
        if (!strcmp(buf,"endplst"))
          break;
#ifdef SET_TITLE
	else if (!strncmp(bufall,"title",5))
          sscanf(bufall,"title %s[^\n]",pl->own_title);
#endif /* SET_TITLE */
#ifdef SAVE_WINDOW_POSITIONS
	else if (!strcmp(buf,"win_pos")) {
	  pl->valid_save_positions=
	    (sscanf(bufall,
		    "win_pos %d %d %u %u %d %d %u %u %d %d %u %u %d %d %u %u %d %d %u %u %d %d %u %u",
		    &pl->win_pos[0].x,&pl->win_pos[0].y,
		    &pl->win_pos[0].w,&pl->win_pos[0].h,
		    &pl->win_pos[1].x,&pl->win_pos[1].y,
		    &pl->win_pos[1].w,&pl->win_pos[1].h,
		    &pl->win_pos[2].x,&pl->win_pos[2].y,
		    &pl->win_pos[2].w,&pl->win_pos[2].h,
		    &pl->win_pos[3].x,&pl->win_pos[3].y,
		    &pl->win_pos[3].w,&pl->win_pos[3].h,
		    &pl->win_pos[4].x,&pl->win_pos[4].y,
		    &pl->win_pos[4].w,&pl->win_pos[4].h,
		    &pl->win_pos[5].x,&pl->win_pos[5].y,
		    &pl->win_pos[5].w,&pl->win_pos[5].h)==24);
	}
#endif /* SAVE_WINDOW_POSITIONS */
#ifdef EXPLORE_MODE
      else if (!strcmp(buf,"explore"))
        pl->explore = value;
#endif
        else if (!strcmp(buf,"gen_hp"))
          pl->gen_hp=value;
        else if (!strcmp(buf,"shoottype"))
          pl->shoottype=(rangetype)value;
        else if (!strcmp(buf,"gen_sp"))
          pl->gen_sp=value;
        else if (!strcmp(buf,"gen_grace"))
          pl->gen_grace=value;
        else if (!strcmp(buf,"spell"))
          pl->chosen_spell=value;
        else if (!strcmp(buf,"listening"))
          pl->listening=value;
        else if (!strcmp(buf,"berzerk"))
          pl->berzerk=value;
        else if (!strcmp(buf,"peaceful"))
          pl->peaceful=value;
        else if (!strcmp(buf,"scroll"))
          pl->scroll=value;
        else if (!strcmp(buf,"digestion"))
          pl->digestion=value;
	else if (!strcmp(buf,"pickup"))
	  pl->mode=value;
        else if (!strcmp(buf,"keyboard_flush"))
	  pl->keyboard_flush = value;
	else if (!strcmp(buf,"sort_inventory"))
	  pl->sort_inventory = value;
	else if (!strcmp(buf,"show_buttons"))
	  pl->show_buttons = value;
        else if (!strcmp(buf,"show_inv_icon")) {
	  pl->show_inv_icon = value;
	  if (pl->show_inv_icon)
	    strcpy(pl->format_inv,"%-20.20s%-6s"); /* This can be changed by resize */
	  else
	    strcpy(pl->format_inv,"%-24.24s%-6s"); /* This can be changed by resize */
	}
	else if (!strcmp(buf,"outputs_sync"))
	  pl->outputs_sync = value;
	else if (!strcmp(buf,"outputs_count"))
	  pl->outputs_count = value;
        else if (!strcmp(buf,"map"))
	    sscanf(bufall,"map %s", pl->maplevel);
        else if (!strcmp(buf,"weapon_sp"))
          sscanf(buf,"weapon_sp %f",&pl->weapon_sp);
        else if (!strcmp(buf,"Str"))
          pl->orig_stats.Str=value;
        else if (!strcmp(buf,"Dex"))
          pl->orig_stats.Dex=value;
        else if (!strcmp(buf,"Con"))
          pl->orig_stats.Con=value;
        else if (!strcmp(buf,"Int"))
          pl->orig_stats.Int=value;
        else if (!strcmp(buf,"Pow"))
          pl->orig_stats.Pow=value;
        else if (!strcmp(buf,"Wis"))
          pl->orig_stats.Wis=value;
        else if (!strcmp(buf,"Cha"))
          pl->orig_stats.Cha=value;
        else if (!strcmp(buf,"lev_array")){
          for(i=1;i<=value;i++) {
            int j;
            fscanf(fp,"%d\n",&j);
            pl->levhp[i]=j;
            fscanf(fp,"%d\n",&j);
            pl->levsp[i]=j;
            fscanf(fp,"%d\n",&j);
            pl->levgrace[i]=j;
          }
        } else if (!strcmp(buf,"spell_array")) {
          pl->nrofknownspells=value;
          for(i=0;i<pl->nrofknownspells;i++) {
            int j;
            fscanf(fp,"%d\n",&j);
            pl->known_spells[i]=j;
          }
        } else if (!strcmp(buf,"known_spell")) {
          char *cp=strchr(bufall,'\n');
          *cp='\0';
          cp=strchr(bufall,' ');
          cp++;
          for(i=0;i<NROFREALSPELLS;i++)
            if(!strcmp(spells[i].name,cp)) {
              pl->known_spells[pl->nrofknownspells++]=i;
              break;
            }
          if(i==NROFREALSPELLS)
            LOG(llevDebug, "Error: unknown spell (%s)\n",cp);
        } else if (!strcmp(buf,"confkeys")) {
	  LOG(llevDebug, "Ignoring old configkeys (%d)\n", value);
          for(i=0; i < value; i++)
	    fgets(bufall, sizeof(bufall), fp);
        } else if (!strcmp(buf,"pushkey")) {
	  LOG(llevDebug, "Ignoring old pushkey (%s)\n", bufall);
	} else if (!strcmp(buf,"key")) {
	  insert_key(op->contr, KEYF_USER, &bufall[4]);
        } else {
            LOG(llevError,"Warning, oldfashioned variable in save-file: %s\n",
                buf);
        }
      }

      /* sigh */
      free_object (op);
      op = pl->ob = LoadObject (fp, NULL);
      /* Somehow, the game worked before without this line - relied
       * on getting the object back that we just freed? */
      op->contr = pl;
      CLEAR_FLAG(op, FLAG_NO_FIX_PLAYER);

      x=op->x; y=op->y;
      /* if old save file, try recover from it */
      if(!op->arch) {
         new_draw_info(NDI_UNIQUE, 0,op,"Error: unknown class. (probably old save file)\n");
         op->arch = find_archetype (op->race);
      }
      if(!op->arch) { /* and fail... */
        new_draw_info(NDI_UNIQUE, 0,op,"Fatal error: Can't find archetype\n");
        LOG(llevError, "Error: Unknown class; %s.\n", op->race);
      }
      strncpy(pl->title, op->arch->clone.name,MAX_NAME);

      /* If the map where the person was last saved does not exist,
       * restart them in the beginning town.  This is good for when
       * maps change between versions
       */

      if (check_path(pl->maplevel)==-1) {
	strcpy(pl->maplevel, first_map_path);
	x = -1;
      }

      enter_exit(op,NULL); /* This won't insert the player any longer! */

      if (op->stats.hp<0||op->stats.food<0) {
/* IF NOT_PERMADEATH is set, this should really do something different,
 * like bring the character back to life under the normal rules.
 */
        clear_win_info(op);
        new_draw_info(NDI_UNIQUE, 0,op,"Your character was dead, when you quit.");
        new_draw_info(NDI_UNIQUE, 0,op," ");
	play_again(op);
        delete_character(op->name);
      } else { /* character is alive */
        pl->name_changed=1;
        pl->state = ST_PLAYING;
#ifdef AUTOSAVE
	pl->last_save_tick = pticks;
#endif
	op->carrying=0;
	pl->freeze_inv=1;
	/* Need to call fix_player now - program modified so that it is not
	 * called during the load process (FLAG_NO_FIX_PLAYER set when
	 * saved)
	 * Moved ahead of the esrv functions, so proper weights will be
	 * sent to the client.
	 */
	legal_range(op, op->contr->shoottype);
        fix_player(op);
	fix_weight ();
	pl->freeze_inv=0;
	if (pl->eric_server > 0) {
	    esrv_new_player(pl->eric_server, op->count, op->name, op->weight,
			op->face->number);
	    esrv_send_inventory(op, op);
	} else {
	    draw_all_inventory(op);
	}

	draw_all_inventory(op);


#ifdef ALLOW_SKILLS
	(void) init_player_exp(op);
        (void) link_player_skills(op);
#endif

        if(!pl->split_window && pl->eric_server == 0)
          XClearWindow(pl->gdisp,pl->win_root);
        draw_all_info(op);
        draw_all_message(op);
        new_draw_info(NDI_UNIQUE, 0,op,"Welcome Back!");
        if(pl->loading == NULL) {
          if(!out_of_map(op->map,x,y))
            op->x=x, op->y=y;
          insert_ob_in_map(op,op->map);
        } else {
          LOG(llevError,"Warning: map was not in memory (%s).\n",
                  op->map->path);
          pl->removed = 0; /* Pl. will be inserted when map is loaded */
        }
        refresh(op);
        draw_all_look(op);
        close_and_delete(fp, comp);
        LOG(llevDebug,"Checksums: %x %x\n",
                checksum,calculate_checksum(filename,1));
#ifdef ENABLE_CHECKSUM
        if(calculate_checksum(filename,1) != checksum) {
          new_draw_info(NDI_UNIQUE, 0,op,"Since your savefile has been tampered with,");
          new_draw_info(NDI_UNIQUE, 0,op,"you will not be able to save again.");
          set_cheat(op);
        }
#endif
      }
    }
    if (pl->state != ST_PLAYING)
      close_and_delete(fp, comp); 
  }
  pl->last_value= -1;

  /* This seems to compile without warnings now.  Don't know if it works
   * on SGI's or not, however.
   */
  qsort((void *)pl->known_spells,pl->nrofknownspells,
	sizeof(pl->known_spells[0]),(int (*)())spell_sort);
  draw_stats(op);
#ifdef SAVE_WINDOW_POSITIONS
  if(pl->valid_save_positions && pl->split_window) {
    unsigned long xwc_mask=0;
    XWindowChanges xwc;
    xwc_mask=CWX|CWY|CWWidth|CWHeight;
    
    xwc.x=pl->win_pos[0].x;xwc.y=pl->win_pos[0].y;
    xwc.width=pl->win_pos[0].w;xwc.height=pl->win_pos[0].h;
    XConfigureWindow(pl->gdisp,pl->win_game,xwc_mask,&xwc);

    xwc.x=pl->win_pos[1].x;xwc.y=pl->win_pos[1].y;
    xwc.width=pl->win_pos[1].w;xwc.height=pl->win_pos[1].h;
    XConfigureWindow(pl->gdisp,pl->win_stats,xwc_mask,&xwc);

    xwc.x=pl->win_pos[2].x;xwc.y=pl->win_pos[2].y;
    xwc.width=pl->win_pos[2].w;xwc.height=pl->win_pos[2].h;
    XConfigureWindow(pl->gdisp,pl->win_info,xwc_mask,&xwc);

    xwc.x=pl->win_pos[3].x;xwc.y=pl->win_pos[3].y;
    xwc.width=pl->win_pos[3].w;xwc.height=pl->win_pos[3].h;
    XConfigureWindow(pl->gdisp,pl->win_inv,xwc_mask,&xwc);

    xwc.x=pl->win_pos[4].x;xwc.y=pl->win_pos[4].y;
    xwc.width=pl->win_pos[4].w;xwc.height=pl->win_pos[4].h;
    XConfigureWindow(pl->gdisp,pl->win_look,xwc_mask,&xwc);

    xwc.x=pl->win_pos[5].x;xwc.y=pl->win_pos[5].y;
    xwc.width=pl->win_pos[5].w;xwc.height=pl->win_pos[5].h;
    XConfigureWindow(pl->gdisp,pl->win_message,xwc_mask,&xwc);
    sprintf(pl->format_inv,"%%-%d.%ds%%-6s",pl->inv_chars-6,
          pl->inv_chars-6);
    draw_all_inventory(pl->ob);

  }
    
#endif /* SAVE_WINDOW_POSITIONS */
  return;
}
  

