/
Greed088/
Greed088/archive/
Greed088/backup/
Greed088/clans/
Greed088/classes/
Greed088/dict/
Greed088/doc/MSP/
Greed088/doc/MobProg2.1/
Greed088/doc/OLC11/
Greed088/doc/OLC11/doc/
Greed088/doc/OLC11/options/
Greed088/log/
Greed088/mobprogs/
Greed088/player/
Greed088/player/t/
/***************************************************************************
 *  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.                              *
 *                                                                         *
 *  Envy Diku Mud improvements copyright (C) 1994 by Michael Quan, David   *
 *  Love, Guilherme 'Willie' Arnold, and Mitchell Tse.                     *
 *                                                                         *
 *  EnvyMud 2.0 improvements copyright (C) 1995 by Michael Quan and        *
 *  Mitchell Tse.                                                          *
 *                                                                         *
 *  EnvyMud 2.2 improvements copyright (C) 1996, 1997 by Michael Quan.     *
 *                                                                         *
 *  GreedMud 0.88 improvements copyright (C) 1997, 1998 by Vasco Costa.    *
 *                                                                         *
 *  In order to use any part of this Envy Diku Mud, you must comply with   *
 *  the original Diku license in 'license.doc', the Merc license in        *
 *  'license.txt', as well as the Envy license in 'license.nvy'.           *
 *  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 <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"
#include "olc.h"

#if defined( sun )
#include <memory.h>
#endif

#if !defined( macintosh )
extern	int	_filbuf		args( (FILE *) );
#endif

#if defined( sun )
int     system          args( ( const char *string ) );
#endif


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


int stat;

/*
 * 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 ) );
int	fread_char	args( ( CHAR_DATA *ch,  FILE *fp ) );
int	envy_fread_obj	args( ( CHAR_DATA *ch,  FILE *fp ) );
int	fread_obj	args( ( CHAR_DATA *ch,  FILE *fp ) );


/* Courtesy of Yaz of 4th Realm */
char *initial( const char *str )
{
    static char strint [ MAX_STRING_LENGTH ];

    strint[0] = LOWER( str[ 0 ] );
    return strint;

}

/*
 * Backups a character and inventory.
 * Courtesy of Zen :)
 */
void backup_char_obj( CHAR_DATA *ch )
{
    FILE *fp;
    char  buf     [ MAX_STRING_LENGTH ];
    char  strsave [ MAX_INPUT_LENGTH  ];

    if ( IS_NPC( ch ) || ch->level < 2 )
	return;

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

    ch->save_time = current_time;
    fclose( fpReserve );

    /* player files parsed directories by Yaz 4th Realm */
#if !defined( macintosh ) && !defined( _WIN32 )
    sprintf( strsave, "%s%s%s%s", BACKUP_DIR, initial( ch->name ),
	    "/", capitalize( ch->name ) );
#else
    sprintf( strsave, "%s%s", BACKUP_DIR, capitalize( ch->name ) );
#endif
    if ( !( fp = fopen( strsave, "w" ) ) )
    {
        sprintf( buf, "Backup_char_obj: fopen %s: ", ch->name );
	bug( buf, 0 );
	perror( strsave );
    }
    else
    {
	fwrite_char( ch, fp );
	if ( ch->carrying )
	    fwrite_obj( ch, ch->carrying, fp, 0 );
	fprintf( fp, "#END\n" );
    }
    fclose( fp );
    fpReserve = fopen( NULL_FILE, "r" );
    return;
}

/*
 * Delete a character's file.
 * Used for retire & delete commands for now.
 * Courtesy of Zen :)
 */
void delete_char_obj( CHAR_DATA *ch )
{
    char  buf     [ MAX_STRING_LENGTH ];
    char  strsave [ MAX_INPUT_LENGTH  ];

    if ( IS_NPC( ch ) || ch->level < 2 )
	return;

    /* player files parsed directories by Yaz 4th Realm */
#if !defined( macintosh ) && !defined( _WIN32 )
    sprintf( strsave, "%s%s%s%s", PLAYER_DIR, initial( ch->name ),
	    "/", capitalize( ch->name ) );
#else
    sprintf( strsave, "%s%s", PLAYER_DIR, capitalize( ch->name ) );
#endif
    if ( remove( strsave ) )
    {
        sprintf( buf, "Delete_char_obj: remove %s: ", ch->name );
	bug( buf, 0 );
	perror( strsave );
    }
    return;
}

/*
 * 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 )
{
    FILE *fp;
    char  buf     [ MAX_STRING_LENGTH ];
    char  strsave [ MAX_INPUT_LENGTH  ];

    if ( IS_NPC( ch ) || ch->level < 2 )
	return;

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

    ch->save_time = current_time;
    fclose( fpReserve );

    /* player files parsed directories by Yaz 4th Realm */
#if !defined( macintosh ) && !defined( _WIN32 )
    sprintf( strsave, "%s%s%s%s", PLAYER_DIR, initial( ch->name ),
	    "/", capitalize( ch->name ) );
#else
    sprintf( strsave, "%s%s", PLAYER_DIR, capitalize( ch->name ) );
