/*
 * files.c: misc code to access files used by Moria 
 *
 * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke 
 *
 * This software may be copied and distributed for educational, research, and
 * not for profit purposes provided that this copyright and statement are
 * included in all such copies. 
 */

#include <stdio.h>
#include <errno.h>

#ifndef MSDOS
#include <sys/param.h>
#endif

#include "constant.h"
#include "config.h"
#include "types.h"
#include "externs.h"

#ifdef ibm032
#include <ctype.h>
#endif

#ifdef MSDOS
extern int          errno;

#endif

#if defined(GEMDOS) && (__STDC__ == 0)
#include <access.h>
char               *strcat();

#endif

#ifdef USG
#ifndef ATARIST_MWC
#include <string.h>
#include <fcntl.h>
#ifdef __MINT__
#include <ctype.h>
#endif
#endif
#else
#include <strings.h>
#ifndef VMS
#include <sys/file.h>
#else
#include <file.h>
#endif
#endif

#ifndef VMS
#if defined(ultrix) || defined(USG)
void exit();

#endif
#endif


/* This is done first in main.c, so I'll rudely drop it into the front
 * of files.c...  If NEW_FILEPATHS is defined, we'll look for the ANGBAND_PATH
 * environment variable, and then look for the files in there.  This is
 * much nicer than having explicit paths, and should help us make things
 * more portable.  Now, if only someone would graft on the NetHack display
 * code...
 *
 * This is rather inelegant code; no checks are made for malloc() or
 * realloc() failures.  If you malloc/realloc are broken, you're in
 * trouble, and your compiler vendor needs a good swift kick in the
 * forehead. [cjh]
 */
#ifdef NEW_FILEPATHS
char *ANGBAND_TST;		/* was LIBDIR(test)			*/
char *ANGBAND_HOU;		/* was LIBDIR(files/hours)		*/
char *ANGBAND_MOR;		/* was LIBDIR(files/news)		*/
char *ANGBAND_TOP;		/* was LIBDIR(files/newscores)		*/
char *ANGBAND_BONES;		/* was LIBDIR(bones)			*/
char *ANGBAND_HELP;		/* was LIBDIR(files/roglcmds.hlp)	*/
char *ANGBAND_ORIG_HELP;	/* was LIBDIR(files/origcmds.hlp)	*/
char *ANGBAND_WIZ_HELP;		/* was LIBDIR(files/rwizcmds.hlp)	*/
char *ANGBAND_OWIZ_HELP;	/* was LIBDIR(files/owizcmds.hlp)	*/
char *ANGBAND_WELCOME;		/* was LIBDIR(files/welcome.hlp)	*/
char *ANGBAND_LOG;		/* was LIBDIR(files/ANGBAND.log)	*/
char *ANGBAND_VER;		/* was LIBDIR(files/version.hlp)	*/
char *ANGBAND_LOAD;		/* was LIBDIR(files/loadcheck)		*/
char *ANGBAND_WIZ;		/* was LIBDIR(files/wizards)		*/
char *ANGBAND_SAV;		/* was LIBDIR(save)			*/

