1stMUD/corefiles/
1stMUD/gods/
1stMUD/player/
1stMUD/win32/
1stMUD/win32/ROM/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael         *
*  Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                         *
*  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          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.org)                                    *
*       By using this code, you have agreed to follow the terms of the    *
*       ROM license, in the file Rom24/doc/rom.license                    *
***************************************************************************
*       1stMUD ROM Derivative (c) 2001-2002 by Ryan Jennings              *
*            http://1stmud.dlmud.com/  <r-jenn@shaw.ca>                   *
***************************************************************************/
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"
#include "recycle.h"
#include "tables.h"
#include "lookup.h"

extern int _filbuf args ((FILE *));

/* int rename(const char *oldfname, const char *newfname); viene en stdio.h */

/*
 * 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 fwrite_pet args ((CHAR_DATA * pet, FILE * fp));
void fread_char args ((CHAR_DATA * ch, FILE * fp));
void fread_pet args ((CHAR_DATA * ch, FILE * fp));
void fread_obj args ((CHAR_DATA * ch, FILE * fp));

/*
 * 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];
	FILE *fp;

	if (IS_NPC (ch))
		return;

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

	/* create god log */
	if (IS_IMMORTAL (ch) || ch->level >= LEVEL_IMMORTAL)
	{
		sprintf (strsave, "%s%s", GOD_DIR, capitalize (ch->name));
		if ((fp = file_open (strsave, "w")) == NULL)
		{
			bug ("Save_char_obj: file_open", 0);
			perror (strsave);
		}

		fprintf (fp, "Lev %2d Trust %2d  %s%s\n", ch->level,
				 get_trust (ch), ch->name, ch->pcdata->title);
		file_close (fp);
	}

	sprintf (strsave, "%s%s", PLAYER_DIR, capitalize (ch->name));
#if !defined(WIN32)
	if ((fp = file_open (TEMP_FILE, "w")) == NULL)
#else
	if ((fp = file_open (strsave, "w")) == NULL)
#endif
	{
		bug ("Save_char_obj: file_open", 0);
		perror (strsave);
	}
	else
	{
		fwrite_char (ch, fp);
		if (ch->carrying != NULL)
			fwrite_obj (ch, ch->carrying, fp, 0);
		/* save the pets */
		if (ch->pet != NULL && ch->pet->in_room == ch->in_room)
			fwrite_pet (ch->pet, fp);
		fprintf (fp, "#END\n");
	}
	file_close (fp);
#if !defined(WIN32)
	rename (TEMP_FILE, strsave);