#endif
    if ( !( fp = fopen( strsave, "w" ) ) )
    {
        sprintf( buf, "Save_char_obj: fopen %s: ", ch->name );
	bug( buf, 0 );
	perror( strsave );
    }
    else
    {
	fwrite_char( ch, fp );
	if ( ch->carrying )
	    fwrite_obj( ch, ch->carrying, fp, 0 );
	fprintf( fp, "#END\n" );
    }
    fclose( fp );
    fpReserve = fopen( NULL_FILE, "r" );
    return;
}



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

    fprintf( fp, "#%s\n", IS_NPC( ch ) ? "MOB" : "PLAYER"		);

    fprintf( fp, "Nm          %s~\n",	ch->name			);
    fprintf( fp, "ShtDsc      %s~\n",	ch->short_descr			);
    fprintf( fp, "LngDsc      %s~\n",	ch->long_descr			);
    fprintf( fp, "Dscr        %s~\n",	fix_string( ch->description )	);
    fprintf( fp, "Prmpt       %s~\n",	ch->pcdata->prompt		);
    fprintf( fp, "Sx          %d\n",	ch->sex				);

    fprintf( fp, "Class      " );
    for ( sn = 0; sn < MAX_MULTICLASS; sn++ )
      fprintf( fp, " %s~", ch->class[sn] ? ch->class[sn]->name : "" );
    fprintf( fp, "\n" );

    fprintf( fp, "Race        %s~\n",	race_table[ ch->race ].name 	);

    fprintf( fp, "Lvl         %d\n",	ch->level			);
    fprintf( fp, "Trst        %d\n",	ch->trust			);
    fprintf( fp, "Playd       %d\n",
	ch->played + (int) ( current_time - ch->logon )			);
    fprintf( fp, "Note        %ld\n",   (unlong) ch->last_note		);
    fprintf( fp, "Room        %d\n",
	    (  ch->in_room == get_room_index( ROOM_VNUM_LIMBO )
	     && ch->was_in_room )
	    ? ch->was_in_room->vnum
	    : ch->in_room->vnum );

    fprintf( fp, "HpMnMv      %d %d %d %d %d %d\n",
	ch->hit, ch->max_hit, ch->mana, ch->max_mana, ch->move, ch->max_move );
    fprintf( fp, "Gold        %d\n",	ch->gold		);
    fprintf( fp, "Exp         %d\n",	ch->exp			);
    fprintf( fp, "ActF        %s~\n",   flag_strings( plr_flags, ch->act ) );
    fprintf( fp, "AffectedBy  %s~\n",	vect_strings( affect_flags, ch->affected_by ) );

    if ( ch->resistant )
	fprintf( fp, "Res         %d\n",	ch->resistant		);
    if ( ch->immune )
	fprintf( fp, "Imm         %d\n",	ch->immune			);
    if ( ch->susceptible )
	fprintf( fp, "Susc        %d\n",	ch->susceptible		);

    fprintf( fp, "Langs       %d %d\n",		ch->speaks, ch->speaking );

    /* Bug fix from Alander */
    fprintf( fp, "Pos         %d\n",
	    ch->position == POS_FIGHTING ? POS_STANDING : ch->position );

    fprintf( fp, "Prac        %d\n",	ch->practice		);
    fprintf( fp, "SavThr      %d\n",	ch->saving_throw	);
    fprintf( fp, "Align       %d\n",	ch->alignment		);
    fprintf( fp, "Hit         %d\n",	ch->hitroll		);
    fprintf( fp, "Dam         %d\n",	ch->damroll		);
    fprintf( fp, "Armr        %d\n",	ch->armor		);
    fprintf( fp, "Wimp        %d\n",	ch->wimpy		);
    fprintf( fp, "Deaf        %d\n",	ch->deaf		);

    if ( IS_NPC( ch ) )
    {
	fprintf( fp, "Vnum        %d\n",	ch->pIndexData->vnum	);
    }
    else
    {
	fprintf( fp, "Paswd       %s~\n",	ch->pcdata->pwd		);
	fprintf( fp, "Bmfin       %s~\n",	ch->pcdata->bamfin	);
	fprintf( fp, "Bmfout      %s~\n",	ch->pcdata->bamfout	);
	fprintf( fp, "Immskll     %s~\n",	ch->pcdata->immskll	);
	fprintf( fp, "Wiznet      %d\n",	ch->pcdata->wiznet	);
	fprintf( fp, "Ttle        %s~\n",	ch->pcdata->title	);
	fprintf( fp, "AtrPrm      %d %d %d %d %d\n",
		ch->pcdata->perm_str,
		ch->pcdata->perm_int,
		ch->pcdata->perm_wis,
		ch->pcdata->perm_dex,
		ch->pcdata->perm_con );

	fprintf( fp, "AtrMd       %d %d %d %d %d\n",
		ch->pcdata->mod_str, 
		ch->pcdata->mod_int, 
		ch->pcdata->mod_wis,
		ch->pcdata->mod_dex, 
		ch->pcdata->mod_con );

	fprintf( fp, "Cond        %d %d %d\n",
		ch->pcdata->condition[0],
		ch->pcdata->condition[1],
		ch->pcdata->condition[2] );

	fprintf( fp, "Security    %d\n",   ch->pcdata->security		);

	if ( is_clan( ch ) )
	    fprintf( fp, "PClan       %2d %s~\n",
		    ch->pcdata->rank,
		    ch->pcdata->clan->name );

	if ( ch->pcdata->pkills )
	    fprintf( fp, "PKills      %d\n", ch->pcdata->pkills	);
	if ( ch->pcdata->pdeaths )
	    fprintf( fp, "PDeaths     %d\n", ch->pcdata->pdeaths	);
	if ( ch->pcdata->illegal_pk )
	    fprintf( fp, "IllegalPK   %d\n", ch->pcdata->illegal_pk	);

	fprintf( fp, "MKills      %d\n",   ch->pcdata->mkills		);
	fprintf( fp, "MDeaths     %d\n",   ch->pcdata->mdeaths		);

	fprintf( fp, "Pglen       %d\n",   ch->pcdata->pagelen		);

        for ( alias = ch->pcdata->alias_list; alias; alias = alias->next )
	    fprintf( fp, "NAlias      '%s' %s~\n", alias->cmd, alias->subst );

	for ( sn = 0; sn < MAX_SKILL; sn++ )
	{
	    if ( skill_table[sn].name && ch->pcdata->learned[sn] > 0 )
	    {
		fprintf( fp, "Skll        %d '%s'\n",
		    ch->pcdata->learned[sn], skill_table[sn].name );
	    }
	}
    }

    for ( paf = ch->affected; paf; paf = paf->next )
    {
        if ( paf->deleted )
	    continue;

	fprintf( fp, "Affect     %18s~ %3d %3d %s~ %s~\n",
		skill_table[ paf->type ].name,
		paf->duration,
		paf->modifier,
		flag_strings( apply_flags, paf->location ),
		vect_strings( affect_flags, paf->bitvector ) );
    }

    fprintf( fp, "End\n\n" );
    return;
}



/*
 * Write an object and its contents.
 */