void get_file_paths()
{
#ifndef MAXPATHLEN
#define BOZO_MAXPATHLEN
#define MAXPATHLEN 512
#endif

    char *angband_path;

/* Find the path to our files...  if the ANGBAND_PATH environment var
 * isn't set, use DEFAULT_PATH to look for the files.  If your system
 * can't do environment variables, you'll have to kludge this.  [cjh]
 */

    angband_path = getenv( "ANGBAND_PATH" );
    if (angband_path == NULL) {

#if 0 /* I don't think this is appropriate, particularly since this may
       * well be set by a system-wide games administrator.  -CWS */

       fprintf(stderr, "Warning: ANGBAND_PATH not set!\n" );
       fprintf(stderr, "Warning: Using %s for ANGBAND_PATH.\n", DEFAULT_PATH );
#endif
	angband_path = (char *)malloc( strlen( DEFAULT_PATH ) + 1 );
	strcpy( angband_path, DEFAULT_PATH );
    }

    ANGBAND_TST = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_TST, angband_path );
    strcat( ANGBAND_TST, PATH_SEP );
    strcat( ANGBAND_TST, "test" );
    ANGBAND_TST = (char *)realloc( ANGBAND_TST, strlen( ANGBAND_TST ) + 1 );

    ANGBAND_HOU = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_HOU, angband_path );
    strcat( ANGBAND_HOU, PATH_SEP );
    strcat( ANGBAND_HOU, "files" );
    strcat( ANGBAND_HOU, PATH_SEP );
    strcat( ANGBAND_HOU, "hours" );
    ANGBAND_HOU = (char *)realloc( ANGBAND_HOU, strlen( ANGBAND_HOU ) + 1 );

    ANGBAND_MOR = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_MOR, angband_path );
    strcat( ANGBAND_MOR, PATH_SEP );
    strcat( ANGBAND_MOR, "files" );
    strcat( ANGBAND_MOR, PATH_SEP );
    strcat( ANGBAND_MOR, "news" );
    ANGBAND_MOR = (char *)realloc( ANGBAND_MOR, strlen( ANGBAND_MOR ) + 1 );

    ANGBAND_TOP = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_TOP, angband_path );
    strcat( ANGBAND_TOP, PATH_SEP );
    strcat( ANGBAND_TOP, "files" );
    strcat( ANGBAND_TOP, PATH_SEP );
    strcat( ANGBAND_TOP, "newscores" );
    ANGBAND_TOP = (char *)realloc( ANGBAND_TOP, strlen( ANGBAND_TOP ) + 1 );

    ANGBAND_BONES = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_BONES, angband_path );
    strcat( ANGBAND_BONES, PATH_SEP );
    strcat( ANGBAND_BONES, "bones" );
    ANGBAND_BONES = (char *)realloc( ANGBAND_BONES, strlen( ANGBAND_BONES ) + 1 );

    ANGBAND_HELP = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_HELP, angband_path );
    strcat( ANGBAND_HELP, PATH_SEP );
    strcat( ANGBAND_HELP, "files" );
    strcat( ANGBAND_HELP, PATH_SEP );
    strcat( ANGBAND_HELP, "roglcmds.hlp" );
    ANGBAND_HELP = (char *)realloc( ANGBAND_HELP, strlen( ANGBAND_HELP ) + 1 );

    ANGBAND_ORIG_HELP = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_ORIG_HELP, angband_path );
    strcat( ANGBAND_ORIG_HELP, PATH_SEP );
    strcat( ANGBAND_ORIG_HELP, "files" );
    strcat( ANGBAND_ORIG_HELP, PATH_SEP );
    strcat( ANGBAND_ORIG_HELP, "origcmds.hlp" );
    ANGBAND_ORIG_HELP = (char *)realloc(ANGBAND_ORIG_HELP,
					strlen( ANGBAND_ORIG_HELP ) + 1 );

    ANGBAND_WIZ_HELP = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_WIZ_HELP, angband_path );
    strcat( ANGBAND_WIZ_HELP, PATH_SEP );
    strcat( ANGBAND_WIZ_HELP, "files" );
    strcat( ANGBAND_WIZ_HELP, PATH_SEP );
    strcat( ANGBAND_WIZ_HELP, "rwizcmds.hlp" );
    ANGBAND_WIZ_HELP = (char *)realloc(ANGBAND_WIZ_HELP,
				       strlen( ANGBAND_WIZ_HELP ) + 1 );

    ANGBAND_OWIZ_HELP = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_OWIZ_HELP, angband_path );
    strcat( ANGBAND_OWIZ_HELP, PATH_SEP );
    strcat( ANGBAND_OWIZ_HELP, "files" );
    strcat( ANGBAND_OWIZ_HELP, PATH_SEP );
    strcat( ANGBAND_OWIZ_HELP, "owizcmds.hlp" );
    ANGBAND_OWIZ_HELP = (char *)realloc(ANGBAND_OWIZ_HELP,
					strlen( ANGBAND_OWIZ_HELP ) + 1 );

    ANGBAND_WELCOME = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_WELCOME, angband_path );
    strcat( ANGBAND_WELCOME, PATH_SEP );
    strcat( ANGBAND_WELCOME, "files" );
    strcat( ANGBAND_WELCOME, PATH_SEP );
    strcat( ANGBAND_WELCOME, "welcome.hlp" );
    ANGBAND_WELCOME = (char *)realloc( ANGBAND_WELCOME, strlen( ANGBAND_WELCOME ) + 1 );

    ANGBAND_LOG = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_LOG, angband_path );
    strcat( ANGBAND_LOG, PATH_SEP );
    strcat( ANGBAND_LOG, "files" );
    strcat( ANGBAND_LOG, PATH_SEP );
    strcat( ANGBAND_LOG, "ANGBAND.log" );
    ANGBAND_LOG = (char *)realloc( ANGBAND_LOG, strlen( ANGBAND_LOG ) + 1 );

    ANGBAND_VER = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_VER, angband_path );
    strcat( ANGBAND_VER, PATH_SEP );
    strcat( ANGBAND_VER, "files" );
    strcat( ANGBAND_VER, PATH_SEP );
    strcat( ANGBAND_VER, "version.hlp" );
    ANGBAND_VER = (char *)realloc( ANGBAND_VER, strlen( ANGBAND_VER ) + 1 );

    ANGBAND_LOAD = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_LOAD, angband_path );
    strcat( ANGBAND_LOAD, PATH_SEP );
    strcat( ANGBAND_LOAD, "files" );
    strcat( ANGBAND_LOAD, PATH_SEP );
    strcat( ANGBAND_LOAD, "loadcheck" );
    ANGBAND_LOAD = (char *)realloc( ANGBAND_LOAD, strlen( ANGBAND_LOAD ) + 1 );

    ANGBAND_WIZ = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_WIZ, angband_path );
    strcat( ANGBAND_WIZ, PATH_SEP );
    strcat( ANGBAND_WIZ, "files" );
    strcat( ANGBAND_WIZ, PATH_SEP );
    strcat( ANGBAND_WIZ, "wizards" );
    ANGBAND_WIZ = (char *)realloc( ANGBAND_WIZ, strlen( ANGBAND_WIZ ) + 1 );

    ANGBAND_SAV = (char *)malloc( MAXPATHLEN );
    strcpy( ANGBAND_SAV, angband_path );
    strcat( ANGBAND_SAV, PATH_SEP );
    strcat( ANGBAND_SAV, "save" );
    ANGBAND_SAV = (char *)realloc( ANGBAND_SAV, strlen( ANGBAND_SAV ) + 1 );

    return;
}