#endif
	return;
}

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

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

	fprintf (fp, "Name %s~\n", ch->name);
	fprintf (fp, "Id   %ld\n", ch->id);
	fprintf (fp, "LogO %ld\n", current_time);
	fprintf (fp, "Vers %d\n", 7);
	if (ch->short_descr[0] != '\0')
		fprintf (fp, "ShD  %s~\n", ch->short_descr);
	if (ch->long_descr[0] != '\0')
		fprintf (fp, "LnD  %s~\n", ch->long_descr);
	if (ch->description[0] != '\0')
		fprintf (fp, "Desc %s~\n", ch->description);
	if (ch->prompt != NULL || !str_cmp (ch->prompt, "<%hhp %mm %vmv> "))
		fprintf (fp, "Prom %s~\n", ch->prompt);
	fprintf (fp, "Race %s~\n", race_table[ch->race].name);
	if (ch->clan > -1)
		fprintf (fp, "Clan %s~\n", clan_table[ch->clan].name);
	fprintf (fp, "Sex  %d\n", ch->sex);
	fprintf (fp, "Cla  %s\n", class_numbers (ch, TRUE));
	if (ch->pcdata->stay_race)
		fprintf (fp, "StayRace\n");
	fprintf (fp, "Levl %d\n", ch->level);
	if (ch->trust != 0)
		fprintf (fp, "Tru  %d\n", ch->trust);
	fprintf (fp, "Sec  %d\n", ch->pcdata->security);	/* OLC */
	fprintf (fp, "Plyd %d\n", ch->played + (int) (current_time - ch->logon));
	fprintf (fp, "Scro %d\n", ch->lines);
	fprintf (fp, "Room %ld\n",
			 (ch->in_room == get_room_index (ROOM_VNUM_LIMBO) &&
			  ch->was_in_room !=
			  NULL) ? ch->was_in_room->vnum : ch->in_room ==
			 NULL ? 3001 : ch->in_room->vnum);

	fprintf (fp, "HMV  %ld %ld %ld %ld %ld %ld\n", ch->hit, ch->max_hit,
			 ch->mana, ch->max_mana, ch->move, ch->max_move);
	if (ch->gold > 0)
		fprintf (fp, "Gold %ld\n", ch->gold);
	else
		fprintf (fp, "Gold %d\n", 0);
	if (ch->silver > 0)
		fprintf (fp, "Silv %ld\n", ch->silver);
	else
		fprintf (fp, "Silv %d\n", 0);
	fprintf (fp, "Exp  %d\n", ch->exp);
	if (ch->act != 0)
	{
		if (!IS_NPC (ch) && gquest_info.running == GQUEST_OFF
			&& IS_SET (ch->act, PLR_GQUEST))
		{
			REMOVE_BIT (ch->act, PLR_GQUEST);
			reset_gqmob (ch, 0);
		}
		if (!IS_NPC (ch))
		{
			if (war_info.iswar == WAR_OFF && IS_SET (ch->act, PLR_WAR))
				REMOVE_BIT (ch->act, PLR_WAR);
		}
		fprintf (fp, "Act  %s\n", fwrite_flags (ch->act));
	}
	if (ch->affected_by != 0)
		fprintf (fp, "AfBy %s\n", fwrite_flags (ch->affected_by));
	fprintf (fp, "Comm %s\n", fwrite_flags (ch->comm));
	if (ch->wiznet)
		fprintf (fp, "Wizn %s\n", fwrite_flags (ch->wiznet));
	if (ch->invis_level)
		fprintf (fp, "Invi %d\n", ch->invis_level);
	if (ch->incog_level)
		fprintf (fp, "Inco %d\n", ch->incog_level);
	fprintf (fp, "Pos  %d\n",
			 ch->position == POS_FIGHTING ? POS_STANDING : ch->position);
	if (ch->practice != 0)
		fprintf (fp, "Prac %d\n", ch->practice);
	if (ch->train != 0)
		fprintf (fp, "Trai %d\n", ch->train);
	if (ch->saving_throw != 0)
		fprintf (fp, "Save  %d\n", ch->saving_throw);
	fprintf (fp, "Alig  %d\n", ch->alignment);
	if (ch->hitroll != 0)
		fprintf (fp, "Hit   %d\n", ch->hitroll);
	if (ch->damroll != 0)
		fprintf (fp, "Dam   %d\n", ch->damroll);
	fprintf (fp, "ACs %d %d %d %d\n", ch->armor[0], ch->armor[1],
			 ch->armor[2], ch->armor[3]);
	if (ch->wimpy != 0)
		fprintf (fp, "Wimp  %d\n", ch->wimpy);
	fprintf (fp, "Attr %d %d %d %d %d\n", ch->perm_stat[STAT_STR],
			 ch->perm_stat[STAT_INT], ch->perm_stat[STAT_WIS],
			 ch->perm_stat[STAT_DEX], ch->perm_stat[STAT_CON]);

	fprintf (fp, "AMod %d %d %d %d %d\n", ch->mod_stat[STAT_STR],
			 ch->mod_stat[STAT_INT], ch->mod_stat[STAT_WIS],
			 ch->mod_stat[STAT_DEX], ch->mod_stat[STAT_CON]);

	if (IS_NPC (ch))
	{
		fprintf (fp, "Vnum %ld\n", ch->pIndexData->vnum);
	}
	else
	{
		fprintf (fp, "Pass %s~\n", ch->pcdata->pwd);
		if (ch->pcdata->bamfin[0] != '\0')
			fprintf (fp, "Bin  %s~\n", ch->pcdata->bamfin);
		if (ch->pcdata->bamfout[0] != '\0')
			fprintf (fp, "Bout %s~\n", ch->pcdata->bamfout);
		fprintf (fp, "Titl %s~\n", ch->pcdata->title);
		fprintf (fp, "Pnts %d\n", ch->pcdata->points);
		fprintf (fp, "TSex %d\n", ch->pcdata->true_sex);
		fprintf (fp, "LLev %d\n", ch->pcdata->last_level);
		fprintf (fp, "HMVP %ld %ld %ld\n", ch->pcdata->perm_hit,
				 ch->pcdata->perm_mana, ch->pcdata->perm_move);
		fprintf (fp, "Cnd  %d %d %d %d\n", ch->pcdata->condition[0],
				 ch->pcdata->condition[1], ch->pcdata->condition[2],
				 ch->pcdata->condition[3]);

		if (ch->pcdata->questpoints != 0)
			fprintf (fp, "QuestPnts %d\n", ch->pcdata->questpoints);
		if (ch->pcdata->nextquest != 0)
			fprintf (fp, "QuestNext %d\n", ch->pcdata->nextquest);
		else if (ch->pcdata->countdown != 0)
			fprintf (fp, "QuestCount %d\n", ch->pcdata->countdown);
		if (ch->pcdata->questgiver != 0)
			fprintf (fp, "QuestGiver %ld\n", ch->pcdata->questgiver);
		if (ch->pcdata->questloc != 0)
			fprintf (fp, "QuestLoc   %ld\n", ch->pcdata->questloc);
		if (ch->pcdata->questobj != 0)
			fprintf (fp, "QuestObj %ld\n", ch->pcdata->questobj);
		else if (ch->pcdata->questmob != 0)
			fprintf (fp, "QuestMob %ld\n", ch->pcdata->questmob);
		if (ch->pcdata->trivia != 0)
			fprintf (fp, "Trivia  %d\n", ch->pcdata->trivia);

		if (ON_GQUEST (ch) || (gquest_info.running != GQUEST_OFF
							   && count_gqmobs (ch) == gquest_info.mob_count))
		{
			fprintf (fp, "GQmobs %d ", gquest_info.mob_count);
			for (i = 0; i < gquest_info.mob_count; i++)
				fprintf (fp, "%ld ", ch->pcdata->gq_mobs[i]);
			fprintf (fp, "\n");
		}

		if (count_home (ch) > 0)
		{
			fprintf (fp, "Homes  %d ", MAX_HOUSE_ROOMS);
			for (i = 0; i < MAX_HOUSE_ROOMS; i++)
				fprintf (fp, "%ld ", ch->pcdata->home[i]);
			fprintf (fp, "\n");
			fprintf (fp, "HKey  %ld\n", ch->pcdata->home_key);
		}

		fprintf (fp, "Colo  %d ", MAX_CUSTOM_COLOUR);
		for (i = 0; i < MAX_CUSTOM_COLOUR; i++)
			fprintf (fp, "%d ", ch->pcdata->colour[i]);
		fprintf (fp, "\n");

		if (ch->pcdata->who_descr[0] != '\0')
			fprintf (fp, "WhoD  %s~\n", ch->pcdata->who_descr);

		fprintf (fp, "GStats  %d ", MAX_GAMESTAT);
		for (i = 0; i < MAX_GAMESTAT; i++)
			fprintf (fp, "%ld ", ch->pcdata->gamestat[i]);
		fprintf (fp, "\n");

		/* write alias */
		for (pos = 0; pos < MAX_ALIAS; pos++)
		{
			if (ch->pcdata->alias[pos] == NULL ||
				ch->pcdata->alias_sub[pos] == NULL)
				break;

			fprintf (fp, "Alias %s %s~\n", ch->pcdata->alias[pos],
					 ch->pcdata->alias_sub[pos]);
		}

		/* Save note board status */
		/* Save number of boards in case that number changes */
		fprintf (fp, "Boards       %d ", MAX_BOARD);
		for (i = 0; i < MAX_BOARD; i++)
			fprintf (fp, "%s %ld ", boards[i].short_name,
					 ch->pcdata->last_note[i]);
		fprintf (fp, "\n");

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

		for (gn = 0; gn < maxGroup; gn++)
		{
			if (group_table[gn].name != NULL && ch->pcdata->group_known[gn])
			{
				fprintf (fp, "Gr '%s'\n", group_table[gn].name);
			}
		}
	}

	for (paf = ch->affected; paf != NULL; paf = paf->next)
	{
		if (paf->type < 0 || paf->type >= maxSkill)
			continue;

		fprintf (fp, "Affc '%s' %3d %3d %3d %3d %3d %s\n",
				 skill_table[paf->type].name, paf->where, paf->level,
				 paf->duration, paf->modifier, paf->location,
				 fwrite_flags (paf->bitvector));
	}
	fwrite_rle (ch->pcdata->explored, fp);
	fprintf (fp, "End\n\n");
	return;
}