void fwrite_obj( CHAR_DATA *ch, OBJ_DATA *obj, FILE *fp, int iNest )
{
    AFFECT_DATA      *paf;
    EXTRA_DESCR_DATA *ed;

    /*
     * Slick recursion to write lists backwards,
     *   so loading them will load in forwards order.
     */
    if ( obj->next_content )
	fwrite_obj( ch, obj->next_content, fp, iNest );

    /*
     * Castrate storage characters.
     */
    if ( ch->level < obj->level
	|| obj->item_type == ITEM_KEY
	|| obj->deleted )
	return;

    fprintf( fp, "#OBJ\n" );
    fprintf( fp, "Nest         %d\n",	iNest			     );
    fprintf( fp, "Name         %s~\n",	obj->name		     );
    fprintf( fp, "ShortDescr   %s~\n",	obj->short_descr	     );
    fprintf( fp, "Description  %s~\n",	obj->description	     );
    fprintf( fp, "Vnum         %d\n",	obj->pIndexData->vnum	     );

    if ( obj->spec_fun )
      fprintf( fp, "Special      %s\n",	spec_obj_string( obj->spec_fun ) );

    fprintf( fp, "ExtraFlags   %d\n",	obj->extra_flags	     );
    fprintf( fp, "WearFlags    %d\n",	obj->wear_flags		     );
    fprintf( fp, "WearLoc      %d\n",	obj->wear_loc		     );
    fprintf( fp, "ItemType     %d\n",	obj->item_type		     );
    fprintf( fp, "Weight       %d\n",	obj->weight		     );
    fprintf( fp, "Level        %d\n",	obj->level		     );
    fprintf( fp, "Timer        %d\n",	obj->timer		     );
    fprintf( fp, "Cost         %d\n",	obj->cost		     );
    fprintf( fp, "Values       %d %d %d %d %d\n",
	obj->value[0], obj->value[1], obj->value[2], obj->value[3],
							obj->value[4]   );

    switch ( obj->item_type )
    {
    case ITEM_POTION:
    case ITEM_SCROLL:
	if ( obj->value[1] > 0 )
	{
	    fprintf( fp, "Spell 1      '%s'\n", 
		skill_table[obj->value[1]].name );
	}

	if ( obj->value[2] > 0 )
	{
	    fprintf( fp, "Spell 2      '%s'\n", 
		skill_table[obj->value[2]].name );
	}

	if ( obj->value[3] > 0 )
	{
	    fprintf( fp, "Spell 3      '%s'\n", 
		skill_table[obj->value[3]].name );
	}

	if ( obj->value[4] > 0 )
	{
	    fprintf( fp, "Spell 4      '%s'\n", 
		skill_table[obj->value[4]].name );
	}

	break;

    case ITEM_PILL:
    case ITEM_STAFF:
    case ITEM_WAND:
	if ( obj->value[3] > 0 )
	{
	    fprintf( fp, "Spell 3      '%s'\n", 
		skill_table[obj->value[3]].name );
	}

	break;
    }

    for ( paf = obj->affected; paf; paf = paf->next )
    {
	fprintf( fp, "Affect       %d %d %d %d %s~\n",
		paf->type,
		paf->duration,
		paf->modifier,
		paf->location,
		vect_strings( affect_flags, paf->bitvector ) );
    }

    for ( ed = obj->extra_descr; ed; ed = ed->next )
    {
	fprintf( fp, "ExtraDescr   %s~ %s~\n",
		ed->keyword, ed->description );
    }

    fprintf( fp, "End\n\n" );

    if ( obj->contains )
	fwrite_obj( ch, obj->contains, fp, iNest + 1 );

    tail_chain();
    return;
}



/*
 * Load a char and inventory into a new ch structure.
 */
bool load_char_obj( DESCRIPTOR_DATA *d, char *name )
{
    FILE      *fp;
    CHAR_DATA *ch;
    char       strsave [ MAX_INPUT_LENGTH ];
    bool       found;
    char       sorry_player [] =
      "********************************************************\n\r"
      "** One or more of the critical fields in your player  **\n\r"
      "** file were corrupted since you last played.  Please **\n\r"
      "** contact an administrator or programmer to	     **\n\r"
      "** investigate the recovery of your characters.       **\n\r"
      "********************************************************\n\r";
    char       sorry_object [] =
      "********************************************************\n\r"
      "** One or more of the critical fields in your player  **\n\r"
      "** file were corrupted leading to the loss of one or  **\n\r"
      "** more of your possessions.			     **\n\r"
      "********************************************************\n\r";


    ch					= new_character( TRUE );

    d->character			= ch;
    ch->desc				= d;
    ch->name				= str_dup( name );
    ch->pcdata->prompt                  = str_dup( daPrompt );
    ch->last_note                       = 0;
    ch->act				= PLR_BLANK
					| PLR_COMBINE
					| PLR_PROMPT
					| PLR_PAGER;
    ch->pcdata->pwd			= str_dup( "" );
    ch->pcdata->pwdnew			= str_dup( "" );
    ch->pcdata->bamfin			= str_dup( "" );
    ch->pcdata->bamfout			= str_dup( "" );
    ch->pcdata->immskll			= str_dup( "" );
    ch->pcdata->title			= str_dup( "" );
    ch->pcdata->perm_str		= 13;
    ch->pcdata->perm_int		= 13; 
    ch->pcdata->perm_wis		= 13;
    ch->pcdata->perm_dex		= 13;
    ch->pcdata->perm_con		= 13;
    ch->pcdata->condition[COND_THIRST]	= 48;
    ch->pcdata->condition[COND_FULL]	= 48;
    ch->pcdata->pagelen                 = 20;
    ch->pcdata->security		= 0;
    ch->pcdata->rank			= 0;
    ch->pcdata->clan	                = NULL;
    ch->pcdata->wiznet			= 0;

    ch->pcdata->switched                = FALSE;

    found = FALSE;
    fclose( fpReserve );

    /* parsed player file directories by Yaz of 4th Realm */
    /* decompress if .gz file exists - Thx Alander */
#if !defined( macintosh ) && !defined( _WIN32 )
    sprintf( strsave, "%s%s%s%s%s", PLAYER_DIR, initial( ch->name ),
	    "/", capitalize( name ), ".gz" );
    if ( ( fp = fopen( strsave, "r" ) ) )
    {
        char       buf     [ MAX_STRING_LENGTH ];

	fclose( fp );
	sprintf( buf, "gzip -dfq %s", strsave );
	system( buf );
    }
#endif

#if !defined( macintosh ) && !defined( _WIN32 )
    sprintf( strsave, "%s%s%s%s", PLAYER_DIR, initial( ch->name ),
	    "/", capitalize( name ) );
#else
    sprintf( strsave, "%s%s", PLAYER_DIR, capitalize( name ) );
#endif
    if ( ( fp = fopen( strsave, "r" ) ) )
    {
	char buf[ MAX_STRING_LENGTH ];
	int iNest;

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

	found = TRUE;
	for ( ; ; )
	{
	    char *word;
	    int   letter;
	    int   status;

	    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, &status );

	    if ( !str_cmp( word, "PLAYER" ) )
	    {
	        if ( fread_char ( ch, fp ) )
		{
		    sprintf( buf,
			    "Load_char_obj:  %s section PLAYER corrupt.\n\r",
			    name );
		    bug( buf, 0 );
		    write_to_buffer( d, sorry_player, 0 );

		    /* 
		     * In case you are curious,
		     * it is ok to leave ch alone for close_socket
		     * to free.
		     * We want to now kick the bad character out as
		     * what we are missing are MANDATORY fields.  -Kahn
		     */
		    SET_BIT( ch->act, PLR_DENY );
		    return TRUE;
		}
	    }
	    else if ( !str_cmp( word, "OBJECT" ) )
	    {
	        if ( !envy_fread_obj  ( ch, fp ) )
		{
		    sprintf( buf,
			    "Load_char_obj:  %s section OBJECT corrupt.\n\r",
			    name );
		    bug( buf, 0 );
		    write_to_buffer( d, sorry_object, 0 );
		    return FALSE;
		}
	    }
	    else if ( !str_cmp( word, "OBJ" ) )
	    {
	        if ( !fread_obj  ( ch, fp ) )
		{
		    sprintf( buf,
			    "Load_char_obj:  %s section OBJ corrupt.\n\r",
			    name );
		    bug( buf, 0 );
		    write_to_buffer( d, sorry_object, 0 );
		    return FALSE;
		}
	    }
	    else if ( !str_cmp( word, "END"    ) ) break;
	    else
	    {
		bug( "Load_char_obj: bad section.", 0 );
		break;
	    }
	} /* for */

	fclose( fp );
    }

    fpReserve = fopen( NULL_FILE, "r" );
    return found;
}