#ifdef BOZO_MAXPATHLEN
#undef BOZO_MAXPATHLEN
#undef MAXPATHLEN
#endif /* BOZO_MAXPATHLEN */

#endif /* NEW_FILEPATHS */


/* Not touched for Mac port */
/*
 * init_scorefile Open the score file while we still have the setuid
 * privileges.  Later when the score is being written out, you must be sure
 * to flock the file so we don't have multiple people trying to write to it
 * at the same time. Craig Norborg (doc)		Mon Aug 10 16:41:59
 * EST 1987 
 */
void 
init_scorefile()
{
#ifdef SET_UID
    if (1 > (highscore_fd = my_topen(ANGBAND_TOP, O_RDWR | O_CREAT, 0644)))
#else
    if (1 > (highscore_fd = my_topen(ANGBAND_TOP, O_RDWR | O_CREAT, 0666)))
#endif
    {
	(void)fprintf(stderr, "Can't open score file \"%s\"\n", ANGBAND_TOP);
	exit(1);
    }
}


/* Attempt to open the intro file			-RAK-	 */
/* This routine also checks the hours file vs. what time it is	-Doc */
void 
read_times()
{
    register int i;
    vtype        in_line;
    FILE        *file1;

#ifdef CHECKHOURS
/* Attempt to read hours.dat.	 If it does not exist,	   */
/* inform the user so he can tell the wizard about it	 */
    if ((file1 = my_tfopen(ANGBAND_HOU, "r")) != NULL) {
	while (fgets(in_line, 80, file1) != NULL)
	    if (strlen(in_line) > 3) {
		if (!strncmp(in_line, "SUN:", 4))
		    (void)strcpy(days[0], in_line);
		else if (!strncmp(in_line, "MON:", 4))
		    (void)strcpy(days[1], in_line);
		else if (!strncmp(in_line, "TUE:", 4))
		    (void)strcpy(days[2], in_line);
		else if (!strncmp(in_line, "WED:", 4))
		    (void)strcpy(days[3], in_line);
		else if (!strncmp(in_line, "THU:", 4))
		    (void)strcpy(days[4], in_line);
		else if (!strncmp(in_line, "FRI:", 4))
		    (void)strcpy(days[5], in_line);
		else if (!strncmp(in_line, "SAT:", 4))
		    (void)strcpy(days[6], in_line);
	    }
	(void)fclose(file1);
    } else {
	restore_term();
	(void)fprintf(stderr, "There is no hours file \"%s\".\nPlease inform the wizard, %s, so he can correct this!\n", ANGBAND_HOU, WIZARD);
	exit(1);
    }

/* Check the hours, if closed	then exit. */
    if (!check_time()) {
	if ((file1 = my_tfopen(ANGBAND_HOU, "r")) != NULL) {
	    clear_screen();
	    for (i = 0; fgets(in_line, 80, file1) != NULL; i++)
		put_buffer(in_line, i, 0);
	    (void)fclose(file1);
	    pause_line(23);
	}
	exit_game();
    }
#endif				   /* CHECK_HOURS */

/* Print the introduction message, news, etc.		 */
    if ((file1 = my_tfopen(ANGBAND_MOR, "r")) != NULL) {
	clear_screen();
	for (i = 0; fgets(in_line, 80, file1) != NULL; i++)
	    put_buffer(in_line, i, 0);
	pause_line(23);
	(void)fclose(file1);
    }
}

