daleken/
daleken/data/notes/
daleken/data/player/
daleken/data/system/poses/
daleken/doc/Homepage/images/
daleken/log/
/*___________________________________________________________________________*
   )()(			  DalekenMUD 1.12 (C) 2000			)()(
   `]['		       by Martin Thomson, Lee Brooks,			`]['
    ||		       Ken Herbert and David Jacques			 ||
    || ----------------------------------------------------------------- ||
    || Envy Diku Mud improvements copyright (C) 1994 by Michael Quan,	 ||
    || David Love, Guilherme 'Willie' Arnold, and Mitchell Tse.		 ||
    || Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael	 ||
    || Chastain, Michael Quan, and Mitchell Tse.			 ||
    || Original Diku Mud copyright (C) 1990, 1991			 ||
    || by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt,	 ||
    || Tom Madsen, and Katja Nyboe.					 ||
    || ----------------------------------------------------------------- ||
    || Any use of this software must follow the licenses of the		 ||
    || creators.  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.					 ||
    || ----------------------------------------------------------------- ||
    ||				    db_io.c				 ||
    || This file contains generic data loading and saving functions.	 ||
 *_/<>\_________________________________________________________________/<>\_*/


#include "mud.h"
#include "olc.h"
#include "event.h"
#include "db.h"

/**
 * Note: All functions that return bool in the reading/writing of data will
 * return FALSE upon an error.  This also means that the error could not be
 * handled properly.  This can mean irreversible problems as the reading of
 * the file will have to be halted.
 *
 * Look for places where functions return TRUE when they handle an error.
 */

static struct
{
    struct sysinfo_type sys;

    PLANE_DATA plane;
    AREA_DATA area;
    ROOM_INDEX_DATA room;
    MOB_INDEX_DATA mob_index;
    OBJ_INDEX_DATA obj_index;
    MPROG_GLOBAL gprog;

    CHAR_DATA ch;
    PC_DATA pcdata;
    OBJ_DATA obj;
    AFFECT_DATA aff;
    EVENT event;
    QUEST_DATA quest;
    ALIAS_DATA alias;

    EXTRA_DESCR_DATA exdesc;
    MPROG_DATA mprog;
    SHOP_DATA shop;
    EXIT_DATA exit;

    HELP_DATA help;
    SOCIAL_DATA soc;
    POSE_DATA pose;
    RELIGION_DATA rel;
    CLAN_DATA clan;
    RELIGION_SKILL relskill;

    NOTE_DATA note;
    HIGHEST_DATA high;
    HIGHEST_ENTRY highent;
} sdb;

int file_version;
time_t file_date;

/*
 * The controlling table for all reading and writing through this file.
 */
struct top_data_type global_data_table[] = {
    {
	"Affect",	affect_data_table,
	&sdb.aff,	sizeof( AFFECT_DATA ),	dbrwf_affect
    },

    {
	"Alias",	alias_data_table,
	&sdb.alias,	sizeof( ALIAS_DATA ),	dbrwf_alias
    },

    {
	"Area",		area_data_table,
	&sdb.area,	sizeof( AREA_DATA ),	dbrwf_area
    },

    {
	"Char",		char_data_table,
	&sdb.ch,	sizeof( CHAR_DATA ),	dbrwf_char
    },

    {
	"Clan",		clan_data_table,
	&sdb.clan,	sizeof( CLAN_DATA ),	dbrwf_clan
    },

    {
	"Event",	event_data_table,
	&sdb.event,	sizeof( EVENT ),	dbrwf_event
    },

    {
	"ExDesc",	exdesc_data_table,
	&sdb.exdesc,	sizeof( EXTRA_DESCR_DATA ), dbrwf_exdesc
    },

    {
	"Exit",		exit_data_table,
	&sdb.exit,	sizeof( EXIT_DATA ),	dbrwf_exit
    },

    {
	"GProg",	gprog_data_table,
	&sdb.gprog,	sizeof( MPROG_GLOBAL ),	dbrwf_gprog
    },

    {
	"Help",		help_data_table,
	&sdb.help,	sizeof( HELP_DATA ),	dbrwf_help
    },

    {
	"High",		high_data_table,
	&sdb.high,	sizeof( HIGHEST_DATA ),	dbrwf_high
    },

    {
	"HighEnt",	highent_data_table,
	&sdb.highent,	sizeof( struct highest_entry ),	dbrwf_highent
    },

    {
	"Mobile",	mobile_data_table,
	&sdb.mob_index,	sizeof( MOB_INDEX_DATA ), dbrwf_mob_index
    },

    {
	"MudProg",	mudprog_data_table,
	&sdb.mprog,	sizeof( MPROG_DATA ),	dbrwf_mudprog
    },

    {
	"Note",		note_data_table,
	&sdb.note,	sizeof( NOTE_DATA ),	dbrwf_note
    },

    {
	"Obj",		obj_data_table,
	&sdb.obj,	sizeof( OBJ_DATA ), dbrwf_obj
    },

    {
	"Object",	object_data_table,
	&sdb.obj_index,	sizeof( OBJ_INDEX_DATA ), dbrwf_obj_index
    },

    {
	"PcData",	pcdata_data_table,
	&sdb.pcdata,	sizeof( PC_DATA ),	dbrwf_pcdata
    },

    {
	"Plane",	plane_data_table,
	&sdb.plane,	sizeof( PLANE_DATA ),	dbrwf_plane
    },

    {
	"Pose",		pose_data_table,
	&sdb.pose,	sizeof( POSE_DATA ),	dbrwf_pose
    },

    {
	"Religion",	religion_data_table,
	&sdb.rel,	sizeof( RELIGION_DATA ), dbrwf_religion
    },

    {
	"Quest",	quest_data_table,
	&sdb.quest,	sizeof( QUEST_DATA ), dbrwf_quest
    },

    {
	"RelSkill",	relskill_data_table,
	&sdb.relskill,	sizeof( RELIGION_SKILL ), dbrwf_relskill
    },

    {
	"Room",		room_data_table,
	&sdb.room,	sizeof( ROOM_INDEX_DATA ), dbrwf_room
    },

    {
	"Shop",		shop_data_table,
	&sdb.shop,	sizeof( SHOP_DATA ),	dbrwf_shop
    },

    {
	"Social",	social_data_table,
	&sdb.soc,	sizeof( SOCIAL_DATA ),	dbrwf_social
    },

    {
	"SysInfo",	sysinfo_data_table,
	&sdb.sys,	sizeof( struct sysinfo_type ),	dbrwf_sysinfo
    },

    {
	NULL, NULL, NULL, 0, NULL
    }
};


/****************************************************************************
 * Load/Save tables for all types.
 *
 * Note objects of type DFLAG_ARRAY, MUST HAVE A RWF FUNCTION.
 */