/*
 * Read in a char.
 */

int fread_char( CHAR_DATA *ch, FILE *fp )
{
    char        *word;
    char        buf [ MAX_STRING_LENGTH ];
    AFFECT_DATA *paf;
    int         sn;
    int         i;
    int         j;
    int         error_count = 0;
    int         status;
    int         status1;
    char        *p;
    int         tmpi;
    int         num_keys;
    int         last_key = 0;

    char        def_sdesc  [] = "Your short description was corrupted.";
    char        def_ldesc  [] = "Your long description was corrupted.";
    char        def_desc   [] = "Your description was corrupted.";
    char        def_title  [] = "Your title was corrupted.";

    struct key_data key_tab [] = {
      { "ShtDsc", TRUE,  (unlong) &def_sdesc,	{ &ch->short_descr,   NULL } },
      { "LngDsc", TRUE,  (unlong) &def_ldesc,	{ &ch->long_descr,    NULL } },
      { "Dscr",   TRUE,  (unlong) &def_desc,	{ &ch->description,   NULL } },
      { "Prmpt",  TRUE,  (unlong) &daPrompt,	{ &ch->pcdata->prompt,NULL } },
      { "Sx",     FALSE, SEX_MALE,		{ &ch->sex,           NULL } },
      { "Lvl",    FALSE, MAND,			{ &ch->level,         NULL } },
      { "Trst",   FALSE, 0,			{ &ch->trust,         NULL } },
      { "Playd",  FALSE, 0,			{ &ch->played,        NULL } },
      { "Note",   FALSE, 0,			{ &ch->last_note,     NULL } },
      { "HpMnMv", FALSE, MAND,			{ &ch->hit,
						  &ch->max_hit,
						  &ch->mana,
						  &ch->max_mana,
						  &ch->move,
						  &ch->max_move,      NULL } },
      { "Gold",   FALSE, 0,			{ &ch->gold,          NULL } },
      { "Exp",    FALSE, MAND,			{ &ch->exp,           NULL } },
      { "Act",    FALSE, DEFLT,			{ &ch->act,           NULL } },
      { "Res",    FALSE, 0,			{ &ch->resistant,     NULL } },
      { "Imm",    FALSE, 0,			{ &ch->immune,        NULL } },
      { "Susc",   FALSE, 0,			{ &ch->susceptible,   NULL } },
      { "Langs",  FALSE, 0,			{ &ch->speaks,
						  &ch->speaking,      NULL } },
      { "Pos",    FALSE, POS_STANDING, 		{ &ch->position,      NULL } },
      { "Prac",   FALSE, MAND,			{ &ch->practice,      NULL } },
      { "SavThr", FALSE, MAND,			{ &ch->saving_throw,  NULL } },
      { "Align",  FALSE, 0,			{ &ch->alignment,     NULL } },
      { "Hit",    FALSE, MAND,			{ &ch->hitroll,       NULL } },
      { "Dam",    FALSE, MAND,			{ &ch->damroll,       NULL } },
      { "Armr",   FALSE, MAND,			{ &ch->armor,         NULL } },
      { "Wimp",   FALSE, 10,			{ &ch->wimpy,         NULL } },
      { "Deaf",   FALSE, 0,			{ &ch->deaf,          NULL } },
      { "Paswd",  TRUE,  MAND,			{ &ch->pcdata->pwd,   NULL } },
      { "Bmfin",  TRUE,  DEFLT,			{ &ch->pcdata->bamfin,
						                      NULL } },
      { "Bmfout", TRUE,  DEFLT,			{ &ch->pcdata->bamfout,
						                      NULL } },
      { "Immskll",TRUE,  DEFLT,			{ &ch->pcdata->immskll,
						                      NULL } },
      { "Wiznet", FALSE,  0,			{ &ch->pcdata->wiznet,
						                      NULL } },
      { "Ttle",   TRUE,  (unlong) &def_title,	{ &ch->pcdata->title, NULL } },
      { "AtrPrm", FALSE, MAND,			{ &ch->pcdata->perm_str,
						  &ch->pcdata->perm_int,
						  &ch->pcdata->perm_wis,
						  &ch->pcdata->perm_dex,
						  &ch->pcdata->perm_con,
						                      NULL } },
      { "AtrMd",  FALSE, MAND,			{ &ch->pcdata->mod_str,
						  &ch->pcdata->mod_int,
						  &ch->pcdata->mod_wis,
						  &ch->pcdata->mod_dex,
						  &ch->pcdata->mod_con,
						                      NULL } },
      { "Cond",   FALSE, DEFLT,			{ &ch->pcdata->condition [0],
						  &ch->pcdata->condition [1],
						  &ch->pcdata->condition [2],
						                      NULL } },
      { "Security", FALSE, DEFLT,             { &ch->pcdata->security,
								      NULL } },
      { "PKills",	FALSE, 0,		{ &ch->pcdata->pkills,
						                      NULL } },
      { "PDeaths",	FALSE, 0,		{ &ch->pcdata->pdeaths,
						                      NULL } },
      { "IllegalPK",	FALSE, 0,		{ &ch->pcdata->illegal_pk,
						                      NULL } },
      { "MKills",	FALSE, 0,		{ &ch->pcdata->mkills,
						                      NULL } },
      { "MDeaths",	FALSE, 0,		{ &ch->pcdata->mdeaths,
						                      NULL } },
      { "Pglen",  FALSE, 20,			{ &ch->pcdata->pagelen,
						                      NULL } },
      { "\0",     FALSE, 0                                                 } };


    for ( num_keys = 0; *key_tab [num_keys].key; )
        num_keys++;

    for ( ; !feof (fp) ; )
    {

        word = fread_word( fp, &status );

        if ( !word )
	{
            bug( "fread_char:  Error reading key.  EOF?", 0 );
            fread_to_eol( fp );
            break;
	}

                /* This little diddy searches for the keyword
                   from the last keyword found */

        for ( i = last_key;
              i < last_key + num_keys &&
                str_cmp (key_tab [i % num_keys].key, word); )
            i++;

        i = i % num_keys;

        if ( !str_cmp (key_tab [i].key, word) )
            last_key = i;
        else
            i = num_keys;

        if ( *key_tab [i].key )         /* Key entry found in key_tab */
	{
            if ( key_tab [i].string == SPECIFIED )
                bug ("Key already specified.", 0);

                        /* Entry is a string */

            else
	      if ( key_tab [i].string )
	      {
                  if ( ( p = fread_string( fp, &status ) ) && !status )
		  {
		      free_string ( *(char **)key_tab [i].ptrs [0] );
		      *(char **)key_tab [i].ptrs [0] = p;
		  }
	      }

                        /* Entry is an integer */
            else
                for ( j = 0; key_tab [i].ptrs [j]; j++ )
		{
                    tmpi = fread_number ( fp, &status );
                    if ( !status )
                        *(int *)key_tab [i].ptrs [j] = tmpi;
		}

            if ( status )
	    {
                fread_to_eol( fp );
                continue;
	    }
	    else
                key_tab [i].string = SPECIFIED;
	}

        else if ( *word == '*' || !str_cmp( word, "Nm" ) )
            fread_to_eol( fp );

        else if ( !str_cmp( word, "End" ) )
            break;

        else if ( !str_cmp( word, "ActF" ) )
	  {
	      ch->act = fread_flag( fp, plr_flags );
	  }

        else if ( !str_cmp( word, "AffdBy" ) )
	  {
	      fread_vector( fp, ch->affected_by );
	  }

        else if ( !str_cmp( word, "AffectedBy" ) )
	  {
	      vcopy( ch->affected_by, fread_vect( fp, affect_flags, &status ) );
	  }

        else if ( !str_cmp( word, "Class" ) )
	  {
	      for ( i = 0; i < MAX_MULTICLASS; i++ )
	      {
		  temp_fread_string( fp, buf );

		  ch->class[i] = class_lookup( buf );
	      }
	  }

        else if ( !str_cmp( word, "Room" ) )
	  {
	      ch->in_room = get_room_index( fread_number( fp, &status ) );
	      if ( !ch->in_room )
                  ch->in_room = get_room_index( ROOM_VNUM_LIMBO );
	  }

	else if ( !str_cmp( word, "Race" ) )
	  {
	      i  = race_lookup( fread_string( fp, &status ) );

	      if ( status )
	      {
		  bug( "Fread_char: Error reading Race.", 0 );
		  fread_to_eol( fp );
		  continue;
	      }

	      if ( i < 0 )
		  bug( "Fread_char: Unknown Race.", 0 );
	      else
		  ch->race = i;
	  }

        else if ( !str_cmp( word, "PClan" ) )
	  {
	      char *clan_name;

	      ch->pcdata->rank      = fread_number( fp, &status );
	      clan_name             = fread_string( fp, &status1 );

	      if ( status || status1 )
	      {
		  bug( "Fread_char: Error reading PClan.", 0 );
		  fread_to_eol( fp );
		  continue;
	      }

	      if ( !clan_name
		  || !( ch->pcdata->clan = get_clan( clan_name ) ) )
	      {
		  bug( "Fread_char: Unknown PClan.", 0 );
		  ch->pcdata->rank      = 0;
	      }
	  }

	else if ( !str_cmp( word, "NAlias" ) )
	  {
	      ALIAS_DATA *alias;
	      int         num;

	      num = 0;

	      for ( alias = ch->pcdata->alias_list; alias; alias = alias->next )
		  num++;

	      if ( num >= MAX_ALIAS )
		  continue;

	      alias                  = alloc_mem( sizeof( ALIAS_DATA ) );

	      alias->cmd             = str_dup( fread_word( fp, &status1 ) );
	      alias->subst           = fread_string( fp, &status );

	      if ( status || status1 )
	      {
		  bug( "Fread_char: Error reading NAlias.", 0 );
		  fread_to_eol( fp );
		  continue;
	      }

	      alias->next            = ch->pcdata->alias_list;
	      ch->pcdata->alias_list = alias;
	  }

        else if ( !str_cmp( word, "Skll" ) )
	  {
              i  = fread_number( fp, &status );
	      sn = skill_lookup( fread_word( fp, &status1 ) );
	      
	      if ( status || status1 )
	      {
		  bug( "Fread_char: Error reading skill.", 0 );
		  fread_to_eol( fp );
		  continue;
	      }

	      if ( sn == -1 )
                  bug( "Fread_char: unknown skill.", 0 );
	      else
                  ch->pcdata->learned[sn] = i;
	  }

	else if ( !str_cmp ( word, "Afft" ) )
	  {

	      int   status;
	      char  buf1 [ MAX_STRING_LENGTH ];

	      paf                 = new_affect();

	      temp_fread_string( fp, buf1 );

	      paf->type           = affect_lookup( buf1 );

	      if ( paf->type < 0 )
	      {
		  paf->next	  = affect_free;
		  affect_free	  = paf;

		  sprintf( buf, "Fread_char: Error reading Afft %s.", buf1 );
		  bug( buf, 0 );

		  fread_to_eol( fp );
		  continue;
	      }

	      paf->duration       = fread_number( fp, &status );
	      paf->modifier       = fread_number( fp, &status );
	      paf->location       = fread_number( fp, &status );
	      fread_vector( fp, paf->bitvector );
	      paf->deleted        = FALSE;
	      paf->next           = ch->affected;
	      ch->affected        = paf;
	  }

	else if ( !str_cmp ( word, "Affect" ) )
	  {

	      int   status;
	      char  buf1 [ MAX_STRING_LENGTH ];

	      paf                 = new_affect();

	      temp_fread_string( fp, buf1 );

	      paf->type           = affect_lookup( buf1 );

	      if ( paf->type < 0 )
	      {
		  paf->next	  = affect_free;
		  affect_free	  = paf;

		  sprintf( buf, "Fread_char: Error reading Affect %s.", buf1 );
		  bug( buf, 0 );

		  fread_to_eol( fp );
		  continue;
	      }

	      paf->duration       = fread_number( fp, &status );
	      paf->modifier       = fread_number( fp, &status );
	      paf->location       = fread_flag( fp, apply_flags );

	      vcopy( paf->bitvector, fread_vect( fp, affect_flags, &status ) );

	      paf->deleted        = FALSE;
	      paf->next           = ch->affected;
	      ch->affected        = paf;
	  }

        else
	{
	    sprintf( buf, "Fread_char: Unknown key '%s' in pfile.", word );
	    bug( buf, 0 );
	    fread_to_eol( fp );
	}
	
    }

                /* Require all manditory fields, set defaults */

    for ( i = 0; *key_tab [i].key; i++ )
    {

        if ( key_tab [i].string == SPECIFIED ||
             key_tab [i].deflt == DEFLT )
            continue;

        if ( key_tab [i].deflt == MAND )
	{
            sprintf( buf, "Manditory field '%s' missing from pfile.",
                          key_tab [i].key );
            bug( buf, 0 );
            error_count++;
            continue;
	}

               /* This if/else sets default strings and numbers */

        if ( key_tab [i].string && key_tab [i].deflt )
	{
	    free_string( *(char **)key_tab [i].ptrs [0] );
            *(char **)key_tab [i].ptrs [0] =
	      str_dup( (char *)key_tab [i].deflt );
	}
        else
            for ( j = 0; key_tab [i].ptrs [j]; j++ )
	        *(int *)key_tab [i].ptrs [j] = key_tab [i].deflt;
    }

                /* Fixups */

    if ( ch->pcdata->title && isalnum ( *ch->pcdata->title ) )
    {
        sprintf( buf, " %s", ch->pcdata->title );
        free_string( ch->pcdata->title );
        ch->pcdata->title = str_dup( buf );
    }

    return error_count;

}

