/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  Ground ZERO improvements copyright pending 1994, 1995 by James Hilke   *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "ground0.h"
 
#if !defined(macintosh)
extern  int     _filbuf         args( (FILE *) );
#endif



/*
 * Array of containers read for proper re-nesting of objects.
 */
#define MAX_NEST	100
static	OBJ_DATA *	rgObjNest	[MAX_NEST];



/*
 * Local functions.
 */
void	fwrite_char	args( ( CHAR_DATA *ch,  FILE *fp ) );
void	fwrite_obj	args( ( CHAR_DATA *ch,  OBJ_DATA  *obj,
			    FILE *fp, int iNest ) );
void	fread_char	args( ( CHAR_DATA *ch,  FILE *fp ) );
void	fread_obj	args( ( CHAR_DATA *ch,  FILE *fp ) );
void    set_default_pcdata args ((CHAR_DATA *ch));
void    set_default_colors args ((CHAR_DATA *ch));
void    set_char_defaults  args ((CHAR_DATA *ch));
PC_DATA *alloc_pcdata      args(());
CHAR_DATA *alloc_char      args(());


void do_save_all (CHAR_DATA *ch, char *argument)
{
  FILE *fp;
  CHAR_DATA *chars_to_save;
  OBJ_DATA *objs_to_save;
  char buf[MAX_STRING_LENGTH];
  int count_objs = 0, count_chars = 0;
  int count;

  /* form =
            random number seed
	    max on
	    tick counter
	    expansions
            !name1 kills killed rep x y level hp
            vnum1 hpc1 hps1 vnum2 hpc2 hps2 . . . vnumN hpcN hpsN 0
            !name2 kills killed rep x y level hp
            vnum1 hpc1 hps1 vnum2 hpc2 hps2 . . . vnumN hpcN hpsN 0
	    .
	    .
            !nameN kills killed rep x y level hp
            vnum1 hpc1 hps1 vnum2 hpc2 hps2 . . . vnumN hpcN hpsN 0
	    ~
	    vnum1 x y level hpc hps special#
	    vnum2 x y level hpc hps special#
	    .
	    .
	    vnum3 x y level hpc hps special#
	    0
  special# is zero in general case, but can be assigned an arbitrary value to
  be checked when loaded in for cases such as a mine burried under the ground,
  etc that requires special action by the game
  */

  if (ch)
    send_to_char ("Saving EVERYTHING . . .\n\r", ch);
  if ((fp = fopen ("../boot/fullsave.txt", "w")) == NULL)
    {
      if (ch)
	send_to_char ("Error openning file fullsave.c.", ch);
      return;
    }
  fprintf (fp, "%d\n%d\n%d\n%d\n", game_seed, max_on, tick_counter,
	   expansions);
  for (chars_to_save = char_list; chars_to_save; 
       chars_to_save = chars_to_save->next)
    {
      /* don't save the imms cuz they come back untrusted which puts them
         in a random room when they are kicked out of their god room for
	 being ld and untrusted */
      if (IS_NPC (chars_to_save) || IS_IMMORTAL (chars_to_save))
	continue;
      count_chars++;
      fprintf (fp, "!%s\n%d %d %d %d %d %d\n", chars_to_save->names, 
	       chars_to_save->kills, chars_to_save->deaths,
	       chars_to_save->in_room->x, 
	       chars_to_save->in_room->y, 
	       chars_to_save->in_room->level, 
	       chars_to_save->hit);
      for (objs_to_save = chars_to_save->carrying; objs_to_save; 
	   objs_to_save = objs_to_save->next_content)
	{
	  count_objs++;
	  if (!objs_to_save->pIndexData->number_to_put_in)
	    continue;
	  fprintf (fp, "%d %d %d ", objs_to_save->pIndexData->vnum,
		   objs_to_save->hp_char, objs_to_save->hp_struct);
	  if (objs_to_save->contains)
	    {
	      count_objs++;
	      fprintf (fp, "%d %d %d ", 
		       objs_to_save->contains->pIndexData->vnum,
		       objs_to_save->contains->hp_char, 
		       objs_to_save->contains->hp_struct);
	    }
	}
      fprintf (fp, "0\n");
    }
  fprintf (fp, "~\n");
  for (objs_to_save = object_list; objs_to_save; 
       objs_to_save = objs_to_save->next)
    {
      if (objs_to_save->carried_by)
	continue;
      if (!objs_to_save->pIndexData->number_to_put_in)
	continue;
      if (objs_to_save->in_room)
	{
	  fprintf (fp, "%d %d %d %d %d %d", objs_to_save->pIndexData->vnum,
		   objs_to_save->in_room->x, objs_to_save->in_room->y,
		   objs_to_save->in_room->level, objs_to_save->hp_char,
		   objs_to_save->hp_struct);
	  count_objs++;
	}
      else
	if (objs_to_save->destination)
	  {
	    fprintf (fp, "%d %d %d %d %d %d", objs_to_save->pIndexData->vnum,
		     objs_to_save->destination->x,
		     objs_to_save->destination->y,
		     objs_to_save->destination->level, objs_to_save->hp_char,
		     objs_to_save->hp_struct);
	    count_objs++;
	  }
	else
	  {
	    /* otherwise it is ammo in a weapon and should have been gotten 
	       when characters were saved */
	    continue;
	  }
      if (IS_SET (objs_to_save->general_flags, GEN_CAN_BURY) &&
	  (objs_to_save->destination) && 
	  (objs_to_save->destination->mine == objs_to_save))
	fprintf (fp, " 1\n");
      else
	fprintf (fp, " 0\n");
    }
  fprintf (fp, "0\n");
  fclose (fp);
  sprintf (buf, "%%%d objects and %d characters were saved.\n\r", count_objs,
	   count_chars);
  do_immtalk (NULL, buf);
  log_string (&(buf[1]));
  if (ch)
    send_to_char ("Done.\n\r", ch);
}