/* Plane */
struct data_desc_type plane_data_table[] = {
    {
	"Name",		DTYPE_STRING | DFLAG_MAND,	&sdb.plane.name,
	{ 0 },		NULL,				NULL
    },
    {
	"Minimum",	DTYPE_NUMBER,			&sdb.plane.min_level,
	{ 1000 },	NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Area */
struct data_desc_type area_data_table[] = {
    {
	"Name",		DTYPE_STRING | DFLAG_MAND,	&sdb.area.name,
	{ 0 },		NULL,				NULL
    },
    {
	"Plane",	DTYPE_COMPLEX | DFLAG_SPECIAL,	&sdb.area.plane,
	{ 0 },		NULL,				dbrwf_area_plane
    },
    {
	"Builders",	DTYPE_STRING,			&sdb.area.builders,
	{ 0 },		NULL,				NULL
    },
    {
	"Repop",	DTYPE_STRING,			&sdb.area.repop,
	{ 0 },		NULL,				NULL
    },
    {
	"Age",		DTYPE_NUMBER,			&sdb.area.age,
	{ 15 },		NULL,				NULL
    },
    {
	"LVnum",	DTYPE_NUMBER,			&sdb.area.lvnum,
	{ 0 },		NULL,				NULL
    },
    {
	"UVnum",	DTYPE_NUMBER,			&sdb.area.uvnum,
	{ 0 },		NULL,				NULL
    },
    {
	"Security",	DTYPE_NUMBER,			&sdb.area.security,
	{ 10 },		NULL,				NULL
    },
    {
	"Recall",	DTYPE_NUMBER,			&sdb.area.recall,
	{ ROOM_VNUM_TEMPLE },	NULL,			NULL
    },
    {
	"Temp",		DTYPE_NUMBER,			&sdb.area.ave_temp,
	{ 0 },		NULL,				NULL
    },
    {
	"Flags",	DTYPE_NUMBER | DFLAG_SPECWRITE,	&sdb.area.area_flags,
	{ 0 },		"area",				dbrwf_area_flags
    },
    {
	"Order",	DTYPE_COMPLEX | DFLAG_SPECIAL,	&sdb.area.order,
	{ 0 },		NULL,				dbrwf_all_clan
    },
    {
	"TokenRoom",	DTYPE_NUMBER,			&sdb.area.token_room,
	{ -1 },		NULL,				dbrwf_check_room
    },
    {
	"Economy",	DTYPE_NUMBER,			&sdb.area.economy,
	{ 0 },		NULL,				NULL
    },
    {
	"Desc",		DTYPE_STRING,			&sdb.area.description,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Help */
struct data_desc_type help_data_table[] = {
    {
	"Level",	DTYPE_NUMBER,			&sdb.help.level,
	{ 0 },		NULL,				NULL
    },
    {
	"Name",		DTYPE_STRING | DFLAG_MAND,	&sdb.help.keyword,
	{ 0 },		NULL,				NULL
    },
    {
	"Text",		DTYPE_STRING | DFLAG_MAND,	&sdb.help.text,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Mobile Index */
struct data_desc_type mobile_data_table[] = {
    {
	"Vnum",		DTYPE_NUMBER | DFLAG_MAND,	&sdb.mob_index.vnum,
	{ 0 },		NULL,				NULL
    },
    {
	"Name",		DTYPE_STRING | DFLAG_MAND,	&sdb.mob_index.name,
	{ 0 },		NULL,				NULL
    },
    {
	"Short",	DTYPE_STRING | DFLAG_MAND,	&sdb.mob_index.short_descr,
	{ 0 },		NULL,				NULL
    },
    {
	"Long",		DTYPE_STRING | DFLAG_MAND,	&sdb.mob_index.long_descr,
	{ 0 },		NULL,				NULL
    },
    {
	"Desc",		DTYPE_STRING,			&sdb.mob_index.description,
	{ 0 },		NULL,				NULL
    },
    {
	"Level",	DTYPE_NUMBER,			&sdb.mob_index.level,
	{ 1 },		NULL,				NULL
    },
    {
	"Align",	DTYPE_NUMBER,			&sdb.mob_index.alignment,
	{ 0 },		NULL,				NULL
    },
    {
	"Repop",	DTYPE_NUMBER,			&sdb.mob_index.reset_chance,
	{ 1000 },	NULL,				NULL
    },
    {
	"Class",	DTYPE_NUMBER,			&sdb.mob_index.class,
	{ CLASS_NONE },	"class",			NULL
    },
    {
	"Act",		DTYPE_VECTOR,			sdb.mob_index.act,
	{ 0 },		"act",				NULL
    },
    {
	"Affected",	DTYPE_VECTOR,			sdb.mob_index.affected_by,
	{ 0 },		"affect",			NULL
    },
    {
	"Sex",		DTYPE_NUMBER,			&sdb.mob_index.sex,
	{ SEX_NEUTRAL }, "sex",				NULL
    },
    {
	"Race",		DTYPE_NUMBER,			&sdb.mob_index.race,
	{ 0 },		"race",				NULL
    },
    {
	"Parts",	DTYPE_NUMBER | DFLAG_CHKDEFAULT | DFLAG_NEEDPARENT,
	&sdb.mob_index.body_parts,
	{ -1 },		"limbs",			dbrwf_mob_parts
    },
    {
	"Specfun",	DTYPE_NUMBER | DFLAG_SPECIAL,	&sdb.mob_index.spec_fun,
	{ 0 },		NULL,				dbrwf_mob_spec
    },
    {
	"Shop",		DTYPE_COMPLEX,			&sdb.mob_index.pShop,
	{ 0 },		NULL,				NULL
    },
    {
	"MudProg",	DTYPE_LLIST,			&sdb.mob_index.mudprogs,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};


/* Mobile : Shop */
struct data_desc_type shop_data_table[] = {
    {
	"Trade",
	DTYPE_NUMBER | DFLAG_ARRAY | DFLAG_SPECWRITE | ( MAX_TRADE << DSHIFT_ARRAY ),
	sdb.shop.buy_type,
	{ -1 },		"type",				dbrwf_shop_trade
    },
    {
	"Markup",	DTYPE_NUMBER,			&sdb.shop.profit_buy,
	{ 150 },	NULL,				NULL
    },
    {
	"Markdown",	DTYPE_NUMBER,			&sdb.shop.profit_sell,
	{ 0 },		NULL,				NULL
    },
    {
	"Open",		DTYPE_NUMBER,			&sdb.shop.open_hour,
	{ 0 },		NULL,				NULL
    },
    {
	"Close",	DTYPE_NUMBER,			&sdb.shop.close_hour,
	{ 23 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};


/* Mobile/Object/Room : MudProg */
struct data_desc_type mudprog_data_table[] = {
    {
	"Type",		DTYPE_NUMBER,			&sdb.mprog.type,
	{ -1 },		"mudprogs",			NULL
    },
    {
	"Args",		DTYPE_STRING,			&sdb.mprog.arglist,
	{ 0 },		NULL,				NULL
    },
    {
	"Comlist",	DTYPE_STRING,			&sdb.mprog.comlist,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};


/* Object Index */
struct data_desc_type object_data_table[] = {
    {
	"Vnum",		DTYPE_NUMBER | DFLAG_MAND,	&sdb.obj_index.vnum,
	{ 0 },		NULL,				NULL
    },
    {
	"Name",		DTYPE_STRING,			&sdb.obj_index.name,
	{ 0 },		NULL,				NULL
    },
    {
	"Short",	DTYPE_STRING,			&sdb.obj_index.short_descr,
	{ 0 },		NULL,				NULL
    },
    {
	"Long",		DTYPE_STRING,			&sdb.obj_index.description,
	{ 0 },		NULL,				NULL
    },
    {
	"Action",	DTYPE_STRING,			&sdb.obj_index.action,
	{ 0 },		NULL,				NULL
    },
    {
	"Level",	DTYPE_NUMBER,			&sdb.obj_index.level,
	{ LEVEL_HERO },	NULL,				NULL
    },
    {
	"Cost",		DTYPE_NUMBER,			&sdb.obj_index.cost,
	{ 0 },		NULL,				NULL
    },
    {
	"Weight",	DTYPE_NUMBER,			&sdb.obj_index.weight,
	{ 1 },		NULL,				NULL
    },
    {
	"Repop",	DTYPE_NUMBER,			&sdb.obj_index.reset_chance,
	{ 1000 },	NULL,				NULL
    },
    {
	"Type",		DTYPE_NUMBER,			&sdb.obj_index.item_type,
	{ ITEM_TRASH },	"type",				NULL
    },
    {
	"Wear",		DTYPE_NUMBER,			&sdb.obj_index.wear_flags,
	{ 0 },		"wear",				NULL
    },
    {
	"Extra",	DTYPE_NUMBER,			&sdb.obj_index.extra_flags,
	{ 0 },		"extra",			NULL
    },
    {
	"Material",	DTYPE_NUMBER,			&sdb.obj_index.material,
	{ 100 },	"material",			NULL
    },
    {
	"Condition",	DTYPE_NUMBER,			&sdb.obj_index.condition,
	{ 1000 },	NULL,				NULL
    },
    {
	"Required",	DTYPE_NUMBER,			&sdb.obj_index.required_skill,
	{ -1 },		"skill",			NULL
    },
    {
	"Values",
	DTYPE_NUMBER | DFLAG_ARRAY | DFLAG_SPECIAL | DFLAG_NEEDPARENT
	| ( 4 << DSHIFT_ARRAY ),
	&sdb.obj_index.value[0],
	{ 0 },		"skill",			dbrwf_obj_index_values
    },
    {
	"Affect",	DTYPE_LLIST | DFLAG_SPECWRITE,	&sdb.obj_index.affected,
	{ 0 },		NULL,				dbrwf_all_affect
    },
    {
	"ExDesc",	DTYPE_LLIST,			&sdb.obj_index.extra_descr,
	{ 0 },		NULL,				NULL
    },
    {
	"MudProg",	DTYPE_LLIST,			&sdb.obj_index.mudprogs,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Affect - NOTE: This shouldn't really be used to write data. */
struct data_desc_type affect_data_table[] = {
    {
	"Name",		DTYPE_NUMBER,			&sdb.aff.type,
	{ -1 },		"skill",			NULL
    },
    {
	"Duration",	DTYPE_NUMBER,			&sdb.aff.duration,
	{ -1 },		NULL,				NULL
    },
    {
	"Level",	DTYPE_NUMBER,			&sdb.aff.level,
	{ 0 },		NULL,				NULL
    },
    {
	"Location",	DTYPE_NUMBER,			&sdb.aff.location,
	{ APPLY_NONE },	"apply",			NULL
    },
    {
	"Modifier",	DTYPE_NUMBER,			&sdb.aff.modifier,
	{ 0 },		NULL,				NULL
    },
    {
	"Bits",		DTYPE_NUMBER,			sdb.aff.bitvector,
	{ 0 },		"affect",			NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Object/Room : ExDesc */
struct data_desc_type exdesc_data_table[] = {
    {
	"Keyword",	DTYPE_STRING | DFLAG_MAND,	&sdb.exdesc.keyword,
	{ 0 },		NULL,				NULL
    },
    {
	"Text",		DTYPE_STRING,			&sdb.exdesc.description,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Room */
struct data_desc_type room_data_table[] = {
    {
	"Vnum",		DTYPE_NUMBER | DFLAG_MAND,	&sdb.room.vnum,
	{ 0 },		NULL,				NULL
    },
    {
	"Name",		DTYPE_STRING,			&sdb.room.name,
	{ 0 },		NULL,				NULL
    },
    {
	"Desc",		DTYPE_STRING,			&sdb.room.description,
	{ 0 },		NULL,				NULL
    },
    {
	"Flags",	DTYPE_NUMBER,			&sdb.room.room_flags,
	{ 0 },		"room",				NULL
    },
    {
	"Sector",	DTYPE_NUMBER,			&sdb.room.sector_type,
	{ 0 },		"sector",			NULL
    },
    {
	"Exit",
	DTYPE_COMPLEX | DFLAG_ARRAY | DFLAG_SPECWRITE
	| DFLAG_CHKDEFAULT | ( MAX_DIR << DSHIFT_ARRAY ),
	&sdb.room.exit[0],
	{ 0 },		NULL,				dbrwf_room_exits
    },
    {
	"ExDesc",	DTYPE_LLIST,			&sdb.room.extra_descr,
	{ 0 },		NULL,				NULL
    },
    {
	"Resets",	DTYPE_COMPLEX | DFLAG_SPECIAL,	&sdb.room.reset_first,
	{ 0 },		NULL,				dbrwf_room_resets
    },
    {
	"MudProg",	DTYPE_LLIST,			&sdb.room.mudprogs,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Room : Exit */
struct data_desc_type exit_data_table[] = {
    {
	"Desc",		DTYPE_STRING,			&sdb.exit.description,
	{ 0 },		NULL,				NULL
    },
    {
	"Keyword",	DTYPE_STRING,			&sdb.exit.keyword,
	{ 0 },		NULL,				NULL
    },
    {
	"Key",		DTYPE_NUMBER,			&sdb.exit.key,
	{ -1 },		NULL,				NULL
    },
    {
	"ToRoom",	DTYPE_NUMBER,			&sdb.exit.vnum,
	{ -1 },		NULL,				NULL
    },
    {
	"Flags",	DTYPE_NUMBER,			&sdb.exit.rs_flags,
	{ 0 },		"exit",				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Global Program */
struct data_desc_type gprog_data_table[] = {
    {
	"Vnum",		DTYPE_NUMBER | DFLAG_MAND,	&sdb.gprog.vnum,
	{ 0 },		NULL,				NULL
    },
    {
	"Type",		DTYPE_NUMBER,			&sdb.gprog.type,
	{ 0 },		"mudprogs",			NULL
    },
    {
	"Allowed",	DTYPE_NUMBER,			&sdb.gprog.allowed,
	{ 0 },		"mor",				NULL
    },
    {
	"Args",		DTYPE_STRING,			&sdb.gprog.arglist,
	{ 0 },		NULL,				NULL
    },
    {
	"Comlist",	DTYPE_STRING,			&sdb.gprog.comlist,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};


/* Social */
struct data_desc_type social_data_table[] = {
    {
	"Name",		DTYPE_STRING | DFLAG_MAND,	&sdb.soc.name,
	{ 0 },		NULL,				NULL
    },
    {
	"CNA",		DTYPE_STRING,			&sdb.soc.char_no_arg,
	{ 0 },		NULL,				NULL
    },
    {
	"ONA",		DTYPE_STRING,			&sdb.soc.others_no_arg,
	{ 0 },		NULL,				NULL
    },
    {
	"CF",		DTYPE_STRING,			&sdb.soc.char_found,
	{ 0 },		NULL,				NULL
    },
    {
	"OF",		DTYPE_STRING,			&sdb.soc.others_found,
	{ 0 },		NULL,				NULL
    },
    {
	"VF",		DTYPE_STRING,			&sdb.soc.vict_found,
	{ 0 },		NULL,				NULL
    },
    {
	"CA",		DTYPE_STRING,			&sdb.soc.char_auto,
	{ 0 },		NULL,				NULL
    },
    {
	"OA",		DTYPE_STRING,			&sdb.soc.others_auto,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Religion */
struct data_desc_type religion_data_table[] = {
    {
	"Name",		DTYPE_STRING | DFLAG_MAND,	&sdb.rel.name,
	{ 0 },		NULL,				NULL
    },
    {
	"Display",	DTYPE_STRING,			&sdb.rel.display_name,
	{ 0 },		NULL,				NULL
    },
    {
	"God",		DTYPE_STRING,			&sdb.rel.god_name,
	{ 0 },		NULL,				NULL
    },
    {
	"AlignMin",	DTYPE_NUMBER,			&sdb.rel.align_min,
	{ -1000 },	NULL,				NULL
    },
    {
	"AlignMaxn",	DTYPE_NUMBER,			&sdb.rel.align_max,
	{ 1000 },	NULL,				NULL
    },
    {
	"Token",	DTYPE_NUMBER,			&sdb.rel.token,
	{ -1 },		NULL,				NULL
    },
    {
	"Sacrifice",	DTYPE_NUMBER,			&sdb.rel.sac_events,
	{ 0 },		"sacrifice",			NULL
    },
    {
	"RelSkill",	DTYPE_LLIST,			&sdb.rel.skills,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Clan */
struct data_desc_type clan_data_table[] = {
    {
	"Name",		DTYPE_STRING | DFLAG_MAND,	&sdb.clan.name,
	{ 0 },		NULL,				NULL
    },
    {
	"Display",	DTYPE_STRING,			&sdb.clan.display_name,
	{ 0 },		NULL,				NULL
    },
    {
	"Motto",	DTYPE_STRING,			&sdb.clan.motto,
	{ 0 },		NULL,				NULL
    },
    {
	"Desc",		DTYPE_STRING,			&sdb.clan.description,
	{ 0 },		NULL,				NULL
    },
    {
	"Rules",	DTYPE_STRING,			&sdb.clan.rules,
	{ 0 },		NULL,				NULL
    },
    {
	"Overlord",	DTYPE_STRING,			&sdb.clan.overlord,
	{ 0 },		NULL,				NULL
    },
    {
	"Chiefs",	DTYPE_STRING,			&sdb.clan.chieftains,
	{ 0 },		NULL,				NULL
    },
    {
	"Enemies",	DTYPE_STRING,			&sdb.clan.enemies,
	{ 0 },		NULL,				NULL
    },
    {
	"Type",		DTYPE_NUMBER,			&sdb.clan.clan_type,
	{ CLAN_NORMAL }, "clan",			NULL
    },
    {
	"Heroes",	DTYPE_NUMBER,			&sdb.clan.clanheros,
	{ 0 },		NULL,				NULL
    },
    {
	"Members",	DTYPE_NUMBER,			&sdb.clan.members,
	{ 0 },		NULL,				NULL
    },
    {
	"Kills",	DTYPE_NUMBER,			&sdb.clan.kills,
	{ 0 },		NULL,				NULL
    },
    {
	"Deaths",	DTYPE_NUMBER,			&sdb.clan.deaths,
	{ 0 },		NULL,				NULL
    },
    {
	"Recall",	DTYPE_NUMBER,			&sdb.clan.recall,
	{ ROOM_VNUM_TEMPLE }, NULL,			NULL
    },
    {
	"Religion",	DTYPE_COMPLEX | DFLAG_SPECIAL,	&sdb.clan.religion,
	{ 0 },		NULL,				dbrwf_all_religion
    },
    {
	"Class",	DTYPE_NUMBER,			&sdb.clan.class,
	{ CLASS_NONE },	"class",			NULL
    },
    {
	"Karma",	DTYPE_NUMBER,			&sdb.clan.karma,
	{ 0 },		NULL,				NULL
    },
    {
	"RelSkill",	DTYPE_LLIST,			&sdb.clan.skills,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Religion/Clan : RelSkill */
struct data_desc_type relskill_data_table[] = {
    {
	"Name",		DTYPE_NUMBER | DFLAG_MAND,	&sdb.relskill.sn,
	{ 0 },		"skill",			NULL
    },
    {
	"Level",	DTYPE_NUMBER,			&sdb.relskill.level,
	{ 10 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Note */
struct data_desc_type note_data_table[] = {
    {
	"Sender",	DTYPE_STRING | DFLAG_MAND,	&sdb.note.sender,
	{ 0 },		NULL,				NULL
    },
    {
	"Date",		DTYPE_NUMBER,			&sdb.note.date,
	{ 0 },		NULL,				NULL
    },
    {
	"Stamp",	DTYPE_NUMBER,			&sdb.note.date_stamp,
	{ 0 },		NULL,				NULL
    },
    {
	"Expire",	DTYPE_NUMBER,			&sdb.note.expire,
	{ 0 },		NULL,				NULL
    },
    {
	"To",		DTYPE_STRING | DFLAG_MAND,	&sdb.note.to_list,
	{ 0 },		NULL,				NULL
    },
    {
	"Subject",	DTYPE_STRING,			&sdb.note.subject,
	{ 0 },		NULL,				NULL
    },
    {
	"Text",		DTYPE_STRING,			&sdb.note.text,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Pose */
struct data_desc_type pose_data_table[] = {
    {
	"Char",		DTYPE_STRING,			&sdb.pose.to_char,
	{ 0 },		NULL,				NULL
    },
    {
	"Room",		DTYPE_STRING,			&sdb.pose.to_room,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* SysInfo */
struct data_desc_type sysinfo_data_table[] = {
    {
	"Name",		DTYPE_STRING,			&sdb.sys.name,
	{ 0 },		NULL,				NULL
    },
    {
	"Version",	DTYPE_NUMBER,			&sdb.sys.version,
	{ 1 },		NULL,				NULL
    },
    {
	"Minor",	DTYPE_NUMBER,			&sdb.sys.minor,
	{ 0 },		NULL,				NULL
    },
    {
	"Build",	DTYPE_NUMBER,			&sdb.sys.build,
	{ 1 },		NULL,				NULL
    },
    {
	"Date",		DTYPE_STRING,			&sdb.sys.build_date,
	{ 0 },		NULL,				NULL
    },
    {
	"Platform",	DTYPE_STRING,			&sdb.sys.platform,
	{ 0 },		NULL,				NULL
    },
    {
	"SaveAt",	DTYPE_NUMBER,			&sdb.sys.saveat,
	{ 2 },		NULL,				NULL
    },
    {
	"File",		DTYPE_NUMBER,			&sdb.sys.file_version,
	{ 0 },		NULL,				NULL
    },
    {
	"Dictionary",	DTYPE_STRING,			&sdb.sys.dictionary,
	{ 0 },		NULL,				NULL
    },
    {
	"Numlock",	DTYPE_NUMBER,			&sdb.sys.numlock,
	{ 0 },		NULL,				NULL
    },
    {
	"Flags",	DTYPE_NUMBER,			&sdb.sys.flags,
	{ 0 },		"sysinfo",			NULL
    },
    {
	"Hour",		DTYPE_NUMBER,			&sdb.sys.levels,
	{ 0 },		NULL,				NULL
    },
    {
	"Minute",	DTYPE_NUMBER,			&sdb.sys.deaths,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Char */
struct data_desc_type char_data_table[] = {
    {
	"Name",		DTYPE_STRING | DFLAG_MAND,	&sdb.ch.name,
	{ 0 },		NULL,				NULL
    },
    {
	"Level",	DTYPE_NUMBER | DFLAG_MAND,	&sdb.ch.level,
	{ 0 },		NULL,				NULL
    },
    {
	"Trust",	DTYPE_NUMBER,			&sdb.ch.trust,
	{ 0 },		NULL,				NULL
    },
    {
	"SubLevel",	DTYPE_NUMBER,			&sdb.ch.sublevel,
	{ 0 },		NULL,				NULL
    },
    {
	"Race",		DTYPE_NUMBER | DFLAG_SPECWRITE | DFLAG_NEEDPARENT,
	&sdb.ch.race,
	{ 0 },		"race",				dbrwf_char_race
    },
    {
	"Class",	DTYPE_NUMBER,			&sdb.ch.class,
	{ 0 },		"class",			NULL
    },
    {
	"ShortDesc",	DTYPE_STRING,			&sdb.ch.short_descr,
	{ 0 },		NULL,				NULL
    },
    {
	"LongDesc",	DTYPE_STRING,			&sdb.ch.long_descr,
	{ 0 },		NULL,				NULL
    },
    {
	"Desc",		DTYPE_STRING,			&sdb.ch.description,
	{ 0 },		NULL,				NULL
    },
    {
	"Sex",		DTYPE_NUMBER | DFLAG_SPECWRITE | DFLAG_NEEDPARENT,
	&sdb.ch.sex,
	{ 0 },		"sex",				dbrwf_char_sex
    },
    {
	"InRoom",	DTYPE_COMPLEX | DFLAG_SPECIAL | DFLAG_NEEDPARENT,
	&sdb.ch.in_room,
	{ 0 },		NULL,				dbrwf_char_in_room
    },
    {
	"Recall",	DTYPE_NUMBER,			&sdb.ch.recall_room,
	{ -1 },		NULL,				dbrwf_check_room
    },
    {
	"Played",	DTYPE_TIME | DFLAG_SPECWRITE | DFLAG_NEEDPARENT,
	&sdb.ch.played,
	{ 0 },		NULL,				dbrwf_char_played
    },
    {
	"Hp",		DTYPE_NUMBER,			&sdb.ch.hit,
	{ 0 },		NULL,				NULL
    },
    {
	"MaxHp",	DTYPE_NUMBER | DFLAG_SPECWRITE | DFLAG_NEEDPARENT,
	&sdb.ch.max_hit,
	{ 0 },		NULL,				dbrwf_char_max_hit
    },
    {
	"Mana",		DTYPE_NUMBER | DFLAG_ARRAY | DFLAG_SPECWRITE
	| DFLAG_CHKDEFAULT | ( MAGIC_MAX << DSHIFT_ARRAY ),
	&sdb.ch.mana[0],
	{ 0 },		NULL,				dbrwf_magic_mana
    },
    {
	"MaxMana",		DTYPE_NUMBER | DFLAG_ARRAY | DFLAG_SPECWRITE
	| DFLAG_CHKDEFAULT | DFLAG_NEEDPARENT | ( MAGIC_MAX << DSHIFT_ARRAY ),
	&sdb.ch.max_mana[0],
	{ 0 },		NULL,				dbrwf_char_max_mana
    },
    {
	"Move",		DTYPE_NUMBER,			&sdb.ch.move,
	{ 0 },		NULL,				NULL
    },
    {
	"MaxMove",	DTYPE_NUMBER | DFLAG_SPECWRITE | DFLAG_NEEDPARENT,
	&sdb.ch.max_move,
	{ 0 },		NULL,				dbrwf_char_max_move
    },
    {
	"Gold",		DTYPE_NUMBER,			&sdb.ch.gold,
	{ 0 },		NULL,				NULL
    },
    {
	"Tnl",		DTYPE_NUMBER,			&sdb.ch.exp,
	{ 1000 },	NULL,				NULL
    },
    {
	"Pos",		DTYPE_NUMBER | DFLAG_CHKDEFAULT, &sdb.ch.position,
	{ POS_STANDING }, "position",			dbrwf_char_position
    },
    {
	"Prac",		DTYPE_NUMBER,			&sdb.ch.practice,
	{ 0 },		NULL,				NULL
    },
    {
	"Align",	DTYPE_NUMBER,			&sdb.ch.alignment,
	{ 0 },		NULL,				NULL
    },
    {
	"Wimpy",	DTYPE_NUMBER,			&sdb.ch.wimpy,
	{ 0 },		NULL,				NULL
    },
    {
	"Deaf",		DTYPE_NUMBER,			&sdb.ch.deaf,
	{ 0 },		"channel",			NULL
    },
    {
	"Parts",	DTYPE_NUMBER | DFLAG_CHKDEFAULT | DFLAG_NEEDPARENT,
	&sdb.ch.body_parts,
	{ 0 },		"limbs",			dbrwf_char_parts
    },
    {
	"Damaged",	DTYPE_NUMBER,			&sdb.ch.damaged_parts,
	{ 0 },		"limbs",			NULL
    },
    {
	"Act",		DTYPE_VECTOR,			sdb.ch.act,
	{ 0 },		"pcact",			NULL
    },
    {
	"Affect",	DTYPE_LLIST | DFLAG_SPECWRITE,	&sdb.ch.affected,
	{ 0 },		NULL,				dbrwf_all_affect
    },
    {
	"Event",	DTYPE_LLIST,			&sdb.ch.events,
	{ 0 },		NULL,				NULL
    },
    {
	"PcData",	DTYPE_COMPLEX,			&sdb.ch.pcdata,
	{ 0 },		NULL,				NULL
    },
    {
	"Obj",		DTYPE_COMPLEX | DFLAG_SPECIAL | DFLAG_NEEDPARENT,
	&sdb.ch.carrying,
	{ 0 },		NULL,				dbrwf_char_obj
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Char : PcData */
struct data_desc_type pcdata_data_table[] = {
    {
	"Title",	DTYPE_STRING,			&sdb.pcdata.title,
	{ 0 },		NULL,				NULL
    },
    {
	"Prompt",	DTYPE_STRING,			&sdb.pcdata.prompt,
	{ 0 },		NULL,				NULL
    },
    {
	"Password",	DTYPE_STRING | DFLAG_MAND,	&sdb.pcdata.pwd,
	{ 0 },		NULL,				NULL
    },
    {
	"ImmName",	DTYPE_STRING,			&sdb.pcdata.immname,
	{ 0 },		NULL,				NULL
    },
    {
	"Bamfin",	DTYPE_STRING,			&sdb.pcdata.bamfin,
	{ 0 },		NULL,				NULL
    },
    {
	"Bamfout",	DTYPE_STRING,			&sdb.pcdata.bamfout,
	{ 0 },		NULL,				NULL
    },
    {
	"Setmin",	DTYPE_STRING,			&sdb.pcdata.setmin,
	{ 0 },		NULL,				NULL
    },
    {
	"Setmout",	DTYPE_STRING,			&sdb.pcdata.setmout,
	{ 0 },		NULL,				NULL
    },
    {
	"Immskill",	DTYPE_STRING,			&sdb.pcdata.immskll,
	{ 0 },		NULL,				NULL
    },
    {
	"Familiar",	DTYPE_NUMBER,			&sdb.pcdata.familiar,
	{ 0 },		NULL,				NULL
    },
    {
	"Bounty",	DTYPE_NUMBER,			&sdb.pcdata.bounty,
	{ 0 },		NULL,				NULL
    },
    {
	"Killed",	DTYPE_NUMBER,			&sdb.pcdata.killed,
	{ 0 },		NULL,				NULL
    },
    {
	"Died",		DTYPE_NUMBER,			&sdb.pcdata.died,
	{ 0 },		NULL,				NULL
    },
    {
	"Strength",	DTYPE_NUMBER,			&sdb.pcdata.perm_str,
	{ 15 },		NULL,				NULL
    },
    {
	"Intelligence",	DTYPE_NUMBER,			&sdb.pcdata.perm_int,
	{ 15 },		NULL,				NULL
    },
    {
	"Wisdom",	DTYPE_NUMBER,			&sdb.pcdata.perm_wis,
	{ 15 },		NULL,				NULL
    },
    {
	"Dexterity",	DTYPE_NUMBER,			&sdb.pcdata.perm_dex,
	{ 15 },		NULL,				NULL
    },
    {
	"Constitution",	DTYPE_NUMBER,			&sdb.pcdata.perm_con,
	{ 15 },		NULL,				NULL
    },
    {
	"Magic",	DTYPE_NUMBER | DFLAG_ARRAY | DFLAG_SPECWRITE
	| DFLAG_CHKDEFAULT | ( MAGIC_MAX << DSHIFT_ARRAY ),
	&sdb.pcdata.perm_magic[0],
	{ 0 },		NULL,				dbrwf_magic_mana
    },
    {
	"Thirst",	DTYPE_NUMBER,			&sdb.pcdata.condition[COND_THIRST],
	{ 1000 },	NULL,				NULL
    },
    {
	"Full",		DTYPE_NUMBER,			&sdb.pcdata.condition[COND_FULL],
	{ 1000 },	NULL,				NULL
    },
    {
	"Drunk",	DTYPE_NUMBER,			&sdb.pcdata.condition[COND_DRUNK],
	{ 0 },		NULL,				NULL
    },
    {
	"Bank",		DTYPE_NUMBER,			&sdb.pcdata.banked,
	{ 0 },		NULL,				NULL
    },
    {
	"Page",		DTYPE_NUMBER,			&sdb.pcdata.pagelen,
	{ 20 },		NULL,				NULL
    },
    {
	"Security",	DTYPE_NUMBER,			&sdb.pcdata.security,
	{ 0 },		NULL,				NULL
    },
    {
	"Language",	DTYPE_NUMBER,			&sdb.pcdata.language,
	{ LANG_COMMON }, "language",			NULL
    },
    {
	"Quest",	DTYPE_COMPLEX,			&sdb.pcdata.quest,
	{ 0 },		NULL,				NULL
    },
    {
	"Board",	DTYPE_TIME | DFLAG_ARRAY | DFLAG_SPECWRITE
	| ( MAX_BOARD << DSHIFT_ARRAY ),
	&sdb.pcdata.last_note[0],
	{ 0 },		NULL,				dbrwf_pcdata_board
    },
    {
	"Religion",	DTYPE_COMPLEX | DFLAG_SPECIAL,	&sdb.pcdata.religion,
	{ 0 },		NULL,				dbrwf_all_religion
    },
    {
	"Clan",		DTYPE_COMPLEX | DFLAG_SPECIAL,	&sdb.pcdata.clan,
	{ 0 },		NULL,				dbrwf_all_clan
    },
    {
	"MultiClass",	DTYPE_NUMBER | DFLAG_ARRAY | DFLAG_SPECWRITE
	| ( NUM_MULTI_CLASS << DSHIFT_ARRAY ),
	&sdb.pcdata.multi_class[0],
	{ CLASS_UNKNOWN }, NULL,			dbrwf_pcdata_multi_class
    },
    {
	"Skill",	DTYPE_NUMBER | DFLAG_ARRAY | DFLAG_SPECWRITE
	| ( MAX_SKILL << DSHIFT_ARRAY ),
	&sdb.pcdata.learned[0],
	{ 0 },		NULL,				dbrwf_pcdata_skills
    },
    {
	"Alias",	DTYPE_LLIST,			&sdb.pcdata.aliases,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Char : PcData : Quest */
struct data_desc_type quest_data_table[] = {
    {
	"Time",		DTYPE_NUMBER,			&sdb.quest.time,
	{ 0 },		NULL,				NULL
    },
    {
	"Score",	DTYPE_NUMBER,			&sdb.quest.score,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};


/* Char : PcData : Alias */
struct data_desc_type alias_data_table[] = {
    {
	"Name",		DTYPE_STRING,			&sdb.alias.name,
	{ 0 },		NULL,				NULL
    },
    {
	"Command",	DTYPE_STRING,			&sdb.alias.command,
	{ 0 },		NULL,				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};


/* Char : Event */
struct data_desc_type event_data_table[] = {
    {
	"When",		DTYPE_TIME | DFLAG_SPECWRITE | DFLAG_NEEDPARENT,
	&sdb.event.when,
	{ 0 },		NULL,				dbrwf_event_when
    },
    {
	"Type",		DTYPE_NUMBER,			&sdb.event.type,
	{ 0 },		"event",			NULL
    },
    {
	"Flags",	DTYPE_NUMBER,			&sdb.event.flags,
	{ 0 },		"evextra",			NULL
    },
    {
	"Text",		DTYPE_STRING,			&sdb.event.text,
	{ 0 },		NULL,				NULL
    },
    {
	"Data",		DTYPE_NUMBER | DFLAG_ARRAY | DFLAG_SPECWRITE
	| ( MAX_EVENT_DATA << DSHIFT_ARRAY ),		&sdb.event.data[0],
	{ 0 },		NULL,				dbrwf_event_data
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* Char : Obj */
struct data_desc_type obj_data_table[] = {
    {
	"Vnum",		DTYPE_COMPLEX | DFLAG_MAND | DFLAG_SPECIAL,
	&sdb.obj.pIndexData,
	{ 0 },		NULL,				dbrwf_obj_vnum
    },
    {
	"Key",		DTYPE_NUMBER | DFLAG_MAND,	&sdb.obj.unique_key,
	{ 0 },		NULL,				NULL
    },
    {
	"Name",		DTYPE_STRING | DFLAG_MAND,	&sdb.obj.name,
	{ 0 },		NULL,				NULL
    },
    {
	"Short",	DTYPE_STRING,			&sdb.obj.short_descr,
	{ 0 },		NULL,				NULL
    },
    {
	"Desc",		DTYPE_STRING,			&sdb.obj.description,
	{ 0 },		NULL,				NULL
    },
    {
	"Action",	DTYPE_STRING,			&sdb.obj.action,
	{ 0 },		NULL,				NULL
    },
    {
	"Required",	DTYPE_NUMBER,			&sdb.obj.required_skill,
	{ -1 },		"skill",			NULL
    },
    {
	"Extra",	DTYPE_NUMBER,			&sdb.obj.extra_flags,
	{ 0 },		"extra",			NULL
    },
    {
	"Wear",		DTYPE_NUMBER,			&sdb.obj.wear_flags,
	{ 0 },		"wear",				NULL
    },
    {
	"Location",	DTYPE_NUMBER,			&sdb.obj.wear_loc,
	{ 0 },		"wear-loc",			NULL
    },
    {
	"Type",		DTYPE_NUMBER,			&sdb.obj.item_type,
	{ ITEM_TRASH },	"type",				NULL
    },
    {
	"Level",	DTYPE_NUMBER,			&sdb.obj.level,
	{ LEVEL_HERO },	NULL,				NULL
    },
    {
	"Weight",	DTYPE_NUMBER,			&sdb.obj.weight,
	{ 1 },		NULL,				NULL
    },
    {
	"Cost",		DTYPE_NUMBER,			&sdb.obj.cost,
	{ 300 },	NULL,				NULL
    },
    {
	"Condition",	DTYPE_NUMBER,			&sdb.obj.condition,
	{ 1000 },	NULL,				NULL
    },
    {
	"Affect",	DTYPE_LLIST | DFLAG_SPECWRITE,	&sdb.obj.affected,
	{ 0 },		NULL,				dbrwf_all_affect
    },
    {
	"ExDesc",	DTYPE_LLIST,			&sdb.obj.extra_descr,
	{ 0 },		NULL,				NULL
    },
    {
	"Event",	DTYPE_LLIST,			&sdb.obj.events,
	{ 0 },		NULL,				NULL
    },
    {
	"Values",
	DTYPE_NUMBER | DFLAG_ARRAY | DFLAG_SPECIAL | DFLAG_NEEDPARENT
	| ( 4 << DSHIFT_ARRAY ),
	&sdb.obj.value[0],
	{ 0 },		"skill",			dbrwf_obj_values
    },

    {
	"Obj",		DTYPE_COMPLEX | DFLAG_SPECIAL | DFLAG_NEEDPARENT,
	&sdb.obj.contains,
	{ 0 },		NULL,				dbrwf_obj_obj
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* High */
struct data_desc_type high_data_table[] = {
    {
	"Type",		DTYPE_STRING,			&sdb.high.type,
	{ 0 },		NULL,				NULL
    },
    {
	"HighEnt",	DTYPE_COMPLEX | DFLAG_ARRAY | DFLAG_SPECWRITE
	| ( 10 << DSHIFT_ARRAY ),
	&sdb.high.entries,
	{ 0 },		NULL,				dbrwf_high_entries
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};

/* High : HighEnt */
struct data_desc_type highent_data_table[] = {
    {
	"Name",		DTYPE_STRING,			&sdb.highent.name,
	{ 0 },		NULL,				NULL
    },
    {
	"Level",	DTYPE_NUMBER,			&sdb.highent.level,
	{ 0 },		NULL,				NULL
    },
    {
	"SubLevel",	DTYPE_NUMBER,			&sdb.highent.sublevel,
	{ 0 },		NULL,				NULL
    },
    {
	"Class",	DTYPE_NUMBER,			&sdb.highent.class,
	{ 0 },		"class",			NULL
    },
    {
	"Race",		DTYPE_NUMBER,			&sdb.highent.race,
	{ 0 },		"race",				NULL
    },

    {
	NULL, 0, NULL, { 0 }, NULL, NULL
    }
};


/*
 * Local functions.
 */
bool is_default         args( ( const struct top_data_type *block,
				const struct data_desc_type *entry ) );
void fix_data_desc_table args( ( struct data_desc_type *table ) );
int get_desc_entry	args( ( const struct data_desc_type *table,
				const char *name, int start ) );
int get_top_entry	args( ( const char *name ) );
bool dbrwf_all_obj	args( ( FILE *fp, CHAR_DATA *ch,
				OBJ_DATA *obj_cont, db_action action ) );

/*
 * This returns an index to the global_data_table element that matches
 * 'name'.
 */
int get_top_entry( const char *name )
{
    int i;

    for( i = 0; global_data_table[i].name != NULL; ++i )
	if( !str_cmp( global_data_table[i].name, name ) )
	    break;

    return i;
}


/*
 * This retrieves an entry with the name mentioned from the table.
 * The start value is used to increase the speed of the search where
 * this function is used for consecutive entries.
 * Returns: the index of the entry, -1 on error.
 */
int get_desc_entry( const struct data_desc_type *table, const char *name,
		    int start )
{
    int i;

    for( i = start; table[i].name != NULL; ++i )
	if( !str_cmp( table[i].name, name ) )
	    return i;

    for( i = 0; i < start; ++i )
	if( !str_cmp( table[i].name, name ) )
	    return i;

    return -1;
}


/*
 * This function assigns all of the default values to the data_desc tables.
 * This is needed as some compilers cannot handle the initialisation of
 * unions.
 * This should be run before any loading or saving occurs.
 */
void init_data_desc_tables()
{
    int i;

    for( i = 0; global_data_table[i].name != NULL; ++i )
	fix_data_desc_table( global_data_table[i].table );

    /* AREA */
    i = get_desc_entry( area_data_table, "Builders", 0 );
    area_data_table[i].deflt.string = "none";

    /* MOBILE */
    i = get_desc_entry( mobile_data_table, "Act", 0 );
    xSET_BIT( mobile_data_table[i].deflt.vector, ACT_IS_NPC );
}

/*
 * Helper for the above function, fills in default default values.
 */
void fix_data_desc_table( struct data_desc_type *table )
{
    int i;

    for( i = 0; table[i].name; ++i )
    {
	if( !( table[i].type & DFLAG_MAND ) )
	    switch( table[i].type & DTYPE_BASIC )
	    {
	    case DTYPE_STRING:
		table[i].deflt.string = &str_empty[0];
		break;
	    case DTYPE_VECTOR:
		vzero( table[i].deflt.vector );
		break;
	    case DTYPE_COMPLEX:
	    case DTYPE_LLIST:
		table[i].deflt.table = global_data_table + get_top_entry( table[i].name );
		break;
	    }
    }
}


/****************************************************************************
 * File opening helper for versioning.
 */
FILE *open_file( const char *name, const char *mode, bool version )
{
    FILE *fp;

    if( fpReserve )
	fclose( fpReserve );
    fpReserve = NULL;

    fp = fopen( name, mode );
    if( !fp )
    {
	fpReserve = fopen( NULL_FILE, "r" );
	return NULL;
    }

    if( version )
    {
	if( mode[0] == 'r' )
	{
	    if( SysInfo )
		file_version = SysInfo->file_version;
	    else
		file_version = 1;
	    file_date = current_time;
	}
	else if( mode[0] == 'w' )
	{
	    fprintf( fp, "# This file has been autogenerated, you can edit this online.\n" );
	    fprintf( fp, "-Version %d;\n", SysInfo->file_version );
	    fprintf( fp, "-Date %lu;\n\n", (unsigned long)current_time );
	}
    }

    return fp;
}

/* Neat function for re-opening reserve file. */
void close_file( FILE *fp )
{
    fclose( fp );
    fpReserve = fopen( NULL_FILE, "r" );
}



/****************************************************************************
 * Loading functions.
 */

/*
 * Load a list of files and from there load the files listed.
 */
void load_file_list( const char *dirname, const char *listname )
{
    char buf[MAX_INPUT_LENGTH];
    FILE *fpList;

    sprintf( buf, "%s%s", dirname, listname );
    if( !( fpList = open_file( buf, "r", FALSE ) ) )
    {
	perror( buf );
	exit( 1 );
    }

    for( ;; )
    {
	int stat;

	strcpy( buf, fread_word( fpList, &stat ) );
	if( buf[0] == '$' )
	    break;

	if( buf[0] == '-' )
	{
	    strcpy( strArea, buf+1 );
	    fpArea = stdin;
	    if( !load_file_stream( stdin ) )
		exit( 1 );
	    fpArea = NULL;
	}
	else
	{
	    strcpy( strArea, buf );
	    sprintf( buf, "%s%s", dirname, strArea );
	    if( !load_file( buf ) )
		exit( 1 );
	}

	if( IS_SET( SysInfo->flags, SYSINFO_VERBOSE_LOG ) )
	{
	    if( area_last )
	    {
		fprintf( stderr, "%-14s: Vnums: %5d - %-5d\n",
			 area_last->filename,
			 area_last->lvnum, area_last->uvnum );
	    }
	    else
		fprintf( stderr, "(%s)\n", strArea );
	}
	else
	    fputc( '.', stderr );
    }
    close_file( fpList );
}


/*
 * Generic loading function that automatically does everything.
 */
bool load_file( const char *filename )
{
    strcpy( strArea, filename );
    if( !( fpArea = open_file( filename, "r", TRUE ) ) )
    {
	bug( "Cannot open file for reading '%s'.", filename );
	return FALSE;
    }
    if( !load_file_stream( fpArea ) )
	return FALSE;

    close_file( fpArea );
    fpArea = NULL;
    strArea[0] = '\0';

    return TRUE;
}


/*
 * A little lower level version of the above.
 */
bool load_file_stream( FILE *fp )
{
    void *value;
    bool readArea = FALSE;
    int entry;

    for( ;; )
    {
	if( !( value = read_next_item( fp, &entry ) ) )
	    return FALSE;

	if( entry < 0 )
	{
	    if( readArea )
	    {
		area_last->ave = (battle_min)
		    ? battle_max / battle_min : 0;
		battle_min = battle_max = 0;
	    }
	    break;
	}

	if( !str_cmp( global_data_table[entry].name, "Area" ) )
	{
	    readArea = TRUE;
	}

	if( !global_data_table[entry].rwf
	    || !(*global_data_table[entry].rwf)( NULL, value, DBACTION_ADDWORLD ) )
	{
	    bug( "load_file_stream: Object couldn't be added to the world." );
	    return FALSE;
	}
    }
    return TRUE;
}



/*
 * Reads a value and returns (through the arguments) the name of the value's
 * type and the value itself.  Look for the name returned being "eof" or
 * "end", these signify the end of the file.
 *
 * NULL is returned on error.
 *
 * The value returned through status is the index to the global_data_table
 * that was read in.  This value contains -1 when the end of the file is
 * reached (the returned value in this case is the status value, so that a
 * check on NULL is possible).
 *
 * This function contains code to handle the '-Version' and '-Date' values in
 * files.  When these values are encountered they are placed in the globals
 * file_version and file_date respectively.
 */
void *read_next_item( FILE *fp, int *status )
{
    char *word;
    void *value = NULL;
    int letter, errstatus;

    do	/* while( !value ) */
    {
	letter = fread_letter( fp );
	if( letter == EOF )
	{
	    *status = -1;
	    return status;
	}
	while( letter == '#' )
	{
	    fread_to_eol( fp );
	    letter = fread_letter( fp );
	    if( letter == EOF )
	    {
		*status = -1;
		return status;
	    }
	}

	ungetc( letter, fp );
	word = fread_word( fp, &errstatus );

	if( errstatus )
	{
	    *status = -1;
	    return NULL;
	}

	if( !str_cmp( word, "eof" ) || !str_cmp( word, "end" )
	    || !str_cmp( word, "eof;" ) || !str_cmp( word, "end;" ) )
	{
	    *status = -1;
	    return status;
	}
	else if( !str_cmp( word, "-Version" ) )
	{
	    errstatus = (int)!fread_number_ii( fp, &file_version, NULL );
	    if( errstatus )
		return NULL;
	}
	else if( !str_cmp( word, "-Date" ) )
	{
	    file_date = fread_time( fp, &errstatus );
	    if( errstatus )
		return NULL;
	}
	else
	{
	    *status = get_top_entry( word );

	    if( global_data_table[*status].name == NULL )
	    {
		bug( "Read unknown section header: %s", word );
		return NULL;
	    }

	    value = load_complex_block( fp, global_data_table + *status );
	    if( !value )
	    {
		bug( "read_next_item: error reading item" );
		return NULL;
	    }
	}

	if( ( letter = fread_letter( fp ) != ';' ) )
	{
	    if( letter == EOF )
		bug( "read_next_item: EOF reached early." );
	    else
		bug( "read_next_item: missing ';' character." );
	    return NULL;
	}

    }
    while( !value );

    return value;
}


/*
 * Reads a block from the first '{' to the last '}'.
 *
 * Caveat: The default value for an array cannot be asserted.  This value is
 * used for only the first element of the array.
 *
 * Caveat: Complex sections cannot have a default value, they must be fixed
 * up later, as they are given the default value of NULL.
 */
void *load_complex_block( FILE *fp, const struct top_data_type *block_desc )
{
    struct data_desc_type *load_table = block_desc->table;
    int error;
    int letter;
    char *word;
    int i, num_keys, last_key = 0;
    bool *specified;
    void *value;

    if( fread_letter( fp ) != '{' )
    {
	bug( "load_complex_block: no starting '{' character." );
	return NULL;
    }

    memset( block_desc->data_ptr, 0, block_desc->data_size );

    for( num_keys = 0; load_table[num_keys].name
	     && *load_table[num_keys].name; )
	num_keys++;

    specified = alloc_mem( num_keys * sizeof( bool ) );
    for( i = 0; i < num_keys; ++i )
	specified[i] = FALSE;

    while( !feof( fp ) )
    {
	letter = fread_letter( fp );
	if( letter == EOF )
	{
	    bug( "load_complex_block: EOF reached." );
	    free_mem( specified, num_keys * sizeof( bool ) );
	    return NULL;
	}
	if( letter == '}' )
	    break;
	if( letter == '#' )
	{
	    fread_to_eol( fp );
	    continue;
	}

	ungetc( letter, fp );
	word = fread_word( fp, &error );
	if( error )
	{
	    bug( "Error reading key." );
	    free_mem( specified, num_keys * sizeof( bool ) );
	    return NULL;
	}

	i = get_desc_entry( load_table, word, last_key );

	/*
	 * OHNO! not known to me! ... Error recovery mode.
	 */
	if( i < 0 )
	{
	    last_key = 0;
	    bug( "Unknown key encountered '%s'.", word );
	    if( !ignore_unknown_field( fp ) )
	    {
		free_mem( specified, num_keys * sizeof( bool ) );
		return NULL;
	    }
	    continue;
	}

	last_key = i;
	specified[i] = TRUE;

	/*
	 * Three types of reading: special, array, single.
	 */
	if( IS_SET( load_table[i].type, DFLAG_SPECREAD ) )
	{
	    if( IS_SET( load_table[i].type, DFLAG_NEEDPARENT ) )
		error = !(*load_table[i].rwf)( fp, block_desc->data_ptr,
					       DBACTION_READ );
	    else
		error = !(*load_table[i].rwf)( fp, load_table[i].target,
					       DBACTION_READ );
	    if( !error )
		error = !(*load_table[i].rwf)( fp, load_table[i].target,
					       DBACTION_FIX );
	}
	else if( IS_SET( load_table[i].type, DFLAG_ARRAY ) )
	    error = !load_array( fp, load_table + i );
	else
	    error = !load_single_field( fp, load_table + i );

	if( error )
	{
	    free_mem( specified, num_keys * sizeof( bool ) );
	    return NULL;
	}

	letter = fread_letter( fp );
	if( letter != ';' )
	{
	    if( letter == EOF )
		bug( "load_complex_block: EOF reached." );
	    else
		bug( "load_complex_block: missing ';' character." );
	    free_mem( specified, num_keys * sizeof( bool ) );
	    return NULL;
	}
    }

    /*
     * Assign default values and check for mandatory fields.
     */
    for( i = 0; i < num_keys; i++ )
    {
	if( specified[i] == TRUE )
	    continue;

	if( IS_SET( load_table[i].type, DFLAG_MAND ) )
	{
	    bug( "Problem in %s section, MAND field %s missing.",
		 block_desc->name, load_table[i].name );
	    free_mem( specified, num_keys * sizeof( bool ) );
	    return NULL;
	}
	switch( load_table[i].type & DTYPE_BASIC )
	{
	case DTYPE_STRING:
	    *(char **)load_table[i].target
		= str_dup( load_table[i].deflt.string );
	    break;
	case DTYPE_NUMBER:
	case DTYPE_TIME:
	    *(int *)load_table[i].target = load_table[i].deflt.number;
	    break;
	case DTYPE_VECTOR:
	    vcopy( (int *)load_table[i].target, load_table[i].deflt.vector );
	    break;
	case DTYPE_COMPLEX:
	case DTYPE_LLIST:
	    *(void **)load_table[i].target = NULL;
	    break;
	default:
	    break;
	}
    }
    free_mem( specified, num_keys * sizeof( bool ) );

    value = alloc_perm( block_desc->data_size );
    memcpy( value, block_desc->data_ptr, block_desc->data_size );

    if( block_desc->rwf
	&& !(*block_desc->rwf)( NULL, value, DBACTION_FIX ) )
    {
	dalloc_last( block_desc->data_size );
	return NULL;
    }

    return value;
}


bool load_single_field( FILE *fp, const struct data_desc_type *entry )
{
    int error, letter;
    void **ptr;

    if( !entry || !entry->target )
    {
	bug( "Void target for %s.", entry->name );
	return FALSE;
    }

    error = 1;
    switch( entry->type & DTYPE_BASIC )
    {
    case DTYPE_STRING:
	*(char **)entry->target = fread_string( fp, &error );
	break;
    case DTYPE_NUMBER:
    case DTYPE_VECTOR:
	error = !fread_number_ii( fp, (int *)entry->target, entry->flag_table );
	break;
    case DTYPE_COMPLEX:
	ptr = (void **)entry->target;
	*ptr = load_complex_block( fp, entry->deflt.table );
	if( *ptr )
	    error = 0;
	break;
    case DTYPE_LLIST:
	ptr = (void **)entry->target;
	while( *ptr )
	    ptr = &NEXT( *ptr );

	/* Small extension to the syntax:
	 * Allow for linked lists to be chained together.
	 *
	 * LLName {
	 *  Key "value";
	 * }{
	 *  Key "value";
	 * };
	 */
	do
	{
	    *ptr = load_complex_block( fp, entry->deflt.table );

	    if( !*ptr )
	    {
		bug( "Error reading key %s.", entry->name );
		return FALSE;
	    }
	    letter = fread_letter( fp );
	    ungetc( letter, fp );
	    ptr = &NEXT( *ptr );
	    error = 0;
	}
	while( letter == '{' );
	break;
    case DTYPE_TIME:
	*(time_t *)entry->target = fread_time( fp, &error );
	break;
    }
    if( error )
    {
	bug( "Error reading key %s.", entry->name );
	return FALSE;
    }
    if( entry->rwf
	&& !(*entry->rwf)( NULL, entry->target, DBACTION_FIX ) )
	return FALSE;
    return TRUE;
}


/*
 * This function reads an array from a file.
 * This array should be of the format:
 *
 *	\[ [#<size>] [@<number>] <value>, [@<number>] <value> \]
 *
 * e.g.
 *	[ #12 "value[0]", @2 "value[2]", "value[3]", @10 "value[10]" ]
 *
 * The number following the optional @ symbol signifies the index that
 * the data should be entered at.
 *
 * The size of an array cannot be known, except if it is given in the
 * deflt.number field.  If this is given default values can be assigned.
 */
bool load_array( FILE *fp, const struct data_desc_type *entry )
{
    int letter, error = 0, index = 0;
    struct data_desc_type ddesc;
    bool *specified = NULL;
    int i, num_keys;

    if( fread_letter( fp ) != '[' )
    {
	bug( "Expecting an array for %s.", entry->name );
	return FALSE;
    }

    memcpy( &ddesc, entry, sizeof( ddesc ) );

    num_keys = entry->type >> DSHIFT_ARRAY;
    if( !IS_SET( entry->type, DFLAG_MAND )
	&& num_keys > 0 )
    {
	specified = alloc_mem( num_keys * sizeof( bool ) );
	for( i = 0; i < num_keys; ++i )
	    specified[i] = FALSE;
    }
    ddesc.type &= ~( DFLAG_ARRAY | ( num_keys << DSHIFT_ARRAY ) );

    do
    {
	letter = fread_letter( fp );
	if( letter == EOF )
	{
	    bug( "load_array: EOF encountered." );
	    if( specified )
		free_mem( specified, num_keys * sizeof( bool ) );
	    return FALSE;
	}
	else if( letter == ']' )
	    break;
	else if( letter == '@' )
	    error = !fread_number_ii( fp, &index, entry->flag_table );
	else
	    ungetc( letter, fp );
	switch( entry->type & DTYPE_BASIC )
	{
	case DTYPE_STRING:
	    ddesc.target = (void *)( ((char **)entry->target) + index );
	    break;
	case DTYPE_NUMBER:
	case DTYPE_VECTOR:
	    ddesc.target = (void *)( ((int *)entry->target) + index );
	    break;
	case DTYPE_COMPLEX:
	case DTYPE_LLIST:
	    ddesc.target = (void *)( ((void **)entry->target) + index );
	    break;
	case DTYPE_TIME:
	    ddesc.target = (void *)( ((time_t *)entry->target) + index );
	    break;
	}

	if( error || !load_single_field( fp, &ddesc ) )
	{
	    if( specified )
		free_mem( specified, num_keys * sizeof( bool ) );
	    return FALSE;
	}
	specified[index] = TRUE;

	letter = fread_letter( fp );
	index++;
    }
    while( letter == ',' );


    /*
     * If we are looking for default values, set those that should be default
     * to the proper value.
     */
    if( specified )
    {
	for( i = 0; i < num_keys; ++i )
	{
	    if( specified[i] == TRUE )
		continue;

	    switch( entry->type & DTYPE_BASIC )
	    {
	    case DTYPE_STRING:
		*( (char **)entry->target + i ) = str_dup( entry->deflt.string );
		break;
	    case DTYPE_NUMBER:
	    case DTYPE_TIME:
		*( (int *)entry->target + i ) = entry->deflt.number;
		break;
	    case DTYPE_VECTOR:
		vcopy( (int *)entry->target + i, entry->deflt.vector );
		break;
	    case DTYPE_COMPLEX:
	    case DTYPE_LLIST:
		*( (void **)entry->target + i ) = NULL;
		break;
	    default:
		break;
	    }
	}
	free_mem( specified, num_keys * sizeof( bool ) );
    }

    if( letter != ']' )
    {
	bug( "Expecting an end of array for %s.", entry->name );
	return FALSE;
    }
    return TRUE;
}


/*
 * The problem with human readable formats is that they often don't seem to
 * be all that readable by a computer.  This error recovery function is made
 * complex by the fact that the format is extremely sensitive to syntax
 * errors.  Luckily OLC saves us from this problem occuring too often.
 */
bool ignore_unknown_field( FILE *fp )
{
    int letter, error = 0;

    do
    {
	letter = fread_letter( fp );
	if( letter == EOF )
	    return FALSE;
	else if( letter == '#' )
	    fread_to_eol( fp );
    }
    while( letter == '#' );

    if( isdigit( letter ) || letter == '(' )
    {
	ungetc( letter, fp );
	error = !fread_number_ii( fp, &letter, "skill" );
    }
    else if( letter == '"' )
    {
	ungetc( letter, fp );
	free_string( fread_string( fp, &error ) );
    }
    else if( letter == '[' )
    {
	do
	{
	    letter = fread_letter( fp );
	    if( letter == EOF )
		return FALSE;
	    else if( letter == '@' )
	    {
		error = !fread_number_ii( fp, &letter, "skill" );
		letter = fread_letter( fp );
	    }
	    ungetc( letter, fp );

	    if( isdigit( letter ) || letter == '(' )
		error = !fread_number_ii( fp, &letter, "skill" );
	    else if( letter == '"' )
		free_string( fread_string( fp, &error ) );

	    letter = fread_letter( fp );
	}
	while( letter == ',' );
    }
    else if( letter == '{' )
    {
	do	/* ignore multiple key, value pairs. */
	{
	    do		/* read the key */
	    {
		letter = fread_letter( fp );
		if( letter == EOF )
		    return FALSE;
		else if( letter == '#' )
		    fread_to_eol( fp );
		else if( letter != '}' )
		{
		    ungetc( letter, fp );
		    fread_word( fp, &error );
		}
	    }
	    while( letter == '#' );
	    if( letter != '}' )
		ignore_unknown_field( fp );
	}
	while( letter != '}' );
    }
    else if( letter == '}' )
    {
	bug( "'}' found, assuming that was just junk." );
	ungetc( letter, fp );
	return TRUE;
    }
    /* else - we are stuffed anyway */

    if( error || fread_letter( fp ) != ';' )
    {
	bug( "Unrecoverable error, no semicolon." );
	return FALSE;
    }
    return TRUE;
}


/*
 * Read a number from a file.
 * Now also support hex numbers in the 0xabc123.
 */
int fread_number( FILE *fp, int *status )
{
    int c;
    bool sign = FALSE, hex = FALSE;
    int number = 0;

    *status = 0;
    do
    {
	if( feof( fp ) )
	{
	    *status = 1;
	    bug( "fread_number: EOF encountered on read." );
	    if( fBootDb )
		exit( 1 );
	    return NO_FLAG;
	}
	c = getc( fp );
    }
    while( isspace( c ) );

    if( c == '+' )
    {
	c = getc( fp );
    }
    else if( c == '-' )
    {
	sign = TRUE;
	c = getc( fp );
    }

    if( !isdigit( c ) )
    {
	*status = 1;
	bug( "Fread_number: bad format." );
	bug( "	 If bad object, check for missing '~' in value[] fields." );
	return NO_FLAG;
    }

    if( c == '0' )
    {
	c = getc( fp );
	if( c == 'x' || c == 'X' )
	    hex = TRUE;
	else if( isdigit( c ) )
	    ungetc( c, fp );
    }

    while( isdigit( c ) || ( hex && isxdigit( c ) ) )
    {
	if( hex )
	{
	    if( isdigit( c ) )
		number = number * 16 + c - '0';
	    else
		number = number * 16 + tolower( c ) - 'a' + 10;
	}
	else
	    number = number * 10 + c - '0';
	c = getc( fp );
    }
    if( c == EOF )
    {
	*status = 1;
	bug( "fread_number: EOF encountered on read." );
	if( fBootDb )
	    exit( 1 );
	return NO_FLAG;
    }

    if( sign )
	number = 0 - number;

    if( c == '|' )
	number += fread_number( fp, status );
    else if( c != ' ' )
	ungetc( c, fp );

    return number;
}


/*
 * Read a time (unsigned long).
 */
time_t fread_time( FILE *fp, int *status )
{
    int c;
    unsigned long number = 0;

    *status = 0;
    do
    {
	if( feof( fp ) )
	{
	    *status = 1;
	    bug( "fread_number: EOF encountered on read." );
	    if( fBootDb )
		exit( 1 );
	    return NO_FLAG;
	}
	c = getc( fp );
    }
    while( isspace( c ) );

    if( !isdigit( c ) )
    {
	*status = 1;
	bug( "Fread_time: bad format." );
	bug( "	 If bad object, check for missing '~' in value[] fields." );
	return NO_FLAG;
    }

    while( isdigit( c ) )
    {
	number = number * 10 + c - '0';
	c = getc( fp );
    }
    if( c == EOF )
    {
	*status = 1;
	bug( "fread_time: EOF encountered on read." );
	if( fBootDb )
	    exit( 1 );
	return NO_FLAG;
    }

    if( c != ' ' )
	ungetc( c, fp );

    return number;
}


bool fread_mapped_value( FILE *fp, int *value, const char *def_table )
{
    int c;
    int i;
    char buf[MAX_STRING_LENGTH];

    i = 0;
    c = fread_letter( fp );	/* also skips leading whitespace */
    if( c == EOF )
    {
	bug( "fread_mapped_value: EOF encountered on read." );
	return FALSE;
    }
    if( c == ')' )		/* empty parentheses "()" */
    {
	*value = 0;
	return TRUE;
    }

    while( c != ')' )
    {
	buf[i++] = c;
	c = getc( fp );
	if( c == EOF )
	{
	    bug( "fread_mapped_value: EOF encountered on read." );
	    return FALSE;
	}
    }
    buf[i] = '\0';


    if( buf[0] == '<' )
    {
	const char *p;
	char table[MAX_INPUT_LENGTH];

	p = one_argument( buf, table );
	get_lookup( value, table, p );
    }
    else if( def_table )
	get_lookup( value, def_table, buf );
    else
    {
	bug( "fread_mapped_value: No table specified." );
	return FALSE;
    }

    return TRUE;
}


/*
 * Read a number from a file.
 */
bool fread_number_ii( FILE *fp, int *value, const char *def_table )
{
    int c;
    int stat;

    c = fread_letter( fp );

    if( c != '(' )
    {
	ungetc( c, fp );
	*value = fread_number( fp, &stat );
	return !stat;
    }
    return fread_mapped_value( fp, value, def_table );
}


/***************************************************************************
 * Saving functions.
 */

bool write_next_item( FILE *fp, const char *name, const void *value )
{
    int i;

    i = get_top_entry( name );

    if( global_data_table[i].name == NULL )
    {
	bug( "Read unknown section header: %s", name );
	return FALSE;
    }

    fprintf( fp, "%s ", global_data_table[i].name );
    if( !save_complex_block( fp, global_data_table + i, value ) )
	return FALSE;
    fprintf( fp, ";\n" );

    return TRUE;
}


bool save_complex_block( FILE *fp, const struct top_data_type *block_desc, const void *value )
{
    struct data_desc_type *load_table = block_desc->table;
    int i;
    void *ptr;

    memcpy( block_desc->data_ptr, value, block_desc->data_size );
    fprintf( fp, "{\n" );

    for( i = 0; load_table[i].name && *load_table[i].name; ++i )
    {
	if( is_default( block_desc, load_table + i ) )
	    continue;

	/*
	 * Print the header if it's not a linked list, as sometime linked
	 * lists can be determined to be empty through other means than the
	 * default check and then they wont be displayed.
	 */
	if( ( load_table[i].type & DTYPE_BASIC ) != DTYPE_LLIST )
	    fprintf( fp, "%s\t", load_table[i].name );

	if( IS_SET( load_table[i].type, DFLAG_SPECWRITE )
	    || IS_SET( load_table[i].type, DFLAG_ARRAY ) )
	{
	    if( IS_SET( load_table[i].type, DFLAG_NEEDPARENT ) )
	    {
		if( !(*load_table[i].rwf)( fp, block_desc->data_ptr,
					   DBACTION_WRITE ) )
		    return FALSE;
	    }
	    else
	    {
		if( !(*load_table[i].rwf)( fp, load_table[i].target,
					   DBACTION_WRITE ) )
		    return FALSE;
	    }
	}
	else switch( load_table[i].type & DTYPE_BASIC )
	{
	case DTYPE_STRING:
	    fwrite_quoted_string( fp, *(char**)load_table[i].target );
	    break;

	case DTYPE_NUMBER:
	    if( load_table[i].flag_table )
		fwrite_mapped_value( fp, (int *)load_table[i].target,
				     load_table[i].flag_table, FALSE );
	    else
		fwrite_number( fp, *(int *)load_table[i].target );
	    break;

	case DTYPE_VECTOR:
	    fwrite_mapped_value( fp, (int *)load_table[i].target,
				 load_table[i].flag_table, FALSE );
	    break;

	case DTYPE_COMPLEX:
	    if( !save_complex_block( fp, load_table[i].deflt.table,
				     *(void **)load_table[i].target ) )
		return FALSE;
	    break;

	case DTYPE_LLIST:
	    ptr = *(void **)load_table[i].target;
	    for( ; ptr; ptr = NEXT( ptr ) )
	    {
		if( (*load_table[i].deflt.table->rwf)( fp, ptr, DBACTION_CHKDEFAULT ) )
		    continue;

		fprintf( fp, "%s\t", load_table[i].name );
		if( !save_complex_block( fp, load_table[i].deflt.table, ptr ) )
		    return FALSE;
		fprintf( fp, ";\n" );
	    }
	    break;
	case DTYPE_TIME:
	    fwrite_time( fp, *(time_t *)load_table[i].target );
	    break;
	}
	if( ( load_table[i].type & DTYPE_BASIC ) != DTYPE_LLIST )
	    fprintf( fp, ";\n" );
    }

    fputc( '}', fp );
    return TRUE;
}


bool is_default( const struct top_data_type *block,
		 const struct data_desc_type *entry )
{
    if( IS_SET( entry->type, DFLAG_MAND ) )
	return FALSE;

    if( IS_SET( entry->type, DFLAG_CHKDEFAULT ) )
    {
	if( IS_SET( entry->type, DFLAG_NEEDPARENT ) )
	    return (*entry->rwf)( NULL, block->data_ptr, DBACTION_CHKDEFAULT );
	else
	    return (*entry->rwf)( NULL, entry->target, DBACTION_CHKDEFAULT );
    }

    if( IS_SET( entry->type, DFLAG_ARRAY ) )
	return FALSE;

    switch( entry->type & DTYPE_BASIC )
    {
    case DTYPE_STRING:
	if( !entry->deflt.string )
	    return ( *(char **)entry->target == '\0' ) ? TRUE : FALSE;

	return !strcmp( *(char **)entry->target, entry->deflt.string )
	    ? TRUE : FALSE;

    case DTYPE_NUMBER:
    case DTYPE_TIME:
	return ( *(int *)entry->target == entry->deflt.number )
	    ? TRUE : FALSE;

    case DTYPE_VECTOR:
	return vequal( (int *)entry->target, entry->deflt.vector );

    case DTYPE_COMPLEX:
    case DTYPE_LLIST:
	return ( (*(void **)entry->target) == NULL ) ? TRUE : FALSE;

    default:
	break;
    }
    return FALSE;
}


void fwrite_number( FILE *fp, int value )
{
    fprintf( fp, "%d", value );
}


void fwrite_time( FILE *fp, time_t value )
{
    fprintf( fp, "%lu", (unsigned long)value );
}


void fwrite_mapped_value( FILE *fp, int *value, const char *table, bool title )
{
    char buf[MAX_STRING_LENGTH];
    struct flag_type *flag_table;
    int i;
    bool vect = FALSE, stat = FALSE;

    fputc( '(', fp );
    if( title )
	fprintf( fp, "<%s> ", table );

    buf[0] = '\0';

    if( !str_cmp( table, "skill" ) )
    {
	if( *value > 0 && *value < MAX_SKILL )
	    strcpy( buf, skill_table[*value].name );
	else
	    strcpy( buf, "." );
    }
    else if( !str_cmp( table, "race" ) )
	strcpy( buf, race_table[*value].name );
    else if( !str_cmp( table, "liquid" ) )
	strcpy( buf, liq_table[*value].liq_name );
    else if( !str_cmp( table, "event" ) )
	strcpy( buf, event_table[*value].name );

    if( buf[0] != '\0' )
    {
	fprintf( fp, buf );
	fputc( ')', fp );
	return;
    }

    for( i = 0; bit_table[i].desc && bit_table[i].desc[0]; ++i )
	if( !str_prefix( table, bit_table[i].command ) )
	    break;
    if( !bit_table[i].desc || bit_table[i].desc[0] == '\0' )
    {
	fprintf( fp, "ERROR!!)" );
	return;
    }

    title = FALSE;
    flag_table = (struct flag_type *)bit_table[i].structure;

    if( is_vector( flag_table ) )
	vect = TRUE;
    else if( is_stat( flag_table ) )
	stat = TRUE;

    for( i = 0; *flag_table[i].name; i++ )
    {
	if( ( vect && xIS_SET( value, flag_table[i].bit ) )
	    || ( !vect && !stat
		 && IS_SET( *value, flag_table[i].bit ) )
	    || ( !vect && flag_table[i].bit == *value ) )
	{
	    if( title )
		fputc( ' ', fp );
	    else
		title = TRUE;

	    if( strchr( flag_table[i].name, ' ' ) )
	    {
		fputc( '"', fp );
		fprintf( fp, flag_table[i].name );
		fputc( '"', fp );
	    }
	    else
		fprintf( fp, flag_table[i].name );

	    if( stat )
		break;
	}
    }

    fputc( ')', fp );
    return;
}


void fwrite_quoted_string( FILE *fp, const char *str )
{
    fputc( '"', fp );

    while( *str )
    {
	switch( *str )
	{
	case '\n':
	    fputc( '\\', fp );
	    fputc( 'n', fp );
	    break;
	case '\r':
	    if( *( str + 1 ) && *( str + 1 ) != '\n' )
		fputc( '\n', fp );
	    break;

	case '\t':
	    fputc( '\\', fp );
	    fputc( 't', fp );
	    break;

	case '"':
	    fputc( '\\', fp );
	    fputc( '"', fp );
	    break;

	case '\\':
	    fputc( '\\', fp );
	    fputc( '\\', fp );
	    break;

	default:
	    fputc( *str, fp );
	    break;
	}
	str++;
    }
    fputc( '"', fp );
}




/***************************************************************************
 * Special functions for reading, writing and fixing data.
 *
 * The third argument of this function determines if the data is to be read,
 * written or fixed (after reading).
 *
 * Quirk: For fixing of top level objects (e.g. plane/area) the vo pointer is
 * a single pointer to the object.  For values that are a part of a complex
 * object this is a pointer to the element in the object (for int: int*; for
 * char *: char **; etc...)
 *
 * Note: If data is to be fixed, you will not be able to use pointers to that
 * data as this is not the final value of the data, this function merely
 * allows for sanity checks and perhaps a few tweaks.
 *
 * Cases where data is valid:
 *  DBACTION_ADDWORLD, DBACTION_CHKDEFAULT, DBACTION_FIX and all minor
 *  members of the current object (unless they haven't been read yet).
 *
 * Array Note: This function is called for each member of the array, never
 * the entire array on its own, validation of the entire array as a whole
 * must be done by the enclosing object.
 *
 * CHKDEFAULT on the top level objects relates to linked lists only, but it
 * is best to return FALSE in all cases.  This is normally used to detect
 * 'deleted' flags.
 */

/*
 * This buffer is for convenience.
 */
char db_buf[MAX_STRING_LENGTH];


void fix_progtypes( MPROG_DATA *mprg, int *types )
{
    for( ; mprg; mprg = mprg->next )
    {
	if( mprg->type == GLOBAL_PROG )
	{
	    MPROG_GLOBAL *glob = get_global_prog( mprg );
	    if( glob )
		xSET_BIT( types, glob->type );
	    else
		bug( "WARNING: Global Mud Program doesn't exist yet: %s.",
		     mprg->arglist );
	}
	else
	    xSET_BIT( types, mprg->type );
    }
}


bool dbrwf_affect( FILE *fp, void *vo, db_action action )
{
    AFFECT_DATA *paf = (AFFECT_DATA *)vo;

    if( action == DBACTION_WRITE )
    {
	if( paf->deleted )
	    return TRUE;

	fprintf( fp, "{\n" );
	if( paf->type > 0 && paf->type < MAX_SKILL )
	{
	    fprintf( fp, "Name\t" );
	    fwrite_mapped_value( fp, &paf->type, "skill", FALSE );
	    fprintf( fp, ";\n" );
	}
	if( paf->duration >= 0 )
	    fprintf( fp, "Duration\t%d;\n", paf->duration );
	if( paf->level )
	    fprintf( fp, "Level\t%d;\n", paf->level );

	if( paf->type == gsn_continuous_effect
	    || paf->type == gsn_racial_fatigue
	    || paf->type == gsn_religious
	    || paf->type == gsn_delayed_effect
	    || paf->type == gsn_perm_spell )
	{
	    fprintf( fp, "Location\t" );
	    fwrite_mapped_value( fp, &paf->location, "skill", TRUE );
	    fprintf( fp, ";\n" );
	}
	else
	{
	    if( paf->location != APPLY_NONE )
	    {
		fprintf( fp, "Location\t" );
		fwrite_mapped_value( fp, &paf->location, "apply", FALSE );
		fprintf( fp, ";\n" );
	    }

	    if( paf->modifier )
	    {
		fprintf( fp, "Modifier\t" );

		if( paf->location == APPLY_BODY_PART )
		    fwrite_mapped_value( fp, &paf->modifier, "limbs", TRUE );
		else
		    fwrite_number( fp, paf->modifier );
		fprintf( fp, ";\n" );
	    }

	    if( !vnull( paf->bitvector ) )
	    {
		fprintf( fp, "Bits\t" );
		fwrite_mapped_value( fp, paf->bitvector, "affect", FALSE );
		fprintf( fp, ";\n" );
	    }
	}

	fprintf( fp, "}" );
    }
    else if( action == DBACTION_ADDWORLD )
    {
	bug( "Trying to add an affect to the world on its own." );
	return FALSE;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return !paf->deleted;

    return TRUE;
}


bool dbrwf_alias( FILE *fp, void *vo, db_action action )
{
    ALIAS_DATA *alias = (ALIAS_DATA *)vo;

    if( action == DBACTION_FIX )
    {
	char *p;
	const char directions[] = "neswud";

	if( strchr( alias->command, '%' ) )
	    alias->complex = ALIAS_COMPLEX;
	else if( strchr( alias->command, '=' ) )
	    alias->complex = ALIAS_MULTIPLE;
	else
	{
	    alias->complex = ALIAS_MULTIPLE;
	    for( p = alias->command; *p; p++ )
	    {
		if( !strchr( directions, LOWER( *p ) ) )
		{
		    alias->complex = ALIAS_SIMPLE;
		    break;
		}
	    }
	}
    }
    else if( action == DBACTION_ADDWORLD )
    {
	bug( "Trying to add an alias to the world on its own." );
	return FALSE;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_area( FILE *fp, void *vo, db_action action )
{
    AREA_DATA *pArea = (AREA_DATA *)vo;

    if( action == DBACTION_FIX )
    {
	pArea->vnum = top_area++;
	pArea->filename = str_dup( strArea + strlen( AREA_DIR ) );
	pArea->nplayer = 0;
	pArea->next = NULL;
	pArea->min = MAX_LEVEL;
	pArea->max = 0;
	if( !pArea->plane )
	    pArea->plane = plane_lookup( "unfinished" );
	if( pArea->order && pArea->order->clan_type != CLAN_ORDER )
	{
	    bug( "Religious order not found or not an order." );
	    pArea->order = NULL;
	}
    }
    else if( action == DBACTION_ADDWORLD )
    {
	if( !area_first )
	    area_first = pArea;
	if( area_last )
	{
	    area_last->next = pArea;
	    REMOVE_BIT( area_last->area_flags, AREA_LOADING );
	}
	area_last = pArea;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return IS_SET( pArea->area_flags, AREA_DELETED ) ? TRUE : FALSE;

    return TRUE;
}


bool dbrwf_char( FILE *fp, void *vo, db_action action )
{
    CHAR_DATA *ch = (CHAR_DATA *)vo;

    if( action == DBACTION_FIX )
    {
	ROOM_INDEX_DATA *in_room;
	AFFECT_DATA *paf;
	OBJ_DATA *obj;
	int i;

	ch->logon = current_time;
	ch->unique_key = SysInfo->char_key++;
	if( SysInfo->char_key >= (1<<30) )
	    SysInfo->char_key = 1;

	if( !ch->body_parts )
	    ch->body_parts = race_table[ch->race].body_parts;

	clean_char( ch );
	in_room = ch->in_room;
	ch->in_room = get_room_index( ROOM_VNUM_LIMBO );
	vzero( ch->affected_by );
	for( paf = ch->affected; paf; paf = paf->next )
	{
	    if( paf->deleted )
		continue;
	    affect_modify( ch, paf, TRUE );
	}
	for( obj = ch->carrying; obj; obj = obj->next_content )
	{
	    obj->carried_by = ch;
	    if( obj->deleted || obj->wear_loc == WEAR_NONE )
		continue;
	    i = obj->wear_loc;
	    obj->wear_loc = WEAR_NONE;
	    equip_char( ch, obj, i );
	}
	inject_events( ch );
	/* Update the character
	switch( SysInfo->file_version - file_version )
	{
	case 1:
	   fix up.
	case 0:
	} */
	ch->in_room = in_room;
    }
    else if( action == DBACTION_ADDWORLD )
    {
	char_to_room( ch, ch->in_room );
    }
    else if( action == DBACTION_CHKDEFAULT )
	return !ch->deleted;

    return TRUE;
}


bool dbrwf_clan( FILE *fp, void *vo, db_action action )
{
    CLAN_DATA *pClan = (CLAN_DATA *)vo;

    if( action == DBACTION_ADDWORLD )
    {
	top_clan++;

	if( !clan_first )
	    clan_first = pClan;
	if( clan_last )
	    clan_last->next = pClan;
	clan_last = pClan;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_event( FILE *fp, void *vo, db_action action )
{
    EVENT *e = (EVENT *)vo;

    if( action == DBACTION_FIX )
    {
	e->flags |= EVENT_LOADING;
	top_event++;
    }
    else if( action == DBACTION_ADDWORLD )
    {
	bug( "Trying to add an extra description to the world on its own." );
	return FALSE;
    }
    else if( action == DBACTION_CHKDEFAULT )
    {
	if( e->type < 0
	    || IS_SET( e->flags ^ event_table[e->type].flags,
		       EVENT_TEMPORARY )
	    || IS_SET( e->flags ^ event_table[e->type].flags,
		       EVENT_NO_QUIT ) )
	    return TRUE;

	/* A broken event, forget it. */
	if( !IS_SET( e->flags, EVENT_LOADING ) && e->when < SysInfo->pulse )
	    return TRUE;
	return FALSE;
    }

    return TRUE;
}


bool dbrwf_exdesc( FILE *fp, void *vo, db_action action )
{
/*    EXTRA_DESCR_DATA *exdesc = (EXTRA_DESCR_DATA *)vo; */

    if( action == DBACTION_FIX )
    {
	top_ed++;
    }
    else if( action == DBACTION_ADDWORLD )
    {
	bug( "Trying to add an extra description to the world on its own." );
	return FALSE;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_exit( FILE *fp, void *vo, db_action action )
{
    EXIT_DATA *exit = (EXIT_DATA *)vo;

    if( action == DBACTION_FIX )
    {
	top_exit++;

	exit->exit_info = exit->rs_flags;
    }
    else if( action == DBACTION_ADDWORLD )
    {
	bug( "Trying to add an exit to the world on its own." );
	return FALSE;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_help( FILE *fp, void *vo, db_action action )
{
    HELP_DATA *pHelp = (HELP_DATA *)vo;

    if( action == DBACTION_FIX )
    {
	top_help++;

	if( area_last )
	    pHelp->area = area_last;
	else
	    pHelp->area = NULL;

	if( !help_greeting )
	    help_greeting = pHelp->text;
    }
    else if( action == DBACTION_ADDWORLD )
    {
	if( !help_first )
	    help_first = pHelp;
	if( help_last )
	    help_last->next = pHelp;
	help_last = pHelp;
	pHelp->next = NULL;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_high( FILE *fp, void *vo, db_action action )
{
    HIGHEST_DATA *pHigh = (HIGHEST_DATA *)vo;

    if( action == DBACTION_ADDWORLD )
    {
	if( !highest_first )
	    highest_first = pHigh;
	if( highest_last )
	    highest_last->next = pHigh;
	highest_last = pHigh;
	pHigh->next = NULL;
    }

    return TRUE;
}


bool dbrwf_highent( FILE *fp, void *vo, db_action action )
{
    if( action == DBACTION_ADDWORLD )
    {
	bug( "Trying to add a single entry of the highest table to the world." );
	return FALSE;
    }

    return TRUE;
}


bool dbrwf_gprog( FILE *fp, void *vo, db_action action )
{
    MPROG_GLOBAL *glob = (MPROG_GLOBAL *)vo;

    if( action == DBACTION_FIX )
    {
	top_mprog_global++;

	glob->area = area_last;
    }
    else if( action == DBACTION_ADDWORLD )
    {
	int iHash;

	if( get_global_mudprog_index( glob->vnum ) )
	{
	    bug( "dbrwf_gprog(FIX): vnum %d duplicated.", glob->vnum );
	    return FALSE;
	}

	iHash = glob->vnum % MPROG_GLOBAL_HASH;
	glob->next = global_progs[iHash];
	global_progs[iHash] = glob;
	assign_area_vnum( glob->vnum );
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_mob_index( FILE *fp, void *vo, db_action action )
{
    MOB_INDEX_DATA *pMob = (MOB_INDEX_DATA *)vo;

    if( action == DBACTION_FIX )
    {
	top_mob_index++;

	xSET_BIT( pMob->act, ACT_IS_NPC );
	pMob->area = area_last;
	pMob->long_descr[0] = UPPER( pMob->long_descr[0] );
	pMob->description[0] = UPPER( pMob->description[0] );
	if( pMob->body_parts == -1 )
	    pMob->body_parts = race_table[pMob->race].body_parts;
	if( pMob->race < 0 )
	    pMob->race = 0;
	assign_area_vnum( pMob->vnum );
	area_last->min = UMIN( area_last->min, pMob->level );
	area_last->max = UMAX( area_last->max, pMob->level );

	fix_progtypes( pMob->mudprogs, pMob->progtypes );
    }
    else if( action == DBACTION_ADDWORLD )
    {
	int iHash;

	if( get_mob_index( pMob->vnum ) )
	{
	    bug( "dbrwf_mob_index(FIX): vnum %d duplicated.", pMob->vnum );
	    return FALSE;
	}

	iHash = pMob->vnum % MAX_KEY_HASH;
	pMob->next = mob_index_hash[iHash];
	mob_index_hash[iHash] = pMob;
	top_vnum_mob = UMAX( top_vnum_mob, pMob->vnum );
	kill_table[URANGE( 0, pMob->level, LEVEL_HERO * 2 - 1 )].number++;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_mudprog( FILE *fp, void *vo, db_action action )
{
/*    MPROG_DATA *mprg = (MPROG_DATA *)vo; */

    if( action == DBACTION_FIX )
    {
	top_mprog++;
    }
    else if( action == DBACTION_ADDWORLD )
    {
	bug( "Trying to add a mudprog to the world on its own." );
	return FALSE;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_note( FILE *fp, void *vo, db_action action )
{
/*    NOTE_DATA *pNote = (NOTE_DATA *)vo; */

    if( action == DBACTION_ADDWORLD )
    {
	bug( "Notes shouldn't be added to the world in this way." );
	return FALSE;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_obj( FILE *fp, void *vo, db_action action )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;

    if( action == DBACTION_FIX )
    {
	EVENT *e;
	OBJ_DATA *obj2;

	obj->magic = MAGIC_NUM_OBJECT;

	/* Breaking the rules a little.  ADDWORLD should only be used by the
	   top level object. */
	obj->pIndexData->count++;
	obj->next = object_list;
	object_list = obj;

	for( e = obj->events; e; e = e->next_local )
	{
	    e->actor.type = TARGET_OBJ;
	    e->actor.target.obj = obj;
	}

	/*
	 * All of these objects have been added to the static version of this
	 * object.  So they have to be fixed to point to this.
	 */
	for( obj2 = obj->contains; obj2; obj2 = obj2->next_content )
	    obj2->in_obj = obj;
    }
    else if( action == DBACTION_ADDWORLD )
    {
	if( !obj->in_room )
	{
	    bug( "Trying to insert an object into a non-existant room." );
	    return FALSE;
	}
	obj_to_room( obj, obj->in_room );
	strip_events( &obj->events, evn_imp_grab );
    }
    else if( action == DBACTION_CHKDEFAULT )
    {
	OBJ_DATA *in_obj;

	for( in_obj = obj; in_obj->in_obj; in_obj = in_obj->in_obj )
	    ;
	if( in_obj && in_obj->carried_by
	    && get_trust( in_obj->carried_by ) < obj->level - 5 )
	    return TRUE;

	return ( obj->item_type == ITEM_KEY
		 || obj->deleted ) ? TRUE : FALSE;
    }

    return TRUE;
}


bool dbrwf_obj_index( FILE *fp, void *vo, db_action action )
{
    OBJ_INDEX_DATA *pObj = (OBJ_INDEX_DATA *)vo;

    if( action == DBACTION_FIX )
    {
	top_obj_index++;

	pObj->area = area_last;
	pObj->short_descr[0] = LOWER( pObj->short_descr[0] );
	pObj->description[0] = UPPER( pObj->description[0] );

	fix_progtypes( pObj->mudprogs, pObj->progtypes );
    }
    else if( action == DBACTION_ADDWORLD )
    {
	int iHash;

	if( get_obj_index( pObj->vnum ) )
	{
	    bug( "dbrwf_obj_index(FIX): vnum %d duplicated.", pObj->vnum );
	    return FALSE;
	}

	iHash = pObj->vnum % MAX_KEY_HASH;
	pObj->next = obj_index_hash[iHash];
	obj_index_hash[iHash] = pObj;
	top_vnum_obj = UMAX( top_vnum_obj, pObj->vnum );
	assign_area_vnum( pObj->vnum );       /* OLC */
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_pcdata( FILE *fp, void *vo, db_action action )
{
/*    PC_DATA *pcdata = (PC_DATA *)vo; */

    if( action == DBACTION_ADDWORLD )
    {
	bug( "Adding pcdata to the world on its own." );
	return FALSE;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_pose( FILE *fp, void *vo, db_action action )
{
/*    POSE_DATA *pPose = (POSE_DATA *)vo; */

    if( action == DBACTION_ADDWORLD )
    {
	bug( "Poses shouldn't be added to the world in this way." );
	return FALSE;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_quest( FILE *fp, void *vo, db_action action )
{
    QUEST_DATA *pQuest = (QUEST_DATA *)vo;

    if( action == DBACTION_FIX )
    {
	pQuest->target = NULL;
	pQuest->type = QUEST_NONE;
    }
    else if( action == DBACTION_ADDWORLD )
    {
	bug( "Quests shouldn't be added to the world in this way." );
	return FALSE;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}

bool dbrwf_religion( FILE *fp, void *vo, db_action action )
{
    RELIGION_DATA *pReligion = (RELIGION_DATA *)vo;

    if( action == DBACTION_ADDWORLD )
    {
	top_religion++;

	if( !religion_first )
	    religion_first = pReligion;
	if( religion_last )
	    religion_last->next = pReligion;
	religion_last = pReligion;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_relskill( FILE *fp, void *vo, db_action action )
{
/*    RELIGION_SKILL *sk = (RELIGION_SKILL *)vo; */

    if( action == DBACTION_ADDWORLD )
    {
	bug( "Trying to add a religion skill to the world on its own." );
	return FALSE;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_room( FILE *fp, void *vo, db_action action )
{
    ROOM_INDEX_DATA *pRoom = (ROOM_INDEX_DATA *)vo;

    if( action == DBACTION_FIX )
    {
	top_room++;

	pRoom->area = area_last;
	pRoom->people = NULL;
	pRoom->contents = NULL;

	fix_progtypes( pRoom->mudprogs, pRoom->progtypes );
    }
    else if( action == DBACTION_ADDWORLD )
    {
	int iHash;

	if( get_room_index( pRoom->vnum ) )
	{
	    bug( "dbrwf_room(FIX): vnum %d duplicated.", pRoom->vnum );
	    return FALSE;
	}

	iHash = pRoom->vnum % MAX_KEY_HASH;
	pRoom->next = room_index_hash[iHash];
	room_index_hash[iHash] = pRoom;
	top_vnum_room = UMAX( top_vnum_room, pRoom->vnum );
	assign_area_vnum( pRoom->vnum );
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_plane( FILE *fp, void *vo, db_action action )
{
    PLANE_DATA *plane = (PLANE_DATA *)vo;

    if( action == DBACTION_FIX )
    {
	/* Generate the new time and weather for that plane. */
	plane->time.hour = number_range( 0, 23 );
	plane->time.day = number_range( 0, 34 );
	plane->time.month = number_range( 0, 16 );
	plane->time.year = number_bits( 30 );

	if( plane->time.hour < 5 )
	    plane->weather.sunlight = SUN_DARK;
	else if( plane->time.hour < 6 )
	    plane->weather.sunlight = SUN_RISE;
	else if( plane->time.hour < 19 )
	    plane->weather.sunlight = SUN_LIGHT;
	else if( plane->time.hour < 20 )
	    plane->weather.sunlight = SUN_SET;
	else
	    plane->weather.sunlight = SUN_DARK;

	plane->weather.change = 0;
	plane->weather.mmhg = 960;
	if( plane->time.month >= 7 && plane->time.month <= 12 )
	    plane->weather.mmhg += number_range( 1, 50 );
	else
	    plane->weather.mmhg += number_range( 1, 80 );

	if( plane->weather.mmhg <= 980 )
	    plane->weather.sky = SKY_LIGHTNING;
	else if( plane->weather.mmhg <= 1000 )
	    plane->weather.sky = SKY_RAINING;
	else if( plane->weather.mmhg <= 1020 )
	    plane->weather.sky = SKY_CLOUDY;
	else
	    plane->weather.sky = SKY_CLOUDLESS;
    }
    else if( action == DBACTION_ADDWORLD )
    {
	plane->next = plane_list;
	plane_list = plane;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_shop( FILE *fp, void *vo, db_action action )
{
    SHOP_DATA *pShop = (SHOP_DATA *)vo;

    if( action == DBACTION_FIX )
    {
	pShop->profit_buy = UMAX( 100, pShop->profit_buy );
	pShop->profit_sell = UMIN( 100, pShop->profit_sell );
	if( !shop_first )
	    shop_first = pShop;
	if( shop_last )
	    shop_last->next = pShop;

	shop_last = pShop;
	pShop->next = NULL;
	top_shop++;
    }
    else if( action == DBACTION_ADDWORLD )
    {
	bug( "Trying to add a shop to the world on its own." );
	return FALSE;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_social( FILE *fp, void *vo, db_action action )
{
    SOCIAL_DATA *pSocial = (SOCIAL_DATA *)vo;

    if( action == DBACTION_ADDWORLD )
    {
	add_social( pSocial );
	top_social++;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_sysinfo( FILE *fp, void *vo, db_action action )
{
    struct sysinfo_type *si = (struct sysinfo_type *)vo;

    if( action == DBACTION_FIX )
    {
	/* fix autoreboot/shutdown times */
	si->down_time = (time_t)( si->levels * 3600 ) + (time_t)( si->deaths * 60 );
	if( si->down_time > 0 )
	{
	    if( si->down_time <= 300 )
		si->down_time += (time_t)300;
	    si->down_time += current_time;
	}
	si->levels = 0;
	si->deaths = 0;
    }
    else if( action == DBACTION_ADDWORLD )
    {
	SysInfo = si;
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


/*********************
 * dbrwf functions for sub-sections.
 */

bool dbrwf_area_flags( FILE *fp, void *vo, db_action action )
{
    int *flags = (int *)vo;

    if( action == DBACTION_WRITE )
    {
	int i = *flags & AREA_SAVE;

	fwrite_mapped_value( fp, &i, "area", FALSE );
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_area_plane( FILE *fp, void *vo, db_action action )
{
    PLANE_DATA **pPlane = (PLANE_DATA **)vo;

    if( action == DBACTION_READ )
    {
	temp_fread_string( fp, db_buf );
	*pPlane = plane_lookup( db_buf );
    }
    else if( action == DBACTION_WRITE )
    {
	fwrite_quoted_string( fp, (*pPlane)->name );
    }
    else if( action == DBACTION_CHKDEFAULT )
    {
	return !str_cmp( "unfinished", (*pPlane)->name );
    }

    return TRUE;
}


bool dbrwf_char_in_room( FILE *fp, void *vo, db_action action )
{
    CHAR_DATA *ch = (CHAR_DATA *)vo;

    if( action == DBACTION_READ )
    {
	int rnum, stat;

	rnum = fread_number( fp, &stat );
	ch->in_room = get_room_index( rnum );
	if( !ch->in_room )
	    ch->in_room = get_room_index( ROOM_VNUM_TEMPLE );
    }
    else if( action == DBACTION_WRITE )
    {
	fwrite_number( fp, ( ch->in_room->vnum == ROOM_VNUM_LIMBO
			     && ch->was_in_room )
		       ? ch->was_in_room->vnum : ch->in_room->vnum );
    }

    return TRUE;
}


bool dbrwf_char_max_hit( FILE *fp, void *vo, db_action action )
{
    CHAR_DATA *ch = (CHAR_DATA *)vo;

    if( action == DBACTION_WRITE )
    {
	fwrite_number( fp, get_max_hit( ch ) );
    }

    return TRUE;
}


bool dbrwf_char_max_mana( FILE *fp, void *vo, db_action action )
{
    CHAR_DATA *ch = (CHAR_DATA *)vo;

    if( action == DBACTION_WRITE )
    {
	bool found = FALSE;
	int i;

	fputc( '[', fp );

	for( i = 0; i < MAGIC_MAX; ++i )
	{
	    if( found )
		fprintf( fp, ", " );
	    else
		found = TRUE;

	    fwrite_number( fp, get_max_mana( ch, i ) );
	}

	fputc( ']', fp );
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_char_max_move( FILE *fp, void *vo, db_action action )
{
    CHAR_DATA *ch = (CHAR_DATA *)vo;

    if( action == DBACTION_WRITE )
    {
	fwrite_number( fp, get_max_move( ch ) );
    }

    return TRUE;
}


bool dbrwf_char_obj( FILE *fp, void *vo, db_action action )
{
    return dbrwf_all_obj( fp, (CHAR_DATA *)vo, NULL, action );
}


bool dbrwf_char_parts( FILE *fp, void *vo, db_action action )
{
    CHAR_DATA *ch = (CHAR_DATA *)vo;

    if( action == DBACTION_CHKDEFAULT )
    {
	return ( ch->body_parts == race_table[get_orig_race( ch )].body_parts )
	    ? TRUE : FALSE;
    }

    return TRUE;
}


bool dbrwf_char_played( FILE *fp, void *vo, db_action action )
{
    CHAR_DATA *ch = (CHAR_DATA *)vo;

    if( action == DBACTION_WRITE )
    {
	fwrite_time( fp, ch->played + current_time - ch->logon );
    }

    return TRUE;
}


bool dbrwf_char_position( FILE *fp, void *vo, db_action action )
{
    int *pos = (int *)vo;

    if( action == DBACTION_CHKDEFAULT )
	return ( *pos == POS_STANDING || *pos == POS_FIGHTING )
	    ? TRUE : FALSE;

    return TRUE;
}


bool dbrwf_char_race( FILE *fp, void *vo, db_action action )
{
    CHAR_DATA *ch = (CHAR_DATA *)vo;

    if( action == DBACTION_WRITE )
    {
	int race = get_orig_race( ch );

	fwrite_mapped_value( fp, &race, "race", FALSE );
    }

    return TRUE;
}


bool dbrwf_char_sex( FILE *fp, void *vo, db_action action )
{
    CHAR_DATA *ch = (CHAR_DATA *)vo;

    if( action == DBACTION_WRITE )
    {
	int sex = get_orig_sex( ch );

	fwrite_mapped_value( fp, &sex, "sex", FALSE );
    }

    return TRUE;
}

bool dbrwf_check_room( FILE *fp, void *vo, db_action action )
{
    int *rvnum = (int *)vo;

    if( action == DBACTION_FIX )
    {
	if( !get_room_index( *rvnum ) )
	{
	    bug( "Room vnum %d doesn't exist.", *rvnum );
	    *rvnum = -1;
	}
    }
    return TRUE;
}


bool dbrwf_event_data( FILE *fp, void *vo, db_action action )
{
    int *data = (int *)vo;

    if( action == DBACTION_WRITE )
    {
	bool found = FALSE;
	int i;

	fputc( '[', fp );

	for( i = 0; i < MAX_EVENT_DATA; ++i )
	{
	    if( found )
		fprintf( fp, ", " );
	    else
		found = TRUE;

	    fwrite_number( fp, data[i] );
	}

	fputc( ']', fp );
    }

    return TRUE;
}


bool dbrwf_event_when( FILE *fp, void *vo, db_action action )
{
    EVENT *e = (EVENT *)vo;

    if( action == DBACTION_WRITE )
    {
	fwrite_time( fp, IS_SET( e->flags, EVENT_LOADING )
		     ? e->when : ( e->when - SysInfo->pulse ) );
    }

    return TRUE;
}


bool dbrwf_high_entries( FILE *fp, void *vo, db_action action )
{
    struct highest_entry **he = (struct highest_entry **)vo;
    int i;

    if( action == DBACTION_WRITE )
    {
	bool found = FALSE;

	fputc( '[', fp );

	for( i = 0; i < 10; ++i )
	{
	    if( !he[i]  )
		break;
	    if( found )
		fprintf( fp, ", " );
	    else
		found = TRUE;

	    if( !save_complex_block( fp, global_data_table
				     + get_top_entry( "HighEnt" ), he[i] ) )
		return FALSE;
	}

	fputc( ']', fp );
    }

    return TRUE;
}


bool dbrwf_magic_mana( FILE *fp, void *vo, db_action action )
{
    int *magic = (int *)vo;

    if( action == DBACTION_WRITE )
    {
	bool found = FALSE;
	int i;

	fputc( '[', fp );

	for( i = 0; i < MAGIC_MAX; ++i )
	{
	    if( found )
		fprintf( fp, ", " );
	    else
		found = TRUE;

	    fwrite_number( fp, magic[i] );
	}

	fputc( ']', fp );
    }
    else if( action == DBACTION_CHKDEFAULT )
	return FALSE;

    return TRUE;
}


bool dbrwf_mob_parts( FILE *fp, void *vo, db_action action )
{
    MOB_INDEX_DATA *pMob = (MOB_INDEX_DATA *)vo;

    if( action == DBACTION_CHKDEFAULT )
    {
	return ( pMob->body_parts == race_table[pMob->race].body_parts )
	    ? TRUE : FALSE;
    }

    return TRUE;
}


bool dbrwf_mob_spec( FILE *fp, void *vo, db_action action )
{
    int *spec = (int *)vo;

    if( action == DBACTION_READ )
    {
	temp_fread_string( fp, db_buf );
	*spec = spec_lookup( db_buf );
    }
    else if( action == DBACTION_WRITE )
    {
	fwrite_quoted_string( fp, spec_table[*spec].spec_name );
    }

    return TRUE;
}


void fwrite_obj_values( FILE *fp, int type, int *value )
{
    fputc( '[', fp );
    switch( type )
    {
    default:
	fprintf( fp, "%d, %d, %d, %d",
		 value[0], value[1], value[2], value[3] );
	break;

    case ITEM_DRINK_CON:
    case ITEM_FOUNTAIN:
	fprintf( fp, "%d, %d, ", value[0], value[1] );
	fwrite_mapped_value( fp, &value[2], "liquid", TRUE );
	fprintf( fp, ", %d", value[3] );
	break;

    case ITEM_PLANT:
    case ITEM_PILL:
    case ITEM_POTION:
    case ITEM_SCROLL:
	fprintf( fp, "%d, ", value[0] );
	fwrite_mapped_value( fp, &value[1], "skill", FALSE );
	fprintf( fp, ", " );
	fwrite_mapped_value( fp, &value[2], "skill", FALSE );
	fprintf( fp, ", " );
	fwrite_mapped_value( fp, &value[3], "skill", FALSE );
	break;

    case ITEM_LIMB:
	fwrite_mapped_value( fp, &value[0], "limb", TRUE );
	fprintf( fp, ", 0, 0, %d", value[3] );
	break;

    case ITEM_STAFF:
    case ITEM_WAND:
	fprintf( fp, "%d, %d, %d, ",
		 value[0], value[1], value[2] );
	fwrite_mapped_value( fp, &value[3], "skill", FALSE );
	break;

    case ITEM_WEAPON:
	fwrite_mapped_value( fp, &value[0], "skill", FALSE );
	fprintf( fp, ", %d, %d, ", value[1], value[2] );
	fwrite_mapped_value( fp, &value[3], "weapon", TRUE );
	break;

    case ITEM_BOOK:
	fwrite_mapped_value( fp, &value[0], "skill", FALSE );
	fprintf( fp, ", %d, %d, %d",
		 value[1], value[2], value[3] );
	break;

    case ITEM_CORPSE_PC:
    case ITEM_CORPSE_NPC:
	fwrite_mapped_value( fp, &value[0], "race", TRUE );
	fprintf( fp, ", %d, %d, %d",
		 value[1], value[2], value[3] );
	break;

    case ITEM_GEM:
	fwrite_mapped_value( fp, &value[0], "magic", TRUE );
	fprintf( fp, ", %d, %d, %d",
		 value[1], value[2], value[3] );
	break;

    case ITEM_FURNITURE:
	fwrite_mapped_value( fp, &value[0], "furniture", TRUE );
	fprintf( fp, ", %d, %d, %d",
		 value[1], value[2], value[3] );
	break;
    }
    fputc( ']', fp );
}


/*
 * This is a strange situation.  Writing this data requires that the parent
 * object is known.  Thus the reading function is also affect, although we
 * shortcut straight to the default reading method anyway (through some
 * little hacks admittedly).
 */
bool dbrwf_obj_index_values( FILE *fp, void *vo, db_action action )
{
    OBJ_INDEX_DATA *pObj = (OBJ_INDEX_DATA *)vo;

    if( action == DBACTION_READ )
    {
	struct data_desc_type entry;
	memcpy( &entry, object_data_table + get_desc_entry( object_data_table, "Values", 0 ),
		sizeof( entry ) );
	entry.target = &pObj->value[0];

	return load_array( fp, &entry );
    }
    else if( action == DBACTION_WRITE )
    {
	fwrite_obj_values( fp, pObj->item_type, &pObj->value[0] );
    }

    return TRUE;
}


bool dbrwf_obj_obj( FILE *fp, void *vo, db_action action )
{
    return dbrwf_all_obj( fp, NULL, (OBJ_DATA *)vo, action );
}


bool dbrwf_obj_values( FILE *fp, void *vo, db_action action )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;

    if( action == DBACTION_READ )
    {
	struct data_desc_type entry;
	memcpy( &entry, obj_data_table + get_desc_entry( obj_data_table, "Values", 0 ),
		sizeof( entry ) );

	return load_array( fp, &entry );
    }
    else if( action == DBACTION_WRITE )
    {
	fwrite_obj_values( fp, obj->item_type, &obj->value[0] );
    }

    return TRUE;
}


bool dbrwf_obj_vnum( FILE *fp, void *vo, db_action action )
{
    OBJ_INDEX_DATA **pObj = (OBJ_INDEX_DATA **)vo;

    if( action == DBACTION_READ )
    {
	int vnum;

	if( !fread_number_ii( fp, &vnum, NULL ) )
	    return FALSE;

	*pObj = get_obj_index( vnum );
	if( !*pObj )
	{
	    bug( "Can't find object index for vnum %d.", vnum );
	    *pObj = get_obj_index( OBJ_VNUM_DUMMY );
	}
    }
    else if( action == DBACTION_WRITE )
    {
	fwrite_number( fp, (*pObj)->vnum );
    }

    return TRUE;
}


bool dbrwf_pcdata_board( FILE *fp, void *vo, db_action action )
{
    int *last_note = (int *)vo;
    int i;

    if( action == DBACTION_WRITE )
    {
	bool found = FALSE;

	fputc( '[', fp );
	for( i = 0; i < MAX_BOARD; ++i )
	{
	    if( found )
		fprintf( fp, ", " );
	    else
		found = TRUE;

	    fwrite_time( fp, last_note[i] );
	}
	fputc( ']', fp );
    }

    return TRUE;
}


bool dbrwf_pcdata_multi_class( FILE *fp, void *vo, db_action action )
{
    int *mc = (int *)vo;
    int i;

    if( action == DBACTION_WRITE )
    {
	bool found = FALSE;

	fputc( '[', fp );
	for( i = 0; i < NUM_MULTI_CLASS; ++i )
	{
	    if( mc[i] <= CLASS_UNKNOWN )
		continue;

	    if( found )
		fprintf( fp, ", " );
	    else
		found = TRUE;

	    fputc( '@', fp );
	    fwrite_mapped_value( fp, &i, "class", TRUE );

	    fprintf( fp, " %d", mc[i] );
	}
	fputc( ']', fp );
    }

    return TRUE;
}


bool dbrwf_pcdata_skills( FILE *fp, void *vo, db_action action )
{
    int *skill = (int *)vo;
    int i;

    if( action == DBACTION_WRITE )
    {
	bool found = FALSE;

	fputc( '[', fp );
	for( i = 0; i < MAX_SKILL; ++i )
	{
	    if( skill[i] <= 0 )
		continue;

	    if( found )
		fprintf( fp, ",\n" );
	    else
		found = TRUE;

	    fputc( '@', fp );
	    fwrite_mapped_value( fp, &i, "skill", TRUE );

	    fprintf( fp, " %d", skill[i] );
	}
	fputc( ']', fp );
    }

    return TRUE;
}


bool dbrwf_room_exits( FILE *fp, void *vo, db_action action )
{
    EXIT_DATA **exit = (EXIT_DATA **)vo;
    int i;

    if( action == DBACTION_WRITE )
    {
	bool found = FALSE;

	fprintf( fp, "[ " );
	for( i = 0; i < MAX_DIR; ++i )
	{
	    if( !exit[i] || !exit[i]->to_room )
		continue;

	    if( found )
		fputc( ',', fp );
	    else
		found = TRUE;

	    fputc( ' ', fp );

	    fputc( '@', fp );
	    fwrite_mapped_value( fp, &i, "dir", TRUE );
	    fputc( ' ', fp );

	    if( !save_complex_block( fp, global_data_table + get_top_entry( "Exit" ),
				     exit[i] ) )
		return FALSE;
	}
	fprintf( fp, " ]" );
    }
    else if( action == DBACTION_FIX )
    {
	for( i = 0; i < MAX_DIR; ++i )
	    if( exit[i] && exit[i]->key > 0
		&& !get_obj_index( exit[i]->key ) )
		bug( "Warning: door with key %d that doesn't exist.",
		     exit[i]->key );
    }
    else if( action == DBACTION_CHKDEFAULT )
    {
	for( i = 0; i < MAX_DIR; ++i )
	    if( exit[i] )
		return FALSE;
    }


    return TRUE;
}


bool dbrwf_room_resets( FILE *fp, void *vo, db_action action )
{
    RESET_DATA **first = (RESET_DATA **)vo;
    RESET_DATA **last = first + 1;	/* Ewww, hack */
    RESET_DATA *pReset;

    if( action == DBACTION_READ )
    {
	int letter, stat;

	letter = fread_letter( fp );
	if( letter != '{' )
	{
	    bug( "dbrwf_room_resets[READ]: no '{' found." );
	    return FALSE;
	}

	for( ;; )
	{
	    if( ( letter = fread_letter( fp ) ) == '}' )
		break;
	    if( letter == EOF )
	    {
		bug( "dbrwf_room_resets[READ]: eof reached." );
		return FALSE;
	    }
	    if( letter == '#' )
	    {
		fread_to_eol( fp );
		continue;
	    }

	    pReset = alloc_perm( sizeof( *pReset ) );
	    pReset->command = letter;
	    pReset->arg1 = fread_number( fp, &stat );
	    switch( letter )
	    {
	    case 'M':
	    case 'P':
		pReset->arg2 = fread_number( fp, &stat );
		break;

	    case 'R':
		if( pReset->arg1 == 0 )
		{
		    temp_fread_string( fp, db_buf );
		    if( str_cmp( db_buf, "none" ) )
			pReset->arg2 = flag_value( NULL, direction_flags, db_buf );
		    else
			pReset->arg2 = 0;
		}
		break;

	    case 'G':
	    case 'O':
		break;

	    case 'E':
		if( !fread_number_ii( fp, &pReset->arg2, "wear-loc" ) )
		    return FALSE;
		break;

	    default:
		bug( "Load_resets: bad command '%c'.", letter );
		return FALSE;
	    }
	    letter = fread_letter( fp );
	    if( letter != ';' )
		bug( "Loading resets without ';'" );

	    if( *last )
	    {
		(*last)->next = pReset;
		*last = pReset;
	    }
	    else
	    {
		*first = pReset;
		*last = pReset;
	    }
	    pReset->next = NULL;

	    top_reset++;
	}
    }
    else if( action == DBACTION_WRITE )
    {
	bool verbose = FALSE;

	if( IS_SET( SysInfo->flags, SYSINFO_VERBOSE_LOG ) )
	    verbose = TRUE;

	fprintf( fp, "{\n" );
	for( pReset = *first; pReset; pReset = pReset->next )
	{
	    switch( pReset->command )
	    {
	    case 'M':
	    case 'P':
	    default:
		if( verbose )
		{
		    if( pReset->command == 'M' )
			fprintf( fp, "# %s to room.\n",
				 kill_colour( db_buf, (get_mob_index( pReset->arg1 ))->short_descr ) );
		    else
			fprintf( fp, "# %s inside %s.\n",
				 kill_colour( db_buf, (get_obj_index( pReset->arg1 ))->short_descr ),
				 kill_colour( db_buf, (get_obj_index( pReset->arg2 ))->short_descr ) );
		}
		fprintf( fp, "  %c %d %d;\n", pReset->command,
			 pReset->arg1, pReset->arg2 );
		break;
	    case 'O':
	    case 'G':
		if( verbose )
		{
		    if( pReset->command == 'O' )
			fprintf( fp, "# %s to room.\n",
				 kill_colour( db_buf, (get_obj_index( pReset->arg1 ))->short_descr ) );
		    else
			fprintf( fp, "#\t%s to inventory.\n",
				 kill_colour( db_buf, (get_obj_index( pReset->arg1 ))->short_descr ) );
		}
		fprintf( fp, "  %c %d;\n", pReset->command, pReset->arg1 );
		break;

	    case 'E':
		if( verbose )
		    fprintf( fp, "#\tequip %s %s.\n",
			     kill_colour( db_buf, (get_obj_index( pReset->arg1 ))->short_descr ),
			     flag_string( wear_loc_flags, &pReset->arg2 ) );
		fprintf( fp, "  E %d (\"%s\");\n", pReset->arg1,
			 flag_string( wear_loc_flags, &pReset->arg2 ) );
		break;

	    case 'R':
		if( pReset->arg1 == 0 )
		{
		    if( verbose )
			fprintf( fp, "# Randomise exits %s.\n",
				 flag_string( direction_flags, &pReset->arg2 ) );
		    fprintf( fp, "  %c %d ", pReset->command, pReset->arg1 );
		    fwrite_quoted_string( fp, flag_string( direction_flags,
							   &pReset->arg2 ) );
		    fprintf( fp, ";\n" );
		}
		else
		{
		    if( verbose )
			fprintf( fp, "# Randomise %d exits.\n", pReset->arg1 );
		    fprintf( fp, "  %c %d;\n", pReset->command, pReset->arg1 );
		}
		break;

	    case 'D':
		break;
	    }
	}
	fputc( '}', fp );
    }
    else if( action == DBACTION_FIX )
    {
	MOB_INDEX_DATA *pMob = NULL;

	for( pReset = *first; pReset; pReset = pReset->next )
	{
	    switch( pReset->command )
	    {
	    case 'M':
		if( ( pMob = get_mob_index( pReset->arg1 ) ) )
		{
		    battle_min++;
		    battle_max += pMob->level;
		}
		else
		    bug( "Reset: Mob %d doesn't yet exist.\n\r",
			 pReset->arg1 );
		break;

	    case 'E':
		if( !pMob )
		    bug( "Reset: No mob to equip in 'E'." );
		else if( pReset->arg2 == NO_FLAG )
		    bug( "Reset: Bad equip location." );
		break;

		break;

	    case 'R':
		if( pReset->arg1 < 0 || pReset->arg1 > 6 )
		    bug( "Reset: Bad randomising of exits %d,%d.",
			 pReset->arg1, pReset->arg2 );
		break;
	    }
	}

    }

    return TRUE;
}


bool dbrwf_shop_trade( FILE *fp, void *vo, db_action action )
{
    int *trade = (int *)vo;
    int i;

    if( action == DBACTION_WRITE )
    {
	bool found = FALSE;

	fputc( '[', fp );

	for( i = 0; i < MAX_TRADE; ++i )
	{
	    if( trade[i] < 1 )
		continue;

	    if( found )
		fprintf( fp, ", " );
	    else
		found = TRUE;

	    fwrite_mapped_value( fp, trade + i, "type", FALSE );
	}

	fputc( ']', fp );
    }

    return TRUE;
}


bool dbrwf_all_affect( FILE *fp, void *vo, db_action action )
{
    AFFECT_DATA **paf = (AFFECT_DATA **)vo;

    if( action == DBACTION_WRITE )
    {
	AFFECT_DATA *af;

	for( af = *paf; af; af = af->next )
	{
	    if( af->deleted )
		continue;

	    fprintf( fp, "Affect\t" );
	    if( !dbrwf_affect( fp, af, action ) )
		return FALSE;
	    fprintf( fp, ";\n" );
	}
    }

    return TRUE;
}


bool dbrwf_all_clan( FILE *fp, void *vo, db_action action )
{
    CLAN_DATA **pClan = (CLAN_DATA **)vo;

    if( action == DBACTION_READ )
    {
	temp_fread_string( fp, db_buf );
	*pClan = clan_lookup( db_buf );
    }
    else if( action == DBACTION_WRITE )
    {
	fwrite_quoted_string( fp, (*pClan)->name );
    }

    return TRUE;
}


bool dbrwf_all_obj( FILE *fp, CHAR_DATA *ch, OBJ_DATA *obj_cont, db_action action )
{
    OBJ_DATA **obj_list;
    OBJ_DATA *obj;
    OBJ_DATA obj_save;

    if( ch )
    {
	obj_list = &ch->carrying;
    }
    else if( obj_cont )
    {
	obj_list = &obj_cont->contains;
    }
    else
	return FALSE;

    if( action == DBACTION_READ )
    {
	/*
	 * This sort of nesting ruins the static data used as a buffer, so we
	 * keep a copy of the data saved so far in a temporary buffer.
	 */
	memcpy( &obj_save, &sdb.obj, sizeof( OBJ_DATA ) );
	obj = load_complex_block( fp, global_data_table
				  + get_top_entry( "Obj" ) );
	memcpy( &sdb.obj, &obj_save, sizeof( OBJ_DATA ) );

	if( !obj )
	    return FALSE;

	if( ch )
	    obj_to_char( obj, ch );
	else
	    obj_to_obj( obj, obj_cont );

	/* Now move the object from the head of the list to the back, so it
	   is in the same order as before */
	if( obj->next_content )
	{
	    OBJ_DATA *prev;

	    *obj_list = obj->next_content;

	    obj->next_content = NULL;

	    for( prev = *obj_list; prev->next_content; prev = prev->next_content )
		;
	    prev->next_content = obj;
	}
    }
    /*
     * Writes an entire list of objects.
     */
    else if( action == DBACTION_WRITE )
    {
	bool found = FALSE;

	for( obj = *obj_list; obj; obj = obj->next_content )
	{
	    if( dbrwf_obj( fp, obj, DBACTION_CHKDEFAULT ) )
		continue;

	    if( found )
		fprintf( fp, ";\nObj\t" );
	    else
		found = TRUE;

	    memcpy( &obj_save, &sdb.obj, sizeof( OBJ_DATA ) );
	    save_complex_block( fp, global_data_table
				+ get_top_entry( "Obj" ), obj );
	    memcpy( &sdb.obj, &obj_save, sizeof( OBJ_DATA ) );
	}


    }

    return TRUE;
}


bool dbrwf_all_religion( FILE *fp, void *vo, db_action action )
{
    RELIGION_DATA **pReligion = (RELIGION_DATA **)vo;

    if( action == DBACTION_READ )
    {
	temp_fread_string( fp, db_buf );
	*pReligion = religion_lookup( db_buf );
    }
    else if( action == DBACTION_WRITE )
    {
	fwrite_quoted_string( fp, (*pReligion)->name );
    }

    return TRUE;
}



/****************************************************************************
 * Interface functions.
 *
 * See how EASY these are!  What's even better is that these functions for
 * saving data don't even require functions for reading it all back!
 */

void save_planes( void )
{
    FILE *fp;
    PLANE_DATA *pPlane;

    if( !( fp = open_file( SYSTEM_DIR PLANE_FILE, "w", TRUE ) ) )
    {
	bug( "Cannot open planes file." );
	return;
    }

    for( pPlane = plane_list; pPlane; pPlane = pPlane->next )
    {
	write_next_item( fp, "Plane", pPlane );
	fputc( '\n', fp );
    }
    fprintf( fp, "\neof\n\n" );
    close_file( fp );
    return;
}


void save_socials( )
{
    FILE *fp;
    SOCIAL_DATA *pSocial;
    int iHash;

    fp = open_file( SYSTEM_DIR SOCIAL_FILE, "w", TRUE );
    if( !fp )
    {
	bug( "Cannot open social file." );
	return;
    }

    for( iHash = 0; iHash < 27; iHash++ )
    {
	for( pSocial = social_table[iHash]; pSocial; pSocial = pSocial->next )
	{
	    write_next_item( fp, "Social", pSocial );
	    fputc( '\n', fp );
	}
    }

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

    close_file( fp );
    return;
}


void save_religions( )
{
    FILE *fp;
    RELIGION_DATA *pReligion;

    fp = open_file( SYSTEM_DIR RELIGION_FILE, "w", TRUE );
    if( !fp )
    {
	bug( "Cannot open religion file." );
	return;
    }

    for( pReligion = religion_first; pReligion; pReligion = pReligion->next )
    {
	write_next_item( fp, "Religion", pReligion );
	fputc( '\n', fp );
    }

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

    close_file( fp );
    return;
}


void save_clans( )
{
    FILE *fp;
    CLAN_DATA *pClan;

    fp = open_file( SYSTEM_DIR CLAN_FILE, "w", TRUE );
    if( !fp )
    {
	bug( "Cannot open clan file." );
	return;
    }

    for( pClan = clan_first; pClan; pClan = pClan->next )
    {
	write_next_item( fp, "Clan", pClan );
	fputc( '\n', fp );
    }

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

    close_file( fp );
    return;
}


void load_poses( void )
{
    char *p;
    int i, stat;
    POSE_DATA *pose;

    strcpy( strArea, POSES_DIR );
    p = strchr( strArea, '\0' );
    for( i = 0; i < MAX_CLASS; ++i )
    {
	strcpy( p, class_table[i].who_name );
	if( !( fpArea = open_file( strArea, "r", TRUE ) ) )
	{
	    perror( strArea );
	    bug( "No pose file for the %s class.", class_table[i].name );
	    continue;
	}
	for( ;; )
	{
	    pose = (POSE_DATA *)read_next_item( fpArea, &stat );

	    if( stat < 0 )
		break;
	    pose->next = pose_table[i].first;
	    pose_table[i].first = pose;
	    pose_table[i].size++;
	    top_pose++;
	}
	close_file( fpArea );
	fpArea = NULL;
    }
    return;
}


void save_poses( void )
{
    int i;
    FILE *fp;
    char buf[ MAX_INPUT_LENGTH ];
    POSE_DATA *pose;
    char *p;

    strcpy( buf, POSES_DIR );
    p = strchr( buf, '\0' );

    for( i = 0; i < MAX_CLASS; ++i )
    {
	strcpy( p, class_table[i].who_name );
	if( !( fp = open_file( buf, "w", TRUE ) ) )
	{
	    bug( "Bad pose table filename for class %s: %s",
		       class_table[i].name, buf );
	    continue;
	}
	for( pose = pose_table[i].first; pose; pose = pose->next )
	    write_next_item( fp, "Pose", pose );
	fprintf( fp, "\neof\n" );
	close_file( fp );
    }
}