void recover( FILE *fp, long fpos )
{
    char        buf[ MAX_STRING_LENGTH ];

    fseek( fp, fpos, 0 );

    while ( !feof ( fp ) )
    {
        fpos = ftell( fp );

        if ( !fgets( buf, MAX_STRING_LENGTH, fp ) )
            return;

        if ( !strncmp( buf, "#OBJECT", 7 ) ||
	     !strncmp( buf, "#OBJ", 4 ) ||
             !strncmp( buf, "#END", 4 ) )
	{
            fseek( fp, fpos, 0 );
            return;
	}
    }
}

int envy_fread_obj( CHAR_DATA *ch, FILE *fp )
{
    EXTRA_DESCR_DATA *ed;
    OBJ_DATA         obj;
    OBJ_DATA         *new_obj;
    AFFECT_DATA      *paf;
    char              buf[ MAX_STRING_LENGTH ];
    char             *spell_name = NULL;
    char             *p          = NULL;
    char             *word;
    char             *tmp_ptr;
    bool              fNest;
    bool              fVnum;
    long              fpos;
    int               iNest;
    int               iValue;
    int               status;
    int               sn;
    int               vnum;
    int               num_keys;
    int               last_key   = 0;
    int               i, j, tmpi;

    char              corobj [] = "This object was corrupted.";

    struct key_data key_tab [] =
      {
	{ "Name",        TRUE,  MAND,             { &obj.name,        NULL } },
	{ "ShortDescr",  TRUE,  (unlong) &corobj, { &obj.short_descr, NULL } },
	{ "Description", TRUE,  (unlong) &corobj, { &obj.description, NULL } },
	{ "ExtraFlags",  FALSE, MAND,             { &obj.extra_flags, NULL } },
	{ "WearFlags",   FALSE, MAND,             { &obj.wear_flags,  NULL } },
	{ "WearLoc",     FALSE, MAND,             { &obj.wear_loc,    NULL } },
	{ "ItemType",    FALSE, MAND,             { &obj.item_type,   NULL } },
	{ "Weight",      FALSE, 10,               { &obj.weight,      NULL } },
	{ "Level",       FALSE, ch->level,        { &obj.level,       NULL } },
	{ "Timer",       FALSE, 0,                { &obj.timer,       NULL } },
	{ "Cost",        FALSE, 300,              { &obj.cost,        NULL } },
	{ "Values",      FALSE, MAND,             { &obj.value [0],
						    &obj.value [1],
						    &obj.value [2],
						    &obj.value [3],   NULL } },
	{ "\0",          FALSE, 0                                          } };

    memset( &obj, 0, sizeof( OBJ_DATA ) );

    obj.name        = str_dup( "" );
    obj.short_descr = str_dup( "" );
    obj.description = str_dup( "" );
    obj.deleted     = FALSE;

    fNest           = FALSE;
    fVnum           = TRUE;
    iNest           = 0;

    new_obj = new_object ();

    for ( num_keys = 0; *key_tab [num_keys].key; )
        num_keys++;

    for ( fpos = ftell( fp ) ; !feof( fp ) ; )
    {

        word = fread_word( fp, &status );

        for ( i = last_key;
              i < last_key + num_keys &&
                str_cmp( key_tab [i % num_keys].key, word ); )
            i++;

        i = i % num_keys;

        if ( !str_cmp( key_tab [i].key, word ) )
            last_key = i + 1;
        else
            i = num_keys;

        if ( *key_tab [i].key )         /* Key entry found in key_tab */
	{
            if ( key_tab [i].string == SPECIFIED )
                bug( "Key already specified.", 0 );

                        /* Entry is a string */

            else if ( key_tab [i].string )
	    {
                if ( ( p = fread_string( fp, &status ) ) && !status )
		{
                   free_string ( * (char **) key_tab [i].ptrs [0] );
                   * (char **) key_tab [i].ptrs [0] = p;
		}
	    }

                        /* Entry is an integer */
            else
                for ( j = 0; key_tab [i].ptrs [j]; j++ )
		{
                    tmpi = fread_number( fp, &status );
                    if ( !status )
                        * (int *) key_tab [i].ptrs [j] = tmpi;
		}

            if ( status )
	    {
                fread_to_eol( fp );
                continue;
	    }
	    else
                key_tab [i].string = SPECIFIED;
	}

        else if ( *word == '*' )
            fread_to_eol( fp );

        else if ( !str_cmp( word, "End" ) )
	{
            if ( !fNest || !fVnum )
	    {
                bug( "Fread_obj: incomplete object.", 0 );

		recover    ( fp, fpos        );
		free_string( obj.name        );
		free_string( obj.short_descr );
		free_string( obj.description );
		extract_obj( new_obj         );

		return FALSE;
	    }
            break;
	}

        else if ( !str_cmp( word, "Nest" ) )
	{

            iNest = fread_number( fp, &status );

            if ( status )       /* Losing track of nest level is bad */
                iNest = 0;      /* This makes objs go to inventory */

            else if ( iNest < 0 || iNest >= MAX_NEST )
                bug( "Fread_obj: bad nest %d.", iNest );

            else
	    {
                rgObjNest[iNest] = new_obj;
                fNest = TRUE;
	    }
	}

        else if ( !str_cmp( word, "Spell" ) )
	{

            iValue = fread_number( fp, &status );

            if ( !status )
                spell_name = fread_word( fp, &status );

            if ( status )       /* Recover is to skip spell */
	    {
                fread_to_eol( fp );
                continue;
	    }

            sn = skill_lookup( spell_name );

            if ( iValue < 0 || iValue > 3 )
                bug( "Fread_obj: bad iValue %d.", iValue );

            else if ( sn == -1 )
                bug( "Fread_obj: unknown skill.", 0 );

            else
                obj.value [iValue] = sn;
	}

        else if ( !str_cmp( word, "Vnum" ) )
	{

            vnum = fread_number( fp, &status );

            if ( status )               /* Can't live without vnum */
	    {
		recover    ( fp, fpos        );
		free_string( obj.name        );
		free_string( obj.short_descr );
		free_string( obj.description );
		extract_obj( new_obj         );
		return FALSE;
	    }

            if ( !( obj.pIndexData = get_obj_index( vnum ) ) )
                bug( "Fread_obj: bad vnum %d.", vnum );
            else
                fVnum = TRUE;
	}

                /* The following keys require extra processing */

        if ( !str_cmp( word, "Affect" ) )
	{
            paf = new_affect ();

	    paf->type       = fread_number( fp, &status );
	    paf->duration   = fread_number( fp, &status );
	    paf->modifier   = fread_number( fp, &status );
	    paf->location   = fread_number( fp, &status );
	    fread_vector( fp, paf->bitvector );

            paf->next = obj.affected;
            obj.affected = paf;
	}

        else if ( !str_cmp( word, "ExtraDescr" ) )
	{
	    tmp_ptr = fread_string( fp, &status );

            if ( !status )
                p = fread_string( fp, &status );

            if ( status )
	    {
		recover    ( fp, fpos        );
		free_string( obj.name        );
		free_string( obj.short_descr );
		free_string( obj.description );
		extract_obj( new_obj         );
		return FALSE;
	    }

            ed = new_extra_descr ();

            ed->keyword     = tmp_ptr;
            ed->description = p;
            ed->next        = obj.extra_descr;
            obj.extra_descr = ed;
	}
    }
                /* Require all manditory fields, set defaults */

    for ( i = 0; *key_tab [i].key; i++ )
    {

        if ( key_tab [i].string == SPECIFIED ||
             key_tab [i].deflt == DEFLT )
            continue;

        if ( key_tab [i].deflt == MAND )
	{
            sprintf( buf, "Manditory obj field '%s' missing from pfile.",
		    key_tab [i].key );
            bug( buf, 0 );

	    recover    ( fp, fpos        );
	    free_string( obj.name        );
	    free_string( obj.short_descr );
	    free_string( obj.description );
	    extract_obj( new_obj         );

	    return FALSE;
	}

                /* This if/else sets default strings and numbers */

        if ( key_tab [i].string && key_tab [i].deflt )
            * (char **) key_tab [i].ptrs [0] =
                        str_dup ( (char *) key_tab [i].deflt );
        else
            for ( j = 0; key_tab [i].ptrs [j]; j++ )
                * (int *) key_tab [i].ptrs [j] = key_tab [i].deflt;
    }

    memcpy( new_obj, &obj, sizeof( OBJ_DATA ) );

    new_obj->next = object_list;
    object_list   = new_obj;

    new_obj->pIndexData->count++;
    if ( iNest == 0 || !rgObjNest[iNest] )
        obj_to_char( new_obj, ch );
    else
        obj_to_obj( new_obj, rgObjNest[iNest-1] );

    return TRUE;
}