/*
 * Save a character and inventory.
 * Would be cool to save NPC's too for quest purposes,
 *   some of the infrastructure is provided.
 */
void save_char_obj( CHAR_DATA *ch )
{
    char strsave[MAX_INPUT_LENGTH];
    char buf[MAX_STRING_LENGTH];
    FILE *fp;

    if ( IS_NPC(ch) )
	return;

    if ( ch->desc != NULL && ch->desc->original != NULL )
	ch = ch->desc->original;

    /* create god log */
    if (ch->trust)
    {
	fclose(fpReserve);
	sprintf(strsave, "%sPS%s",GOD_DIR, capitalize(ch->names));
	if ((fp = fopen(strsave,"w")) == NULL)
	{
	    bug("Save_char_obj: fopen",0);
	    perror(strsave);
 	}

	fprintf(fp,"Trust %2d  %s%s\n",
	   get_trust(ch), ch->names, ch->pcdata->title_line);
	fclose( fp );
	fpReserve = fopen( NULL_FILE, "r" );
    }

    fclose( fpReserve );
    sprintf( strsave, "%sPS%s", PLAYER_DIR, capitalize( ch->names ) );
    if ( ( fp = fopen( PLAYER_TEMP, "w" ) ) == NULL )
    {
	bug( "Save_char_obj: fopen", 0 );
	perror( strsave );
    }
    else
    {
	fwrite_char( ch, fp );
	fprintf (fp, "#END\n");
    }
    fclose( fp );
    /* move the file */
    sprintf(buf,"mv %s %s",PLAYER_TEMP,strsave);
    system(buf);
    fpReserve = fopen( NULL_FILE, "r" );
    return;
}



/*
 * Write the char.
 */