/* write a pet */
void fwrite_pet (CHAR_DATA * pet, FILE * fp)
{
	AFFECT_DATA *paf;

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

	fprintf (fp, "Vnum %ld\n", pet->pIndexData->vnum);

	fprintf (fp, "Name %s~\n", pet->name);
	fprintf (fp, "LogO %ld\n", current_time);
	if (pet->short_descr != pet->pIndexData->short_descr)
		fprintf (fp, "ShD  %s~\n", pet->short_descr);
	if (pet->long_descr != pet->pIndexData->long_descr)
		fprintf (fp, "LnD  %s~\n", pet->long_descr);
	if (pet->description != pet->pIndexData->description)
		fprintf (fp, "Desc %s~\n", pet->description);
	if (pet->race != pet->pIndexData->race)
		fprintf (fp, "Race %s~\n", race_table[pet->race].name);
	if (pet->clan > -1)
		fprintf (fp, "Clan %s~\n", clan_table[pet->clan].name);
	fprintf (fp, "Sex  %d\n", pet->sex);
	if (pet->level != pet->pIndexData->level)
		fprintf (fp, "Levl %d\n", pet->level);
	fprintf (fp, "HMV  %ld %ld %ld %ld %ld %ld\n", pet->hit, pet->max_hit,
			 pet->mana, pet->max_mana, pet->move, pet->max_move);
	if (pet->gold > 0)
		fprintf (fp, "Gold %ld\n", pet->gold);
	if (pet->silver > 0)
		fprintf (fp, "Silv %ld\n", pet->silver);
	if (pet->exp > 0)
		fprintf (fp, "Exp  %d\n", pet->exp);
	if (pet->act != pet->pIndexData->act)
		fprintf (fp, "Act  %s\n", fwrite_flags (pet->act));
	if (pet->affected_by != pet->pIndexData->affected_by)
		fprintf (fp, "AfBy %s\n", fwrite_flags (pet->affected_by));
	if (pet->comm != 0)
		fprintf (fp, "Comm %s\n", fwrite_flags (pet->comm));
	fprintf (fp, "Pos  %d\n", pet->position =
			 POS_FIGHTING ? POS_STANDING : pet->position);
	if (pet->saving_throw != 0)
		fprintf (fp, "Save %d\n", pet->saving_throw);
	if (pet->alignment != pet->pIndexData->alignment)
		fprintf (fp, "Alig %d\n", pet->alignment);
	if (pet->hitroll != pet->pIndexData->hitroll)
		fprintf (fp, "Hit  %d\n", pet->hitroll);
	if (pet->damroll != pet->pIndexData->damage[DICE_BONUS])
		fprintf (fp, "Dam  %d\n", pet->damroll);
	fprintf (fp, "ACs  %d %d %d %d\n", pet->armor[0], pet->armor[1],
			 pet->armor[2], pet->armor[3]);
	fprintf (fp, "Attr %d %d %d %d %d\n", pet->perm_stat[STAT_STR],
			 pet->perm_stat[STAT_INT], pet->perm_stat[STAT_WIS],
			 pet->perm_stat[STAT_DEX], pet->perm_stat[STAT_CON]);
	fprintf (fp, "AMod %d %d %d %d %d\n", pet->mod_stat[STAT_STR],
			 pet->mod_stat[STAT_INT], pet->mod_stat[STAT_WIS],
			 pet->mod_stat[STAT_DEX], pet->mod_stat[STAT_CON]);

	for (paf = pet->affected; paf != NULL; paf = paf->next)
	{
		if (paf->type < 0 || paf->type >= maxSkill)
			continue;

		fprintf (fp, "Affc '%s' %3d %3d %3d %3d %3d %s\n",
				 skill_table[paf->type].name, paf->where, paf->level,
				 paf->duration, paf->modifier, paf->location,
				 fwrite_flags (paf->bitvector));
	}

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

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

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

	if (!ch)
	{
		if (obj->in_obj != NULL)
			where = 1;
		if (obj->in_room != NULL)
			where = obj->in_room->vnum;
	}

	/*
	 * Castrate storage characters.
	 */
	if (ch
		&& ((ch->level < obj->level - 2 && obj->item_type != ITEM_CONTAINER)
			|| obj->item_type == ITEM_KEY || (obj->item_type == ITEM_MAP
											  && !obj->value[0])))
		return;

	fprintf (fp, "#%c\n", !ch ? 'C' : 'O');
	fprintf (fp, "Vnum %ld\n", obj->pIndexData->vnum);
	if (!ch)
		fprintf (fp, "Where      %ld\n", where);
	if (obj->owner != NULL)
		fprintf (fp, "Owner      %s\n", obj->owner);
	if (!obj->pIndexData->new_format)
		fprintf (fp, "Oldstyle\n");
	if (obj->enchanted)
		fprintf (fp, "Enchanted\n");
	fprintf (fp, "Nest %d\n", iNest);

	/* these data are only used if they do not match the defaults */

	if (!ch || obj->name != obj->pIndexData->name)
		fprintf (fp, "Name %s~\n", obj->name);
	if (!ch || obj->short_descr != obj->pIndexData->short_descr)
		fprintf (fp, "ShD  %s~\n", obj->short_descr);
	if (!ch || obj->description != obj->pIndexData->description)
		fprintf (fp, "Desc %s~\n", obj->description);
	if (!ch || obj->extra_flags != obj->pIndexData->extra_flags)
		fprintf (fp, "ExtF %s\n", fwrite_flags (obj->extra_flags));
	if (!ch || obj->wear_flags != obj->pIndexData->wear_flags)
		fprintf (fp, "WeaF %s\n", fwrite_flags (obj->wear_flags));
	if (obj->item_type != obj->pIndexData->item_type)
		fprintf (fp, "Ityp %d\n", obj->item_type);
	if (!ch || obj->weight != obj->pIndexData->weight)
		fprintf (fp, "Wt   %d\n", obj->weight);
	if (!ch || obj->condition != obj->pIndexData->condition)
		fprintf (fp, "Cond %d\n", obj->condition);

	/* variable data */

	fprintf (fp, "Wear %d\n", obj->wear_loc);
	if (!ch || obj->level != obj->pIndexData->level)
		fprintf (fp, "Lev  %d\n", obj->level);
	if (!ch || obj->timer != 0)
		fprintf (fp, "Time %d\n", obj->timer);
	fprintf (fp, "Cost %d\n", obj->cost);
	if (!ch || (obj->value[0] != obj->pIndexData->value[0] ||
				obj->value[1] != obj->pIndexData->value[1] ||
				obj->value[2] != obj->pIndexData->value[2] ||
				obj->value[3] != obj->pIndexData->value[3] ||
				obj->value[4] != obj->pIndexData->value[4]))
		fprintf (fp, "Val  %ld %ld %ld %ld %ld\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:
	case ITEM_PILL:
		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);
		}

		break;

	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 != NULL; paf = paf->next)
	{
		if (paf->type < 0 || paf->type >= maxSkill)
			continue;
		fprintf (fp, "Affc '%s' %3d %3d %3d %3d %3d %s\n",
				 skill_table[paf->type].name, paf->where, paf->level,
				 paf->duration, paf->modifier, paf->location,
				 fwrite_flags (paf->bitvector));
	}

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

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

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

	return;
}

/*
 * Load a char and inventory into a new ch structure.
 */