int fread_obj( CHAR_DATA *ch, FILE *fp )
{
    EXTRA_DESCR_DATA *ed;
    OBJ_DATA         obj;
    OBJ_DATA         *new_obj;
    AFFECT_DATA      *paf;
    char              buf[ MAX_STRING_LENGTH ];
    char             *spell_name = NULL;
    char             *p          = NULL;
    char             *word;
    char             *tmp_ptr;
    char             *special;
    bool              fNest;
    bool              fVnum;
    long              fpos;
    int               iNest;
    int               iValue;
    int               status;
    int               sn;
    int               vnum;
    int               num_keys;
    int               last_key   = 0;
    int               i, j, tmpi;

    char              corobj [] = "This object was corrupted.";

    struct key_data key_tab [] =
      {
	{ "Name",        TRUE,  MAND,             { &obj.name,        NULL } },
	{ "ShortDescr",  TRUE,  (unlong) &corobj, { &obj.short_descr, NULL } },
	{ "Description", TRUE,  (unlong) &corobj, { &obj.description, NULL } },
	{ "ExtraFlags",  FALSE, MAND,             { &obj.extra_flags, NULL } },
	{ "WearFlags",   FALSE, MAND,             { &obj.wear_flags,  NULL } },
	{ "WearLoc",     FALSE, MAND,             { &obj.wear_loc,    NULL } },
	{ "ItemType",    FALSE, MAND,             { &obj.item_type,   NULL } },
	{ "Weight",      FALSE, 10,               { &obj.weight,      NULL } },
	{ "Level",       FALSE, ch->level,        { &obj.level,       NULL } },
	{ "Timer",       FALSE, 0,                { &obj.timer,       NULL } },
	{ "Cost",        FALSE, 300,              { &obj.cost,        NULL } },
	{ "Values",      FALSE, MAND,             { &obj.value [0],
						    &obj.value [1],
						    &obj.value [2],
						    &obj.value [3],
						    &obj.value [4],   NULL } },
	{ "\0",          FALSE, 0                                          } };

    memset( &obj, 0, sizeof( OBJ_DATA ) );

    obj.name        = str_dup( "" );
    obj.short_descr = str_dup( "" );
    obj.description = str_dup( "" );
    obj.deleted     = FALSE;

    fNest           = FALSE;
    fVnum           = TRUE;
    iNest           = 0;

    new_obj = new_object ();

    for ( num_keys = 0; *key_tab [num_keys].key; )
        num_keys++;

    for ( fpos = ftell( fp ) ; !feof( fp ) ; )
    {

        word = fread_word( fp, &status );

        for ( i = last_key;
              i < last_key + num_keys &&
                str_cmp( key_tab [i % num_keys].key, word ); )
            i++;

        i = i % num_keys;

        if ( !str_cmp( key_tab [i].key, word ) )
            last_key = i + 1;
        else
            i = num_keys;

        if ( *key_tab [i].key )         /* Key entry found in key_tab */
	{
            if ( key_tab [i].string == SPECIFIED )
                bug( "Key already specified.", 0 );

                        /* Entry is a string */

            else if ( key_tab [i].string )
	    {
                if ( ( p = fread_string( fp, &status ) ) && !status )
		{
                   free_string ( * (char **) key_tab [i].ptrs [0] );
                   * (char **) key_tab [i].ptrs [0] = p;
		}
	    }

                        /* Entry is an integer */
            else
                for ( j = 0; key_tab [i].ptrs [j]; j++ )
		{
                    tmpi = fread_number( fp, &status );
                    if ( !status )
                        * (int *) key_tab [i].ptrs [j] = tmpi;
		}

            if ( status )
	    {
                fread_to_eol( fp );
                continue;
	    }
	    else
                key_tab [i].string = SPECIFIED;
	}

        else if ( *word == '*' )
            fread_to_eol( fp );

        else if ( !str_cmp( word, "End" ) )
	{
            if ( !fNest || !fVnum )
	    {
                bug( "Fread_obj: incomplete object.", 0 );

		recover    ( fp, fpos        );
		free_string( obj.name        );
		free_string( obj.short_descr );
		free_string( obj.description );
		extract_obj( new_obj         );

		return FALSE;
	    }
            break;
	}

        else if ( !str_cmp( word, "Nest" ) )
	{

            iNest = fread_number( fp, &status );

            if ( status )       /* Losing track of nest level is bad */
                iNest = 0;      /* This makes objs go to inventory */

            else if ( iNest < 0 || iNest >= MAX_NEST )
                bug( "Fread_obj: bad nest %d.", iNest );

            else
	    {
                rgObjNest[iNest] = new_obj;
                fNest = TRUE;
	    }
	}

        else if ( !str_cmp( word, "Special" ) )
	{
	    special = fread_word( fp, &status );

	    if ( !status )
		obj.spec_fun = spec_obj_lookup( special );

            if ( status )
	    {
                fread_to_eol( fp );
                continue;
	    }

	}

        else if ( !str_cmp( word, "Spell" ) )
	{

            iValue = fread_number( fp, &status );

            if ( !status )
                spell_name = fread_word( fp, &status );

            if ( status )       /* Recover is to skip spell */
	    {
                fread_to_eol( fp );
                continue;
	    }

            sn = skill_lookup( spell_name );

            if ( iValue < 0 || iValue > 4 )
                bug( "Fread_obj: bad iValue %d.", iValue );

            else if ( sn == -1 )
                bug( "Fread_obj: unknown skill.", 0 );

            else
                obj.value [iValue] = sn;
	}

        else if ( !str_cmp( word, "Vnum" ) )
	{

            vnum = fread_number( fp, &status );

            if ( status )               /* Can't live without vnum */
	    {
		recover    ( fp, fpos        );
		free_string( obj.name        );
		free_string( obj.short_descr );
		free_string( obj.description );
		extract_obj( new_obj         );
		return FALSE;
	    }

            if ( !( obj.pIndexData = get_obj_index( vnum ) ) )
                bug( "Fread_obj: bad vnum %d.", vnum );
            else
                fVnum = TRUE;
	}

                /* The following keys require extra processing */

        if ( !str_cmp( word, "Affect" ) )
	{
            paf = new_affect ();

	    paf->type       = fread_number( fp, &status );
	    paf->duration   = fread_number( fp, &status );
	    paf->modifier   = fread_number( fp, &status );
	    paf->location   = fread_number( fp, &status );

	    vcopy( paf->bitvector, fread_vect( fp, affect_flags, &status ) );

            paf->next = obj.affected;
            obj.affected = paf;
	}

        else if ( !str_cmp( word, "ExtraDescr" ) )
	{
	    tmp_ptr = fread_string( fp, &status );

            if ( !status )
                p = fread_string( fp, &status );

            if ( status )
	    {
		recover    ( fp, fpos        );
		free_string( obj.name        );
		free_string( obj.short_descr );
		free_string( obj.description );
		extract_obj( new_obj         );
		return FALSE;
	    }

            ed = new_extra_descr ();

            ed->keyword     = tmp_ptr;
            ed->description = p;
            ed->next        = obj.extra_descr;
            obj.extra_descr = ed;
	}
    }
                /* Require all manditory fields, set defaults */

    for ( i = 0; *key_tab [i].key; i++ )
    {

        if ( key_tab [i].string == SPECIFIED ||
             key_tab [i].deflt == DEFLT )
            continue;

        if ( key_tab [i].deflt == MAND )
	{
            sprintf( buf, "Manditory obj field '%s' missing from pfile.",
		    key_tab [i].key );
            bug( buf, 0 );

	    recover    ( fp, fpos        );
	    free_string( obj.name        );
	    free_string( obj.short_descr );
	    free_string( obj.description );
	    extract_obj( new_obj         );

	    return FALSE;
	}

                /* This if/else sets default strings and numbers */

        if ( key_tab [i].string && key_tab [i].deflt )
            * (char **) key_tab [i].ptrs [0] =
                        str_dup ( (char *) key_tab [i].deflt );
        else
            for ( j = 0; key_tab [i].ptrs [j]; j++ )
                * (int *) key_tab [i].ptrs [j] = key_tab [i].deflt;
    }

    memcpy( new_obj, &obj, sizeof( OBJ_DATA ) );

    new_obj->next = object_list;
    object_list   = new_obj;

    new_obj->pIndexData->count++;
    if ( iNest == 0 || !rgObjNest[iNest] )
        obj_to_char( new_obj, ch );
    else
        obj_to_obj( new_obj, rgObjNest[iNest-1] );

    return TRUE;
}