void fwrite_char( CHAR_DATA *ch, FILE *fp )
{
    AFFECT_DATA *paf;
    int sn, gn;

    fprintf( fp, "#PLAYER\n");

    fprintf( fp, "Name %s~\n",	ch->names		);
    if (ch->short_descript && ch->short_descript[0])
      	fprintf( fp, "ShD  %s~\n",	ch->short_descript);
    fprintf( fp, "Sex  %d\n",	ch->sex			);
    fprintf (fp, "nhp_solo %d\n", ch->pcdata->solo_hit);
    if (ch->kills)
      fprintf (fp, "Kills2 %d\n", ch->kills);
    if (ch->deaths)
      fprintf (fp, "Killed2 %d\n", ch->deaths);
    fprintf( fp, "Plyd %d\n",
	ch->played + (int) (current_time - ch->logon)	);
    fprintf( fp, "Note %d\n",		ch->last_note	);
    fprintf( fp, "Scro %d\n", 	ch->lines		);
    if (ch->act != 0)
	fprintf( fp, "Act  %d\n",   ch->act		);
    if (ch->invis_level != 0)
	fprintf( fp, "Invi %d\n", 	ch->invis_level	);
    fprintf( fp, "Pass %s~\n",	ch->pcdata->password);
    if (ch->kill_msg)
      fprintf( fp, "KillMsg %s~\n", ch->kill_msg);
    if (ch->pcdata->account)
      fprintf (fp, "Account %s~\n", ch->pcdata->account);
    if (ch->pcdata->poofin_msg && ch->pcdata->poofin_msg[0])
      fprintf( fp, "Bin  %s~\n",	ch->pcdata->poofin_msg);
    if (ch->pcdata->poofout_msg && ch->pcdata->poofout_msg[0])
      fprintf( fp, "Bout %s~\n",	ch->pcdata->poofout_msg);
    fprintf( fp, "Titl %s~\n",	ch->pcdata->title_line	);

    fprintf( fp, "Color %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
    	       ch->color,
               ch->pcdata->color_hp,
               ch->pcdata->color_desc,
               ch->pcdata->color_obj,
               ch->pcdata->color_action,
               ch->pcdata->color_combat_o,
               ch->pcdata->color_combat_condition_s,
               ch->pcdata->color_combat_condition_o,
               ch->pcdata->color_xy,
               ch->pcdata->color_wizi,
               ch->pcdata->color_level,
               ch->pcdata->color_exits,
               ch->pcdata->color_say,
               ch->pcdata->color_tell,
               ch->pcdata->color_reply);
    fprintf (fp, "End\n\n");
}

CHAR_DATA *alloc_char ()
{
  CHAR_DATA *temp;

  if ( char_free == NULL )
    return alloc_perm( sizeof(CHAR_DATA) );
  else
    {
      temp = char_free;
      char_free = char_free->next;
      return temp;
    }
}

PC_DATA *alloc_pcdata ()
{
  PC_DATA *temp;
  if ( pcdata_free == NULL )
    return alloc_perm( sizeof(PC_DATA) );
  else
    {
      temp = pcdata_free;
      pcdata_free = pcdata_free->next;
      return temp;
    }
}

void set_char_defaults (CHAR_DATA *ch)
{
  ch->move_delay                        = 0;
  ch->where_start                       = NULL;
  ch->valid                             = VALID_VALUE;
  ch->ld_behavior                       = 0;
  ch->affected_by			= 0;
  ch->temp_flags                        = 0;
  ch->act				= 0;
  ch->comm				= COMM_COMBINE | COMM_PROMPT;
  ch->invis_level			= 0;
  ch->trust				= 0;
  ch->armor                             = 0;
  ch->last_fight			= 0; 
}

void set_default_colors (CHAR_DATA *ch)
{
  ch->pcdata->color_combat_condition_s= 0x1;
  ch->pcdata->color_action		= 0x9;
  ch->pcdata->color_xy		        = 0xe;
  ch->pcdata->color_wizi		= 0x4;
  ch->pcdata->color_hp		        = 0x5;
  ch->pcdata->color_combat_condition_o  = 0x8;
  ch->pcdata->color_combat_o		= 0xd;
  ch->pcdata->color_level		= 0x8;
  ch->pcdata->color_exits		= 0x9;
  ch->pcdata->color_desc		= 0xa;
  ch->pcdata->color_obj		        = 0xb;
  ch->pcdata->color_say		        = 0xc;
  ch->pcdata->color_tell		= 0x0;
  ch->pcdata->color_reply		= 0x1;
}

void set_default_pcdata (CHAR_DATA *ch)
{
  ch->pcdata->confirm_delete		= FALSE;
  ch->pcdata->password			= NULL;
  ch->pcdata->poofin_msg		= NULL;
  ch->pcdata->poofin_msg		= NULL;
  ch->pcdata->title_line		= NULL;
  ch->pcdata->account			= NULL;
  ch->pcdata->solo_hit			= HIT_POINTS_MORTAL;
}