bool load_char_obj (DESCRIPTOR_DATA * d, const char *name)
{
	char strsave[MAX_INPUT_LENGTH];
	char buf[100];
	CHAR_DATA *ch;
	FILE *fp;
	bool found;
	int stat, i, iClass;

	ch = new_char ();
	ch->pcdata = new_pcdata ();

	d->character = ch;
	ch->desc = d;
	ch->name = str_dup (name);
	ch->id = get_pc_id ();
	ch->race = race_lookup ("human");
	ch->act = PLR_NOSUMMON | PLR_AUTOMAP;
	ch->comm = COMM_COMBINE | COMM_PROMPT;
	for (iClass = 0; iClass < MAX_MCLASS; iClass++)
		ch->Class[iClass] = -1;
	ch->prompt = str_dup ("<%hhp %mm %vmv> ");
	ch->pcdata->confirm_delete = FALSE;
	ch->pcdata->board = &boards[DEFAULT_BOARD];
	ch->info_settings = INFO_QUIET;	/* this actually turns quiet off */
	ch->pcdata->pwd = str_dup ("");
	ch->pcdata->bamfin = str_dup ("");
	ch->pcdata->bamfout = str_dup ("");
	ch->pcdata->title = str_dup ("");
	ch->pcdata->who_descr = str_dup ("");
	for (stat = 0; stat < MAX_STATS; stat++)
		ch->perm_stat[stat] = 13;
	ch->pcdata->condition[COND_THIRST] = 48;
	ch->pcdata->condition[COND_FULL] = 48;
	ch->pcdata->condition[COND_HUNGER] = 48;
	ch->pcdata->security = 0;	/* OLC */
	default_colour (ch, -1);
	for (i = 0; i < MAX_GAMESTAT; i++)
		ch->pcdata->gamestat[i] = 0;
	ch->pcdata->trivia = 0;
	end_quest (ch, 0);
	reset_gqmob (ch, 0);

	found = FALSE;

	/* decompress if .gz file exists */
	sprintf (strsave, "%s%s%s", PLAYER_DIR, capitalize (name), ".gz");
	if ((fp = file_open (strsave, "r")) != NULL)
	{
		file_close (fp);
		sprintf (buf, "gzip -dfq %s", strsave);
		system (buf);
	}

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

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

		found = TRUE;
		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, "OBJECT"))
				fread_obj (ch, fp);
			else if (!str_cmp (word, "O"))
				fread_obj (ch, fp);
			else if (!str_cmp (word, "PET"))
				fread_pet (ch, fp);
			else if (!str_cmp (word, "END"))
				break;
			else
			{
				bug ("Load_char_obj: bad section.", 0);
				break;
			}
		}
	}
	file_close (fp);

	/* initialize race */
	if (found)
	{
		if (ch->race == 0)
			ch->race = race_lookup ("human");

		ch->size = race_table[ch->race].size;
		ch->dam_type = 17;		/*punch */

		for (i = 0; i < 5; i++)
		{
			if (race_table[ch->race].skills[i] == NULL)
				break;
			group_add (ch, race_table[ch->race].skills[i], FALSE);
		}
		ch->affected_by = ch->affected_by | race_table[ch->race].aff;
		ch->imm_flags = ch->imm_flags | race_table[ch->race].imm;
		ch->res_flags = ch->res_flags | race_table[ch->race].res;
		ch->vuln_flags = ch->vuln_flags | race_table[ch->race].vuln;
		ch->form = race_table[ch->race].form;
		ch->parts = race_table[ch->race].parts;

		if (d != NULL)
		{
			if (IS_SET (ch->comm, COMM_NOCOLOUR))
				REMOVE_BIT (d->desc_flags, DESC_COLOUR);
		}

	}

	/* RT initialize skills */

	if (found && ch->version < 2)	/* need to add the new skills */
	{
		group_add (ch, "rom basics", FALSE);
		add_base_groups (ch);
		add_default_groups (ch);
		ch->pcdata->learned[gsn_recall] = 50;
	}

	/* fix levels */
	if (found && ch->version < 6 && (ch->level > 51 || ch->trust > 51))
	{
		ch->level += 149;
		ch->trust += 149;
	}

	/* ream gold */
	if (found && ch->version < 4)
	{
		ch->gold /= 100;
	}
	return found;
}

/*
 * 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;				\
				}

/* provided to free strings */
#if defined(KEYS)
#undef KEYS
#endif

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