/*
 * File perusal.	    -CJS- primitive, but portable 
 */
void 
helpfile(filename)
const char *filename;
{
    bigvtype tmp_str;
    FILE    *file;
    char     input;
    int      i;

    file = my_tfopen(filename, "r");
    if (file == NULL) {
	(void)sprintf(tmp_str, "Can not find help file \"%s\".\n", filename);
	prt(tmp_str, 0, 0);
	return;
    }
    save_screen();

    while (!feof(file)) {
	clear_screen();
	for (i = 0; i < 23; i++)
	    if (fgets(tmp_str, BIGVTYPESIZ - 1, file) != NULL)
		put_buffer(tmp_str, i, 0);
	prt("[Press any key to continue.]", 23, 23);
	input = inkey();
	if (input == ESCAPE)
	    break;
    }

    (void)fclose(file);
    restore_screen();
}

/* Prints a list of random objects to a file.  Note that -RAK-	 */
/* the objects produced is a sampling of objects which		 */
/* be expected to appear on that level.				 */
void 
print_objects()
{
    register int         i;
    int                  nobj, j, level;
    vtype                filename1;
    bigvtype             tmp_str;
    register FILE       *file1;
    register inven_type *i_ptr;

    prt("Produce objects on what level?: ", 0, 0);
    level = 0;
    if (!get_string(tmp_str, 0, 32, 10))
	return;
    level = atoi(tmp_str);
    prt("Produce how many objects?: ", 0, 0);
    nobj = 0;
    if (!get_string(tmp_str, 0, 27, 10))
	return;
    nobj = atoi(tmp_str);
    if ((nobj > 0) && (level > -1) && (level < 1201)) {
	if (nobj > 10000)
	    nobj = 10000;
	prt("File name: ", 0, 0);
	if (get_string(filename1, 0, 11, 64)) {
	    if (strlen(filename1) == 0)
		return;
	    if ((file1 = my_tfopen(filename1, "w")) != NULL) {
		(void)sprintf(tmp_str, "%d", nobj);
		prt(strcat(tmp_str, " random objects being produced..."), 0, 0);
		put_qio();
		(void)fprintf(file1, "*** Random Object Sampling:\n");
		(void)fprintf(file1, "*** %d objects\n", nobj);
		(void)fprintf(file1, "*** For Level %d\n", level);
		(void)fprintf(file1, "\n");
		(void)fprintf(file1, "\n");
		j = popt();
		for (i = 0; i < nobj; i++) {
		    if (randint(MAX_DUNGEON_OBJ) == 1) {
			int                 tmp;

		again:
			tmp = randint(MAX_OBJECTS - (SPECIAL_OBJ - 1)) +
			    (SPECIAL_OBJ - 1) - 1;
			switch (tmp) {
			  case (SPECIAL_OBJ - 1):
			    if (object_list[tmp].level <= level)
				msg_print("*Remove Curse*");
			    break;
			  case (SPECIAL_OBJ):
			    if (object_list[tmp].level <= level)
				msg_print("[Resistance of Scarabtarices]");
			    break;
			  case (SPECIAL_OBJ + 1):
			    if (object_list[tmp].level <= level)
				msg_print("[Mordenkainen's Escapes]");
			    break;
			  case (SPECIAL_OBJ + 2):
			    if (object_list[tmp].level <= level)
				msg_print("[Kelek's Grimoire of Power]");
			    break;
			  case (SPECIAL_OBJ + 3):
			    if (randint(2) == 1)
				goto again;
			    if (object_list[tmp].level <= level)
				msg_print("[Tenser's Transformations]");
			    break;
			  case (SPECIAL_OBJ + 4):
			    if (randint(2) == 1)
				goto again;
			    if (object_list[tmp].level <= level)
				msg_print("[Raal's Tome of Destruction]");
			    break;
			  case (SPECIAL_OBJ + 5):
			    if (object_list[tmp].level <= level)
				msg_print("[Ethereal Openings]");
			    break;
			  case (SPECIAL_OBJ + 6):
			    if (object_list[tmp].level <= level)
				msg_print("[Godly Insights]");
			    break;
			  case (SPECIAL_OBJ + 7):
			    if (object_list[tmp].level <= level)
				msg_print("[Purifications and Healing]");
			    break;
			  case (SPECIAL_OBJ + 8):
			    if (object_list[tmp].level <= level)
				msg_print("[Wrath of God]");
			    break;
			  case (SPECIAL_OBJ + 9):
			    if (randint(2) > 1)
				goto again;
			    if (object_list[tmp].level <= level)
				msg_print("[Holy Infusions]");
			    break; /* End of Books */
			  case (SPECIAL_OBJ + 10):	/* Narya */
			    if (randint(4) > 1)
				goto again;
			    if (NARYA)
				goto again;
			    if (object_list[tmp].level <= level)
				msg_print("Narya");
			    NARYA = TRUE;
			    break;
			  case (SPECIAL_OBJ + 11):	/* Nenya */
			    if (randint(5) > 1)
				goto again;
			    if (NENYA)
				goto again;
			    if (object_list[tmp].level <= level)
				msg_print("Nenya");
			    NENYA = TRUE;
			    break;
			  case (SPECIAL_OBJ + 12):	/* Vilya */
			    if (randint(6) > 1)
				goto again;
			    if (VILYA)
				goto again;
			    if (object_list[tmp].level <= level)
				msg_print("Vilya");
			    VILYA = TRUE;
			    break;
			  case (SPECIAL_OBJ + 13):	/* The Ring of Power */
			    if (randint(10) > 1)
				goto again;
			    if (POWER)
				goto again;
			    if (object_list[tmp].level <= level)
				msg_print("Power (The One Ring)");
			    POWER = TRUE;
			    break;
			  case (SPECIAL_OBJ + 14):	/* Dragon Scale mails */
			  case (SPECIAL_OBJ + 15):
			  case (SPECIAL_OBJ + 16):
			  case (SPECIAL_OBJ + 17):
			  case (SPECIAL_OBJ + 18):
			  case (SPECIAL_OBJ + 19):
			  case (SPECIAL_OBJ + 20):
			    if (randint(2) > 1)
				goto again;
			    if (object_list[tmp].level <= level)
				msg_print("Dragon Scale Mail");
			    break;
			  case (SPECIAL_OBJ + 23):	/* Multi-Hued Dragon
							 * Scale Mail */
			    if (randint(3) > 1)
				goto again;
			    if (object_list[tmp].level <= level)
				msg_print("Multi-Hued Dragon Scale Mail");
			    break;
			}
			if (object_list[tmp].level > level)
			    continue;
			invcopy(&t_list[j], tmp);
			if (object_list[t_list[j].index].tval == TV_HARD_ARMOR)
			    t_list[j].toac += 10 + randint(10);
			i_ptr = &t_list[j];
			store_bought(i_ptr);
			objdes(tmp_str, i_ptr, TRUE);
		    } else {
			invcopy(&t_list[j],
				sorted_objects[get_obj_num(level, FALSE)]);
			magic_treasure(j, level, 0, FALSE);
			i_ptr = &t_list[j];
			store_bought(i_ptr);
			if (i_ptr->flags & TR_CURSED)
			    add_inscribe(i_ptr, ID_DAMD);
			objdes(tmp_str, i_ptr, TRUE);
		    }
		    (void)fprintf(file1, "%d %s.\n", i_ptr->level, tmp_str);
		}
		pusht((int16) j);
		(void)fclose(file1);
		prt("Completed.", 0, 0);
	    } else
		prt("File could not be opened.", 0, 0);
	}
    } else
	prt("Parameters no good.", 0, 0);
}