/*
 * Load a char and inventory into a new ch structure.
 */
CHAR_DATA *load_char_obj( DESCRIPTOR_DATA *d, char *name )
{
  static PC_DATA pcdata_zero;
  char strsave[MAX_INPUT_LENGTH];
  char buf[100];
  CHAR_DATA *ch;
  FILE *fp;
  bool found;
  int stat;
  
  ch = alloc_char ();
  clear_char(ch);
  ch->pcdata = alloc_pcdata ();
  *ch->pcdata = pcdata_zero;
  
  if (d)
    {
      d->character = ch;
      ch->desc = d;
    }
  else
    ch->desc = NULL;

  ch->names = str_dup (name);
  set_char_defaults (ch);
  ch->color = 1;
  set_default_colors (ch);
  set_default_pcdata (ch);

  found = FALSE;
  fclose( fpReserve );
    
    #if defined(unix)
    /* decompress if .gz file exists */
    sprintf( strsave, "%sPS%s%s", PLAYER_DIR, capitalize(name),".gz");
    if ( ( fp = fopen( strsave, "r" ) ) != NULL )
    {
	fclose(fp);
	sprintf(buf,"gzip -dfq %s",strsave);
	system(buf);
    }
    #endif

    sprintf( strsave, "%sPS%s", PLAYER_DIR, capitalize( name ) );
    if ( ( fp = fopen( strsave, "r" ) ) != NULL )
    {
	int iNest;

	for ( iNest = 0; iNest < MAX_NEST; iNest++ )
	    rgObjNest[iNest] = NULL;

	found = TRUE;
	/* freed from above, will be found in pfile */
	free_string (ch->names);
	ch->names = NULL;
	for ( ; ; )
	{
	    char letter;
	    char *word;

	    letter = fread_letter( fp );
	    if ( letter == '*' )
	    {
		fread_to_eol( fp );
		continue;
	    }

	    if ( letter != '#' )
	    {
		bug( "Load_char_obj: # not found.", 0 );
		break;
	    }

	    word = fread_word( fp );
	    if      ( !str_cmp( word, "PLAYER" ) ) fread_char ( ch, fp );
	    else if ( !str_cmp( word, "END"    ) ) break;
	    else
	    {
		bug( "Load_char_obj: bad section.", 0 );
		break;
	    }
	}
	fclose( fp );
    }

    fpReserve = fopen( NULL_FILE, "r" );

    if (found)
      return ch;
    else
      return 0;
}



/*
 * Read in a char.
 */

#if defined(KEY)
#undef KEY
#endif

#define KEY( literal, field, value )					\
				if ( !str_cmp( word, literal ) )	\
				{					\
				    field  = value;			\
				    fMatch = TRUE;			\
				    break;				\
				}

#define IGNORE(literal)					\
				if ( !str_cmp( word, literal ) )	\
				{					\
				    fMatch = TRUE;			\
                                    fread_to_eol (fp);                  \
				    break;				\
				}
		