void fread_char (CHAR_DATA * ch, FILE * fp)
{
	char buf[MAX_STRING_LENGTH];
	const char *word;
	bool fMatch;
	int count = 0;
	int lastlogoff = current_time;
	int percent;

	sprintf (buf, "Loading %s.", ch->name);
	log_string (buf);

	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_flag (fp));
			KEY ("AffectedBy", ch->affected_by, fread_flag (fp));
			KEY ("AfBy", ch->affected_by, fread_flag (fp));
			KEY ("Alignment", ch->alignment, fread_number (fp));
			KEY ("Alig", ch->alignment, fread_number (fp));

			if (!str_cmp (word, "Alia"))
			{
				if (count >= MAX_ALIAS)
				{
					fread_to_eol (fp);
					fMatch = TRUE;
					break;
				}

				ch->pcdata->alias[count] = str_dup (fread_word (fp));
				ch->pcdata->alias_sub[count] = str_dup (fread_word (fp));
				count++;
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "Alias"))
			{
				if (count >= MAX_ALIAS)
				{
					fread_to_eol (fp);
					fMatch = TRUE;
					break;
				}

				ch->pcdata->alias[count] = str_dup (fread_word (fp));
				ch->pcdata->alias_sub[count] = fread_string (fp);
				count++;
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "AC") || !str_cmp (word, "Armor"))
			{
				fread_to_eol (fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "ACs"))
			{
				int i;

				for (i = 0; i < 4; i++)
					ch->armor[i] = fread_number (fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "AffD"))
			{
				AFFECT_DATA *paf;
				int sn;

				paf = new_affect ();

				sn = skill_lookup (fread_word (fp));
				if (sn < 0)
					bug ("Fread_char: unknown skill.", 0);
				else
					paf->type = sn;

				paf->level = fread_number (fp);
				paf->duration = fread_number (fp);
				paf->modifier = fread_number (fp);
				paf->location = fread_number (fp);
				paf->bitvector = fread_number (fp);
				paf->next = ch->affected;
				ch->affected = paf;
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "Affc"))
			{
				AFFECT_DATA *paf;
				int sn;

				paf = new_affect ();

				sn = skill_lookup (fread_word (fp));
				if (sn < 0)
					bug ("Fread_char: unknown skill.", 0);
				else
					paf->type = sn;

				paf->where = fread_number (fp);
				paf->level = fread_number (fp);
				paf->duration = fread_number (fp);
				paf->modifier = fread_number (fp);
				paf->location = fread_number (fp);
				paf->bitvector = fread_flag (fp);
				paf->next = ch->affected;
				ch->affected = paf;
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "AttrMod") || !str_cmp (word, "AMod"))
			{
				int stat;
				for (stat = 0; stat < MAX_STATS; stat++)
					ch->mod_stat[stat] = fread_number (fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "AttrPerm") || !str_cmp (word, "Attr"))
			{
				int stat;

				for (stat = 0; stat < MAX_STATS; stat++)
					ch->perm_stat[stat] = fread_number (fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'B':
			KEYS ("Bamfin", ch->pcdata->bamfin, fread_string (fp));
			KEYS ("Bamfout", ch->pcdata->bamfout, fread_string (fp));
			KEYS ("Bin", ch->pcdata->bamfin, fread_string (fp));
			KEYS ("Bout", ch->pcdata->bamfout, fread_string (fp));
			/* Read in board status */
			if (!str_cmp (word, "Boards"))
			{
				int i, num = fread_number (fp);	/* number of boards saved */
				char *boardname;

				for (; num; num--)	/* for each of the board saved */
				{
					boardname = fread_word (fp);
					i = board_lookup (boardname);	/* find board number */

					if (i == BOARD_NOTFOUND)	/* Does board still exist ? */
					{
						sprintf (buf,
								 "fread_char: %s had unknown board name: %s. Skipped.",
								 ch->name, boardname);
						log_string (buf);
						fread_number (fp);	/* read last_note and skip info */
					}
					else		/* Save it */
						ch->pcdata->last_note[i] = fread_number (fp);
				}				/* for */

				fMatch = TRUE;
			}					/* Boards */
			break;

		case 'C':
			if (!str_cmp (word, "Cla") || !str_cmp (word, "Class"))
			{
				int iClass = 0;
				bool invalid = FALSE;

				fMatch = TRUE;
				for (iClass = 0; iClass < MAX_MCLASS; iClass++)
					ch->Class[iClass] = -1;
				for (iClass = 0; iClass < MAX_MCLASS; iClass++)
				{
					ch->Class[iClass] = fread_number (fp);
					if (ch->Class[iClass] == -1)
						break;
				}
				for (iClass = 0; iClass < MAX_MCLASS; iClass++)
				{
					if (invalid)
					{
						ch->Class[iClass - 1] = ch->Class[iClass];
						ch->Class[iClass] = -1;
						continue;
					}
					if (ch->Class[iClass] < 0
						|| ch->Class[iClass] >= maxClass)
					{
						ch->Class[iClass] = -1;
						invalid = TRUE;
					}
				}
			}
			if (!str_cmp (word, "Clan"))
			{
				const char *tmp = fread_string (fp);
				ch->clan = clan_lookup (tmp);
				free_string (tmp);
				fMatch = TRUE;
				break;
			}
			if (!str_cmp (word, "Condition") || !str_cmp (word, "Cond"))
			{
				ch->pcdata->condition[0] = fread_number (fp);
				ch->pcdata->condition[1] = fread_number (fp);
				ch->pcdata->condition[2] = fread_number (fp);
				fMatch = TRUE;
				break;
			}
			if (!str_cmp (word, "Cnd"))
			{
				ch->pcdata->condition[0] = fread_number (fp);
				ch->pcdata->condition[1] = fread_number (fp);
				ch->pcdata->condition[2] = fread_number (fp);
				ch->pcdata->condition[3] = fread_number (fp);
				fMatch = TRUE;
				break;
			}
			KEY ("Comm", ch->comm, fread_flag (fp));
			if (!str_cmp (word, "Colo"))
			{
				int i, num = fread_number (fp);

				for (i = 0; i < num; i++)
				{
					ch->pcdata->colour[i] = fread_number (fp);
					if (i >= MAX_CUSTOM_COLOUR)
						break;
				}
				fread_to_eol (fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'D':
			KEY ("Damroll", ch->damroll, fread_number (fp));
			KEY ("Dam", ch->damroll, fread_number (fp));
			KEYS ("Description", ch->description, fread_string (fp));
			KEYS ("Desc", ch->description, fread_string (fp));
			break;

		case 'E':
			if (!str_cmp (word, "End"))
			{
				/* adjust hp mana move up  -- here for speed's sake */
				percent = (current_time - lastlogoff) * 25 / (2 * 60 * 60);

				percent = UMIN (percent, 100);

				if (percent > 0 && !IS_AFFECTED (ch, AFF_POISON)
					&& !IS_AFFECTED (ch, AFF_PLAGUE))
				{
					ch->hit += (ch->max_hit - ch->hit) * percent / 100;
					ch->mana += (ch->max_mana - ch->mana) * percent / 100;
					ch->move += (ch->max_move - ch->move) * percent / 100;
				}
				return;
			}
			KEY ("Exp", ch->exp, fread_number (fp));
			break;

		case 'G':
			KEY ("Gold", ch->gold, fread_number (fp));
			if (!str_cmp (word, "GQmobs"))
			{
				int maxMob = fread_number (fp);
				int i;

				for (i = 0; i < maxMob; i++)
					ch->pcdata->gq_mobs[i] = fread_number (fp);
				fMatch = TRUE;
			}
			if (!str_cmp (word, "Group") || !str_cmp (word, "Gr"))
			{
				int gn;
				char *temp;

				temp = fread_word (fp);
				gn = group_lookup (temp);
				/* gn    = group_lookup( fread_word( fp ) ); */
				if (gn < 0)
				{
					fprintf (stderr, "%s", temp);
					bug ("Fread_char: unknown group. ", 0);
				}
				else
					gn_add (ch, gn);
				fMatch = TRUE;
			}
			if (!str_cmp (word, "GStats"))
			{
				int i, maxStat = 0;

				maxStat = fread_number (fp);

				for (i = 0; i < maxStat; i++)
					ch->pcdata->gamestat[i] = fread_number (fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'H':
			KEY ("Hitroll", ch->hitroll, fread_number (fp));
			KEY ("Hit", ch->hitroll, fread_number (fp));
			KEY ("HKey", ch->pcdata->home_key, fread_number (fp));
			if (!str_cmp (word, "Homes"))
			{
				int i, maxHome = 0;

				maxHome = fread_number (fp);
				for (i = 0; i < maxHome; i++)
					ch->pcdata->home[i] = fread_number (fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "HpManaMove") || !str_cmp (word, "HMV"))
			{
				ch->hit = fread_number (fp);
				ch->max_hit = fread_number (fp);
				ch->mana = fread_number (fp);
				ch->max_mana = fread_number (fp);
				ch->move = fread_number (fp);
				ch->max_move = fread_number (fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "HpManaMovePerm") || !str_cmp (word, "HMVP"))
			{
				ch->pcdata->perm_hit = fread_number (fp);
				ch->pcdata->perm_mana = fread_number (fp);
				ch->pcdata->perm_move = fread_number (fp);
				fMatch = TRUE;
				break;
			}

			break;

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

		case 'L':
			KEY ("LastLevel", ch->pcdata->last_level, fread_number (fp));
			KEY ("LLev", ch->pcdata->last_level, fread_number (fp));
			KEY ("Level", ch->level, fread_number (fp));
			KEY ("Lev", ch->level, fread_number (fp));
			KEY ("Levl", ch->level, fread_number (fp));
			KEY ("LogO", lastlogoff, fread_number (fp));
			KEYS ("LongDescr", ch->long_descr, fread_string (fp));
			KEYS ("LnD", ch->long_descr, fread_string (fp));
			break;

		case 'N':
			KEYS ("Name", ch->name, fread_string (fp));
			if (!str_cmp (word, "Not"))
			{
				fread_number (fp);
				fread_number (fp);
				fread_number (fp);
				fread_number (fp);
				fread_number (fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'P':
			KEYS ("Password", ch->pcdata->pwd, fread_string (fp));
			KEYS ("Pass", ch->pcdata->pwd, fread_string (fp));
			KEY ("Played", ch->played, fread_number (fp));
			KEY ("Plyd", ch->played, fread_number (fp));
			KEY ("Points", ch->pcdata->points, fread_number (fp));
			KEY ("Pnts", ch->pcdata->points, fread_number (fp));
			KEY ("Position", ch->position, fread_number (fp));
			KEY ("Pos", ch->position, fread_number (fp));
			KEY ("Practice", ch->practice, fread_number (fp));
			KEY ("Prac", ch->practice, fread_number (fp));
			KEYS ("Prompt", ch->prompt, fread_string (fp));
			KEYS ("Prom", ch->prompt, fread_string (fp));
			break;

		case 'Q':
			KEY ("QuestPnts", ch->pcdata->questpoints, fread_number (fp));
			KEY ("QuestNext", ch->pcdata->nextquest, fread_number (fp));
			KEY ("QuestCount", ch->pcdata->countdown, fread_number (fp));
			KEY ("QuestLoc", ch->pcdata->questloc, fread_number (fp));
			KEY ("QuestObj", ch->pcdata->questobj, fread_number (fp));
			KEY ("QuestGiver", ch->pcdata->questgiver, fread_number (fp));
			KEY ("QuestMob", ch->pcdata->questmob, fread_number (fp));
			break;

		case 'R':
			if (!str_cmp (word, "Race"))
			{
				const char *tmp = fread_string (fp);

				ch->race = race_lookup (tmp);
				free_string (tmp);
				fMatch = TRUE;
				break;
			}
			if (!str_cmp (word, "RoomRLE"))
			{
				fread_rle (ch->pcdata->explored, fp);
				fMatch = TRUE;
				break;
			}
			if (!str_cmp (word, "Room"))
			{
				ch->in_room = get_room_index (fread_number (fp));
				if (ch->in_room == NULL)
					ch->in_room = get_room_index (ROOM_VNUM_LIMBO);
				else if (IS_SET (ch->in_room->room_flags, ROOM_ARENA))
					ch->in_room = get_room_index (ROOM_VNUM_TEMPLE);
				fMatch = TRUE;
				break;
			}

			break;

		case 'S':
			KEY ("SavingThrow", ch->saving_throw, fread_number (fp));
			KEY ("Save", ch->saving_throw, fread_number (fp));
			KEY ("Scro", ch->lines, fread_number (fp));
			KEY ("Sex", ch->sex, fread_number (fp));
			KEYS ("ShortDescr", ch->short_descr, fread_string (fp));
			KEYS ("ShD", ch->short_descr, fread_string (fp));
			KEY ("Sec", ch->pcdata->security, fread_number (fp));	/* OLC */
			KEY ("Silv", ch->silver, fread_number (fp));
			if (!str_cmp (word, "StayRace"))
			{
				ch->pcdata->stay_race = TRUE;
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "Skill") || !str_cmp (word, "Sk"))
			{
				int sn;
				int value;
				char *temp;

				value = fread_number (fp);
				temp = fread_word (fp);
				sn = skill_lookup (temp);
				/* sn    = skill_lookup( fread_word( fp ) ); */
				if (sn < 0)
				{
					fprintf (stderr, "%s", temp);
					bug ("Fread_char: unknown skill. ", 0);
				}
				else
					ch->pcdata->learned[sn] = value;
				fMatch = TRUE;
			}

			break;

		case 'T':
			KEY ("TrueSex", ch->pcdata->true_sex, fread_number (fp));
			KEY ("TSex", ch->pcdata->true_sex, fread_number (fp));
			KEY ("Trai", ch->train, fread_number (fp));
			KEY ("Trust", ch->trust, fread_number (fp));
			KEY ("Tru", ch->trust, fread_number (fp));
			KEY ("Trivia", ch->pcdata->trivia, fread_number (fp));
			if (!str_cmp (word, "Title") || !str_cmp (word, "Titl"))
			{
				ch->pcdata->title = fread_string (fp);
				if (ch->pcdata->title[0] != '.' &&
					ch->pcdata->title[0] != ',' &&
					ch->pcdata->title[0] != '!' &&
					ch->pcdata->title[0] != '?')
				{
					sprintf (buf, " %s", ch->pcdata->title);
					free_string (ch->pcdata->title);
					ch->pcdata->title = str_dup (buf);
				}
				fMatch = TRUE;
				break;
			}

			break;

		case 'V':
			KEY ("Version", ch->version, fread_number (fp));
			KEY ("Vers", ch->version, fread_number (fp));
			if (!str_cmp (word, "Vnum"))
			{
				ch->pIndexData = get_mob_index (fread_number (fp));
				fMatch = TRUE;
				break;
			}
			break;

		case 'W':
			KEY ("Wimpy", ch->wimpy, fread_number (fp));
			KEY ("Wimp", ch->wimpy, fread_number (fp));
			KEY ("Wizn", ch->wiznet, fread_flag (fp));
			KEYS ("WhoD", ch->pcdata->who_descr, fread_string (fp));
			break;
		}

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

/* load a pet from the forgotten reaches */
void fread_pet (CHAR_DATA * ch, FILE * fp)
{
	const char *word;
	CHAR_DATA *pet;
	bool fMatch;
	int lastlogoff = current_time;
	int percent;

	/* first entry had BETTER be the vnum or we barf */
	word = feof (fp) ? "END" : fread_word (fp);
	if (!str_cmp (word, "Vnum"))
	{
		vnum_t vnum;

		vnum = fread_number (fp);
		if (get_mob_index (vnum) == NULL)
		{
			bug ("Fread_pet: bad vnum %d.", vnum);
			pet = create_mobile (get_mob_index (MOB_VNUM_FIDO));
		}
		else
			pet = create_mobile (get_mob_index (vnum));
	}
	else
	{
		bug ("Fread_pet: no vnum in file.", 0);
		pet = create_mobile (get_mob_index (MOB_VNUM_FIDO));
	}

	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", pet->act, fread_flag (fp));
			KEY ("AfBy", pet->affected_by, fread_flag (fp));
			KEY ("Alig", pet->alignment, fread_number (fp));

			if (!str_cmp (word, "ACs"))
			{
				int i;

				for (i = 0; i < 4; i++)
					pet->armor[i] = fread_number (fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "AffD"))
			{
				AFFECT_DATA *paf;
				int sn;

				paf = new_affect ();

				sn = skill_lookup (fread_word (fp));
				if (sn < 0)
					bug ("Fread_char: unknown skill.", 0);
				else
					paf->type = sn;

				paf->level = fread_number (fp);
				paf->duration = fread_number (fp);
				paf->modifier = fread_number (fp);
				paf->location = fread_number (fp);
				paf->bitvector = fread_number (fp);
				paf->next = pet->affected;
				pet->affected = paf;
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "Affc"))
			{
				AFFECT_DATA *paf;
				int sn;

				paf = new_affect ();

				sn = skill_lookup (fread_word (fp));
				if (sn < 0)
					bug ("Fread_char: unknown skill.", 0);
				else
					paf->type = sn;

				paf->where = fread_number (fp);
				paf->level = fread_number (fp);
				paf->duration = fread_number (fp);
				paf->modifier = fread_number (fp);
				paf->location = fread_number (fp);
				paf->bitvector = fread_flag (fp);
				paf->next = pet->affected;
				pet->affected = paf;
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "AMod"))
			{
				int stat;

				for (stat = 0; stat < MAX_STATS; stat++)
					pet->mod_stat[stat] = fread_number (fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "Attr"))
			{
				int stat;

				for (stat = 0; stat < MAX_STATS; stat++)
					pet->perm_stat[stat] = fread_number (fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'C':
			if (!str_cmp (word, "Clan"))
			{
				const char *tmp = fread_string (fp);
				pet->clan = clan_lookup (tmp);
				free_string (tmp);
				fMatch = TRUE;
				break;
			}
			KEY ("Comm", pet->comm, fread_flag (fp));
			break;

		case 'D':
			KEY ("Dam", pet->damroll, fread_number (fp));
			KEYS ("Desc", pet->description, fread_string (fp));
			break;

		case 'E':
			if (!str_cmp (word, "End"))
			{
				pet->leader = ch;
				pet->master = ch;
				ch->pet = pet;
				/* adjust hp mana move up  -- here for speed's sake */
				percent = (current_time - lastlogoff) * 25 / (2 * 60 * 60);

				if (percent > 0 && !IS_AFFECTED (ch, AFF_POISON)
					&& !IS_AFFECTED (ch, AFF_PLAGUE))
				{
					percent = UMIN (percent, 100);
					pet->hit += (pet->max_hit - pet->hit) * percent / 100;
					pet->mana += (pet->max_mana - pet->mana) * percent / 100;
					pet->move += (pet->max_move - pet->move) * percent / 100;
				}
				return;
			}
			KEY ("Exp", pet->exp, fread_number (fp));
			break;

		case 'G':
			KEY ("Gold", pet->gold, fread_number (fp));
			break;

		case 'H':
			KEY ("Hit", pet->hitroll, fread_number (fp));

			if (!str_cmp (word, "HMV"))
			{
				pet->hit = fread_number (fp);
				pet->max_hit = fread_number (fp);
				pet->mana = fread_number (fp);
				pet->max_mana = fread_number (fp);
				pet->move = fread_number (fp);
				pet->max_move = fread_number (fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'L':
			KEY ("Levl", pet->level, fread_number (fp));
			KEYS ("LnD", pet->long_descr, fread_string (fp));
			KEY ("LogO", lastlogoff, fread_number (fp));
			break;

		case 'N':
			KEYS ("Name", pet->name, fread_string (fp));
			break;

		case 'P':
			KEY ("Pos", pet->position, fread_number (fp));
			break;

		case 'R':
			if (!str_cmp (word, "Race"))
			{
				const char *tmp = fread_string (fp);
				pet->race = race_lookup (tmp);
				free_string (tmp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'S':
			KEY ("Save", pet->saving_throw, fread_number (fp));
			KEY ("Sex", pet->sex, fread_number (fp));
			KEYS ("ShD", pet->short_descr, fread_string (fp));
			KEY ("Silv", pet->silver, fread_number (fp));
			break;

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

		}
	}
}

extern OBJ_DATA *obj_free;

void fread_obj (CHAR_DATA * ch, FILE * fp)
{
	OBJ_DATA *obj;
	const char *word;
	int iNest;
	bool fMatch;
	bool fNest;
	bool fVnum;
	bool first;
	bool new_format;			/* to prevent errors */
	bool make_new;				/* update object */
	vnum_t where;

	fVnum = FALSE;
	obj = NULL;
	first = TRUE;				/* used to counter fp offset */
	new_format = FALSE;
	make_new = FALSE;

	word = feof (fp) ? "End" : fread_word (fp);
	if (!str_cmp (word, "Vnum"))
	{
		vnum_t vnum;
		first = FALSE;			/* fp will be in right place */

		vnum = fread_number (fp);
		if (get_obj_index (vnum) == NULL)
		{
			bug ("Fread_obj: bad vnum %d.", vnum);
		}
		else
		{
			obj = create_object (get_obj_index (vnum), -1);
			new_format = TRUE;
		}

	}

	if (obj == NULL)			/* either not found or old style */
	{
		obj = new_obj ();
		obj->name = str_dup ("");
		obj->short_descr = str_dup ("");
		obj->description = str_dup ("");
	}

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

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

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

		case 'A':
			if (!str_cmp (word, "AffD"))
			{
				AFFECT_DATA *paf;
				int sn;

				paf = new_affect ();

				sn = skill_lookup (fread_word (fp));
				if (sn < 0)
					bug ("Fread_obj: unknown skill.", 0);
				else
					paf->type = sn;

				paf->level = fread_number (fp);
				paf->duration = fread_number (fp);
				paf->modifier = fread_number (fp);
				paf->location = fread_number (fp);
				paf->bitvector = fread_number (fp);
				paf->next = obj->affected;
				obj->affected = paf;
				fMatch = TRUE;
				break;
			}
			if (!str_cmp (word, "Affc"))
			{
				AFFECT_DATA *paf;
				int sn;

				paf = new_affect ();

				sn = skill_lookup (fread_word (fp));
				if (sn < 0)
					bug ("Fread_obj: unknown skill.", 0);
				else
					paf->type = sn;

				paf->where = fread_number (fp);
				paf->level = fread_number (fp);
				paf->duration = fread_number (fp);
				paf->modifier = fread_number (fp);
				paf->location = fread_number (fp);
				paf->bitvector = fread_flag (fp);
				paf->next = obj->affected;
				obj->affected = paf;
				fMatch = TRUE;
				break;
			}
			break;

		case 'C':
			KEY ("Cond", obj->condition, fread_number (fp));
			KEY ("Cost", obj->cost, fread_number (fp));
			break;

		case 'D':
			KEYS ("Description", obj->description, fread_string (fp));
			KEYS ("Desc", obj->description, fread_string (fp));
			break;

		case 'E':

			if (!str_cmp (word, "Enchanted"))
			{
				obj->enchanted = TRUE;
				fMatch = TRUE;
				break;
			}

			KEY ("ExtraFlags", obj->extra_flags, fread_number (fp));
			KEY ("ExtF", obj->extra_flags, fread_flag (fp));

			if (!str_cmp (word, "ExtraDescr") || !str_cmp (word, "ExDe"))
			{
				EXTRA_DESCR_DATA *ed;

				ed = new_extra_descr ();

				ed->keyword = fread_string (fp);
				ed->description = fread_string (fp);
				ed->next = obj->extra_descr;
				obj->extra_descr = ed;
				fMatch = TRUE;
			}

			if (!str_cmp (word, "End"))
			{
				if (!fNest || (fVnum && obj->pIndexData == NULL))
				{
					bug ("Fread_obj: incomplete object.", 0);
					free_obj (obj);
					return;
				}
				else
				{
					if (!fVnum)
					{
						free_obj (obj);
						obj =
							create_object (get_obj_index (OBJ_VNUM_DUMMY), 0);
					}

					if (!new_format)
					{
						obj->next = object_list;
						object_list = obj;
						obj->pIndexData->count++;
					}

					if (!obj->pIndexData->new_format &&
						obj->item_type == ITEM_ARMOR && obj->value[1] == 0)
					{
						obj->value[1] = obj->value[0];
						obj->value[2] = obj->value[0];
					}
					if (make_new)
					{
						int wear;

						wear = obj->wear_loc;
						extract_obj (obj);

						obj = create_object (obj->pIndexData, 0);
						obj->wear_loc = wear;
					}
					if (iNest == 0 || rgObjNest[iNest] == NULL)
					{
						if (!ch)
						{
							ROOM_INDEX_DATA *Room;
							CORPSE_DATA *c;

							c = new_corpse ();
							c->corpse = obj;
							c->next = corpse_list;
							corpse_list = c;
							if ((Room = get_room_index (where)) == NULL)
								Room = get_room_index (ROOM_VNUM_MORGUE);
							obj_to_room (obj, Room);
						}
						else
							obj_to_char (obj, ch);
					}
					else
						obj_to_obj (obj, rgObjNest[iNest - 1]);
					return;
				}
			}
			break;

		case 'I':
			KEY ("ItemType", obj->item_type, fread_number (fp));
			KEY ("Ityp", obj->item_type, fread_number (fp));
			break;

		case 'L':
			KEY ("Level", obj->level, fread_number (fp));
			KEY ("Lev", obj->level, fread_number (fp));
			break;

		case 'N':
			KEYS ("Name", obj->name, fread_string (fp));

			if (!str_cmp (word, "Nest"))
			{
				iNest = fread_number (fp);
				if (iNest < 0 || iNest >= MAX_NEST)
				{
					bug ("Fread_obj: bad nest %d.", iNest);
				}
				else
				{
					rgObjNest[iNest] = obj;
					fNest = TRUE;
				}
				fMatch = TRUE;
			}
			break;

		case 'O':
			if (!str_cmp (word, "Oldstyle"))
			{
				if (obj->pIndexData != NULL && obj->pIndexData->new_format)
					make_new = TRUE;
				fMatch = TRUE;
			}
			KEYS ("Owner", obj->owner, fread_string (fp));
			break;

		case 'S':
			KEYS ("ShortDescr", obj->short_descr, fread_string (fp));
			KEYS ("ShD", obj->short_descr, fread_string (fp));

			if (!str_cmp (word, "Spell"))
			{
				int iValue;
				int sn;

				iValue = fread_number (fp);
				sn = skill_lookup (fread_word (fp));
				if (iValue < 0 || iValue > 3)
				{
					bug ("Fread_obj: bad iValue %d.", iValue);
				}
				else if (sn < 0)
				{
					bug ("Fread_obj: unknown skill.", 0);
				}
				else
				{
					obj->value[iValue] = sn;
				}
				fMatch = TRUE;
				break;
			}

			break;

		case 'T':
			KEY ("Timer", obj->timer, fread_number (fp));
			KEY ("Time", obj->timer, fread_number (fp));
			break;

		case 'V':
			if (!str_cmp (word, "Values") || !str_cmp (word, "Vals"))
			{
				obj->value[0] = fread_number (fp);
				obj->value[1] = fread_number (fp);
				obj->value[2] = fread_number (fp);
				obj->value[3] = fread_number (fp);
				if (obj->item_type == ITEM_WEAPON && obj->value[0] == 0)
					obj->value[0] = obj->pIndexData->value[0];
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "Val"))
			{
				obj->value[0] = fread_number (fp);
				obj->value[1] = fread_number (fp);
				obj->value[2] = fread_number (fp);
				obj->value[3] = fread_number (fp);
				obj->value[4] = fread_number (fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp (word, "Vnum"))
			{
				vnum_t vnum;

				vnum = fread_number (fp);
				if ((obj->pIndexData = get_obj_index (vnum)) == NULL)
					bug ("Fread_obj: bad vnum %d.", vnum);
				else
					fVnum = TRUE;
				fMatch = TRUE;
				break;
			}
			break;

		case 'W':
			KEY ("WearFlags", obj->wear_flags, fread_number (fp));
			KEY ("WeaF", obj->wear_flags, fread_flag (fp));
			KEY ("WearLoc", obj->wear_loc, fread_number (fp));
			KEY ("Wear", obj->wear_loc, fread_number (fp));
			KEY ("Weight", obj->weight, fread_number (fp));
			KEY ("Wt", obj->weight, fread_number (fp));
			KEY ("Where", where, fread_number (fp));
			break;

		}

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

CORPSE_DATA *corpse_list;

void save_corpses (void)
{
	FILE *fp;
	CORPSE_DATA *c;

	if ((fp = file_open (CORPSE_FILE, "w")) == NULL)
	{
		bug ("save_corpses: " CORPSE_FILE " not found.", 0);
	}
	else
	{
		for (c = corpse_list; c != NULL; c = c->next)
		{
			if (c->corpse->item_type == ITEM_CORPSE_PC)
				fwrite_obj (NULL, c->corpse, fp, 0);
			else
				update_corpses (c->corpse, TRUE);
		}
		fprintf (fp, "#END\n");
		fflush (fp);
	}
	file_close (fp);

	return;
}

void load_corpses (void)
{
	FILE *fp;

	if ((fp = file_open (CORPSE_FILE, "r")) == NULL)
	{
		bug ("load_corpses: " CORPSE_FILE " not found", 0);
	}
	else
	{
		for (;;)
		{
			char letter;
			char *word;

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

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

			word = fread_word (fp);
			if ((!str_cmp (word, "CORPSE")) || (!str_cmp (word, "C")))
				fread_obj (NULL, fp);
			else if (!str_cmp (word, "END"))
				break;
			else
			{
				bug ("load_corpses: bad section.", 0);
				break;
			}
		}
	}

	file_close (fp);

	return;
}

void update_corpses (OBJ_DATA * obj, bool pdelete)
{
	if (obj && obj->item_type == ITEM_CORPSE_PC)
	{
		CORPSE_DATA *c;

		for (c = corpse_list; c != NULL; c = c->next)
			if (c->corpse == obj)
				break;
		if (c != NULL)
		{
			if (c == corpse_list)
			{
				corpse_list = c->next;
			}
			else
			{
				CORPSE_DATA *aux;

				for (aux = corpse_list; aux != NULL; aux = aux->next)
				{
					if (aux->next == c)
					{
						aux->next = c->next;
						break;
					}

					if (aux == NULL)
					{
						bug ("update_corpses: corpse not found.", 0);
						return;
					}
				}
			}
			if (pdelete)
				free_corpse (c);
			save_corpses ();
		}
		else if (obj->contains != NULL && obj->in_room != NULL)
		{
			c = new_corpse ();
			c->corpse = obj;
			c->next = corpse_list;
			corpse_list = c;
			save_corpses ();
		}
	}
	return;
}

void checkcorpse (CHAR_DATA * ch)
{
	CORPSE_DATA *c;
	bool found = FALSE;
	int count = 0;

	if (!ch || IS_NPC (ch))
		return;

	for (c = corpse_list; c != NULL; c = c->next)
	{
		if (c->corpse && is_name (ch->name, c->corpse->owner))
		{
			found = TRUE;
			count++;
		}
	}
	if (found)
	{
		chprintlnf
			(ch, "\n\r{f{RWARNING:{x {WYou have %d corpse%s in the game.{x",
			 count, count > 1 ? "s" : "");
		sprintf (log_buf, "%s has %d corpse%s in the game.", ch->name, count,
				 count > 1 ? "s" : "");
		log_string (log_buf);
		wiznet (log_buf, NULL, NULL, 0, 0, 0);
	}
}