/* Print the character to a file or device		-RAK-	 */
int 
file_character(filename1)
char *filename1;
{
    register int          i;
    int                   j, xbth, xbthb, xfos, xsrh, xstl, xdis, xsave, xdev;
    vtype                 xinfra;
    int                   fd;
    register FILE        *file1;
    bigvtype              prt2;
    register struct misc *p_ptr;
    register inven_type  *i_ptr;
    vtype                 out_val, prt1;
    const char           *p, *colon, *blank;

    fd = my_topen(filename1, O_WRONLY | O_CREAT | O_EXCL, 0644);
    if (fd < 0 && errno == EEXIST) {
	(void)sprintf(out_val, "Replace existing file %s?", filename1);
	if (get_Yn(out_val))
	    fd = my_topen(filename1, O_WRONLY, 0644);
    }
    if (fd >= 0) {
    /*
     * on some non-unix machines, fdopen() is not reliable, hence must call
     * close() and then fopen() 
     */
	(void)close(fd);
	file1 = my_tfopen(filename1, "w");
    } else
	file1 = NULL;

    if (file1 != NULL) {
	prt("Writing character sheet...", 0, 0);
	put_qio();
	colon = ":";
	blank = " ";
	(void)fprintf(file1, "%c\n\n", CTRL('L'));
	(void)fprintf(file1, " Name%9s %-23s", colon, py.misc.name);
	(void)fprintf(file1, "Age%11s %6d ", colon, (int)py.misc.age);
	cnv_stat(py.stats.use_stat[A_STR], prt1);
	(void)fprintf(file1, "   STR : %s\n", prt1);
	(void)fprintf(file1, " Race%9s %-23s", colon, race[py.misc.prace].trace);
	(void)fprintf(file1, "Height%8s %6d ", colon, (int)py.misc.ht);
	cnv_stat(py.stats.use_stat[A_INT], prt1);
	(void)fprintf(file1, "   INT : %s\n", prt1);
	(void)fprintf(file1, " Sex%10s %-23s", colon,
		      (py.misc.male ? "Male" : "Female"));
	(void)fprintf(file1, "Weight%8s %6d ", colon, (int)py.misc.wt);
	cnv_stat(py.stats.use_stat[A_WIS], prt1);
	(void)fprintf(file1, "   WIS : %s\n", prt1);
	(void)fprintf(file1, " Class%8s %-23s", colon,
		      class[py.misc.pclass].title);
	(void)fprintf(file1, "Social Class : %6d ", py.misc.sc);
	cnv_stat(py.stats.use_stat[A_DEX], prt1);
	(void)fprintf(file1, "   DEX : %s\n", prt1);
	(void)fprintf(file1, " Title%8s %-23s", colon, title_string());
	(void)fprintf(file1, "%22s", blank);
	cnv_stat(py.stats.use_stat[A_CON], prt1);
	(void)fprintf(file1, "   CON : %s\n", prt1);
	(void)fprintf(file1, "%34s", blank);
	(void)fprintf(file1, "%26s", blank);
	cnv_stat(py.stats.use_stat[A_CHR], prt1);
	(void)fprintf(file1, "   CHR : %s\n\n", prt1);

	(void)fprintf(file1, " + To Hit    : %6d", py.misc.dis_th);
	(void)fprintf(file1, "%7sLevel      :%9d", blank, (int)py.misc.lev);
	(void)fprintf(file1, "   Max Hit Points : %6d\n", py.misc.mhp);
	(void)fprintf(file1, " + To Damage : %6d", py.misc.dis_td);
	(void)fprintf(file1, "%7sExperience :%9ld", blank, (long)py.misc.exp);
	(void)fprintf(file1, "   Cur Hit Points : %6d\n", py.misc.chp);
	(void)fprintf(file1, " + To AC     : %6d", py.misc.dis_tac);
	(void)fprintf(file1, "%7sMax Exp    :%9ld", blank, (long)py.misc.max_exp);
	(void)fprintf(file1, "   Max Mana%8s %6d\n", colon, py.misc.mana);
	(void)fprintf(file1, "   Total AC  : %6d", py.misc.dis_ac);
	if (py.misc.lev >= MAX_PLAYER_LEVEL)
	    (void)fprintf(file1, "%7sExp to Adv.:%9s", blank, "****");
	else
	    (void)fprintf(file1, "%7sExp to Adv.:%9ld", blank,
			  (long) (player_exp[py.misc.lev - 1] *
				   py.misc.expfact / 100));
	(void)fprintf(file1, "   Cur Mana%8s %6d\n", colon, py.misc.cmana);
	(void)fprintf(file1, "%28sGold%8s%9ld\n", blank, colon, (long)py.misc.au);

	p_ptr = &py.misc;
	xbth = p_ptr->bth + p_ptr->ptohit * BTH_PLUS_ADJ
	    + (class_level_adj[p_ptr->pclass][CLA_BTH] * p_ptr->lev);
	xbthb = p_ptr->bthb + p_ptr->ptohit * BTH_PLUS_ADJ
	    + (class_level_adj[p_ptr->pclass][CLA_BTHB] * p_ptr->lev);
    /* this results in a range from 0 to 29 */
	xfos = 40 - p_ptr->fos;
	if (xfos < 0)
	    xfos = 0;
	xsrh = p_ptr->srh;
    /* this results in a range from 0 to 9 */
	xstl = p_ptr->stl + 1;
	xdis = p_ptr->disarm + 2 * todis_adj() + stat_adj(A_INT)
	    + (class_level_adj[p_ptr->pclass][CLA_DISARM] * p_ptr->lev / 3);
	xsave = p_ptr->save + stat_adj(A_WIS)
	    + (class_level_adj[p_ptr->pclass][CLA_SAVE] * p_ptr->lev / 3);
	xdev = p_ptr->save + stat_adj(A_INT)
	    + (class_level_adj[p_ptr->pclass][CLA_DEVICE] * p_ptr->lev / 3);

	(void)sprintf(xinfra, "%d feet", py.flags.see_infra * 10);

	(void)fprintf(file1, "(Miscellaneous Abilities)\n\n");
	(void)fprintf(file1, " Fighting    : %-10s", likert(xbth, 12));
	(void)fprintf(file1, "   Stealth     : %-10s", likert(xstl, 1));
	(void)fprintf(file1, "   Perception  : %s\n", likert(xfos, 3));
	(void)fprintf(file1, " Bows/Throw  : %-10s", likert(xbthb, 12));
	(void)fprintf(file1, "   Disarming   : %-10s", likert(xdis, 8));
	(void)fprintf(file1, "   Searching   : %s\n", likert(xsrh, 6));
	(void)fprintf(file1, " Saving Throw: %-10s", likert(xsave, 6));
	(void)fprintf(file1, "   Magic Device: %-10s", likert(xdev, 6));
	(void)fprintf(file1, "   Infra-Vision: %s\n\n", xinfra);
    /* Write out the character's history     */
	(void)fprintf(file1, "Character Background\n");
	for (i = 0; i < 4; i++)
	    (void)fprintf(file1, " %s\n", py.misc.history[i]);
    /* Write out the equipment list.	     */
	j = 0;
	(void)fprintf(file1, "\n  [Character's Equipment List]\n\n");
	if (equip_ctr == 0)
	    (void)fprintf(file1, "  Character has no equipment in use.\n");
	else
	    for (i = INVEN_WIELD; i < INVEN_ARRAY_SIZE; i++) {
		i_ptr = &inventory[i];
		if (i_ptr->tval != TV_NOTHING) {
		    switch (i) {
		      case INVEN_WIELD:
			p = "You are wielding";
			break;
		      case INVEN_HEAD:
			p = "Worn on head";
			break;
		      case INVEN_NECK:
			p = "Worn around neck";
			break;
		      case INVEN_BODY:
			p = "Worn on body";
			break;
		      case INVEN_ARM:
			p = "Worn on shield arm";
			break;
		      case INVEN_HANDS:
			p = "Worn on hands";
			break;
		      case INVEN_RIGHT:
			p = "Right ring finger";
			break;
		      case INVEN_LEFT:
			p = "Left  ring finger";
			break;
		      case INVEN_FEET:
			p = "Worn on feet";
			break;
		      case INVEN_OUTER:
			p = "Worn about body";
			break;
		      case INVEN_LIGHT:
			p = "Light source is";
			break;
		      case INVEN_AUX:
			p = "Secondary weapon";
			break;
		      default:
			p = "*Unknown value*";
			break;
		    }
		    objdes(prt2, &inventory[i], TRUE);
		    (void)fprintf(file1, "  %c) %-19s: %s\n", j + 'a', p, prt2);
		    j++;
		}
	    }

    /* Write out the character's inventory.	     */
	(void)fprintf(file1, "%c\n\n", CTRL('L'));
	(void)fprintf(file1, "  [General Inventory List]\n\n");
	if (inven_ctr == 0)
	    (void)fprintf(file1, "  Character has no objects in inventory.\n");
	else {
	    for (i = 0; i < inven_ctr; i++) {
		objdes(prt2, &inventory[i], TRUE);
		(void)fprintf(file1, "%c) %s\n", i + 'a', prt2);
	    }
	}
	(void)fprintf(file1, "%c", CTRL('L'));

	fprintf(file1, "  [%s%s Home Inventory]\n\n", py.misc.name,
		(toupper(py.misc.name[strlen(py.misc.name)-1]) == 'S' ? "'" : "'s"));
	if (store[MAX_STORES-1].store_ctr == 0)
	    (void) fprintf(file1, "  Character has no objects at home.\n");
	else {
	    for (i = 0; i < store[MAX_STORES-1].store_ctr; i++) {
		if (i==12) fprintf(file1, "\n");  
		objdes(prt2, &store[MAX_STORES-1].store_inven[i].sitem, TRUE);
		(void) fprintf(file1, "%c) %s\n", (i%12)+'a', prt2);
	    }
	}
	(void) fprintf(file1, "%c", CTRL('L'));
	
	(void)fclose(file1);
	prt("Completed.", 0, 0);
	return TRUE;
    } else {
	if (fd >= 0)
	    (void)close(fd);
	(void)sprintf(out_val, "Can't open file %s:", filename1);
	msg_print(out_val);
	return FALSE;
    }
}