void fread_char( CHAR_DATA *ch, FILE *fp )
{
    char buf[MAX_STRING_LENGTH];
    char *word;
    bool fMatch;

    for ( ; ; )
    {
	word   = feof( fp ) ? "End" : fread_word( fp );
	fMatch = FALSE;

	switch ( UPPER(word[0]) )
	{
	case '*':
	    fMatch = TRUE;
	    fread_to_eol( fp );
	    break;

	case 'A':
	    KEY( "Act",		ch->act,		fread_number( fp ) );
	    KEY( "Account",	ch->pcdata->account,	fread_string(fp));

	case 'B':
	    KEY( "Bamfin",	ch->pcdata->poofin_msg,	fread_string( fp ) );
	    KEY( "Bamfout",	ch->pcdata->poofout_msg,fread_string( fp ) );
	    KEY( "Bin",		ch->pcdata->poofin_msg,	fread_string( fp ) );
	    KEY( "Bout",	ch->pcdata->poofout_msg,fread_string( fp ) );
	    break;

	case 'C':
            if ( !str_cmp( word, "Color")) 
               {
               log_string("Reading colors");
               ch->color        		    = fread_number( fp );
               ch->pcdata->color_hp 		    = fread_number( fp );
               ch->pcdata->color_desc 		    = fread_number( fp );
               ch->pcdata->color_obj 		    = fread_number( fp );
               ch->pcdata->color_action 	    = fread_number( fp );
               ch->pcdata->color_combat_o 	    = fread_number( fp );
               ch->pcdata->color_combat_condition_s = fread_number( fp );
               ch->pcdata->color_combat_condition_o = fread_number( fp );
               ch->pcdata->color_xy	 	    = fread_number( fp );
               ch->pcdata->color_wizi 		    = fread_number( fp );
               ch->pcdata->color_level  	    = fread_number( fp );
               ch->pcdata->color_exits  	    = fread_number( fp );
               ch->pcdata->color_say		    = fread_number( fp );
               ch->pcdata->color_tell		    = fread_number( fp );
               ch->pcdata->color_reply		    = fread_number( fp );
               fMatch=TRUE;
               break;
               }
	    break;

	case 'E':
	    if ( !str_cmp( word, "End" ) )
		return;
	    break;

	case 'H':
	  IGNORE ("hp_solo");
	  /*	  KEY ("hp_solo", ch->pcdata->solo_hit, fread_number (fp));*/
	  break;

	case 'I':
	    KEY( "InvisLevel",	ch->invis_level,	fread_number( fp ) );
	    KEY( "Invi",	ch->invis_level,	fread_number( fp ) );
	    break;

        case 'K':
	    IGNORE ("Killed");
	    IGNORE ("Kills");
	    KEY ("Kills2",	ch->kills,		fread_number(fp));
	    KEY ("Killed2",	ch->deaths,		fread_number(fp));
	    KEY( "KillMsg",	ch->kill_msg,		fread_string( fp ) );
            break;

	case 'N':
	    KEY( "Name",	ch->names,		fread_string( fp ) );
	    KEY ("nhp_solo", ch->pcdata->solo_hit, fread_number (fp));
	    KEY( "Note",	ch->last_note,		fread_number( fp ) );
	    break;

	case 'P':
	    KEY( "Password",    ch->pcdata->password,	fread_string( fp ) );
	    KEY( "Pass",	ch->pcdata->password,	fread_string( fp ) );
	    KEY( "Played",	ch->played,		fread_number( fp ) );
	    KEY( "Plyd",	ch->played,		fread_number( fp ) );
	    break;

	case 'S':
	    KEY( "Scro",	ch->lines,		fread_number( fp ) );
	    KEY( "Sex",		ch->sex,		fread_number( fp ) );
	    KEY( "ShortDescr",  ch->short_descript,	fread_string( fp ) );
	    KEY( "ShD",		ch->short_descript,	fread_string( fp ) );
	    break;

	case 'T':
	    KEY( "Trust",	ch->trust,		fread_number( fp ) );
	    KEY( "Tru",		ch->trust,		fread_number( fp ) );

	    if ( !str_cmp( word, "Title" )  || !str_cmp( word, "Titl"))
	    {
	      ch->pcdata->title_line = fread_string(fp);
	      if (ch->pcdata->title_line[0] != '.' &&
		  ch->pcdata->title_line[0] != ',' &&
		  ch->pcdata->title_line[0] != '!' && 
		  ch->pcdata->title_line[0] != '?')
		{
		    sprintf( buf, " %s", ch->pcdata->title_line );
		    free_string (ch->pcdata->title_line);
		    ch->pcdata->title_line = str_dup (buf);
		}
		fMatch = TRUE;
		break;
	    }

	    break;
	}

	if ( !fMatch )
	{
	    bug( "Fread_char: no match.", 0 );
	    fread_to_eol( fp );
	}
    }
}

void save_account_list ()
{
  FILE *fp;
  ACCOUNT_DATA *tracker;

  if ((fp = fopen (ACCOUNTS_FILE, "w")) == NULL)
    return;
  fprintf (fp, "#ACCOUNTS\n");
  for (tracker = accounts_list; tracker; tracker = tracker->next)
    fprintf (fp, "%s %s\n", tracker->login, tracker->password);
  fprintf (fp, "$\n\n#$\n\n");
  fclose (fp);
}