/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.0 (C) 1994, 1995, 1996, 1998  by Derek Snider      |    ).:.(    *
 * -----------------------------------------------------------|    {o o}    *
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
 * Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek,      |~'~.VxvxV.~'~*
 * Tricops and Fireblade                                      |             *
 * ------------------------------------------------------------------------ *
 * Merc 2.1 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.     *
 * ------------------------------------------------------------------------ *
 *                         Database management module                       *
 ****************************************************************************/

#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <dlfcn.h>
#include "mud.h"

char strArea[MAX_INPUT_LENGTH];
FILE *fpArea;

MOB_INDEX_DATA *mob_index_hash[MAX_KEY_HASH];
OBJ_INDEX_DATA *obj_index_hash[MAX_KEY_HASH];
ROOM_INDEX_DATA *room_index_hash[MAX_KEY_HASH];

AREA_DATA *first_area;
AREA_DATA *last_area;

short num_skills;
short num_sorted_skills;

void fold_all_areas( void );
void mprog_read_programs( FILE * fp, MOB_INDEX_DATA * pMobIndex );
void oprog_read_programs( FILE * fp, OBJ_INDEX_DATA * pObjIndex );
void rprog_read_programs( FILE * fp, ROOM_INDEX_DATA * pRoomIndex );

void init_mm( void );
void boot_log( const char *str, ... );
void load_resets( AREA_DATA * tarea, FILE * fp );
void renumber_put_resets( ROOM_INDEX_DATA * room );

void shutdown_mud( const char *reason )
{
   FILE *fp;

   if( ( fp = fopen( SHUTDOWN_FILE, "a" ) ) != NULL )
   {
      fprintf( fp, "%s\n", reason );
      fclose( fp );
      fp = NULL;
   }
}

void boot_db( void )
{
   fpArea = NULL;

   log_string( "Loading skill table" );
   load_skill_table(  );
   sort_skill_table(  );
   remap_slot_numbers(  ); /* must be after the sort */

   num_sorted_skills = num_skills;

   first_area = NULL;
   last_area = NULL;

   log_string( "Initializing random number generator" );
   init_mm(  );

   /* Read in all the area files. */
   {
      FILE *fpList;

      log_string( "Reading in area files..." );
      if( !( fpList = fopen( AREA_LIST, "r" ) ) )
      {
         perror( AREA_LIST );
         shutdown_mud( "Unable to open area list" );
         exit( 1 );
      }

      for( ;; )
      {
         if( feof( fpList ) )
         {
            bug( "%s: EOF encountered reading area list - no $ found at end of file.", __FUNCTION__ );
            break;
         }
         mudstrlcpy( strArea, fread_word( fpList ), MAX_INPUT_LENGTH );
         if( strArea[0] == '$' )
            break;

         load_area_file( NULL, strArea );
      }
      fclose( fpList );
      fpList = NULL;
   }

   fold_all_areas( );
}

/*
 * Load an 'area' header line.
 */
AREA_DATA *load_area( FILE * fp, int aversion )
{
   AREA_DATA *pArea;

   CREATE( pArea, AREA_DATA, 1 );
   pArea->version = aversion;
   pArea->first_room = pArea->last_room = NULL;
   pArea->name = fread_string_nohash( fp );
   pArea->author = STRALLOC( "unknown" );
   pArea->credits = STRALLOC( "" );
   pArea->filename = str_dup( strArea );
   pArea->age = 15;
   pArea->nplayer = 0;
   pArea->low_r_vnum = 0;
   pArea->low_o_vnum = 0;
   pArea->low_m_vnum = 0;
   pArea->hi_r_vnum = 0;
   pArea->hi_o_vnum = 0;
   pArea->hi_m_vnum = 0;
   pArea->low_soft_range = 0;
   pArea->hi_soft_range = MAX_LEVEL;
   pArea->low_hard_range = 0;
   pArea->hi_hard_range = MAX_LEVEL;
   pArea->spelllimit = 0;
   pArea->weatherx = 0;
   pArea->weathery = 0;

   LINK( pArea, first_area, last_area, next, prev );
   return pArea;
}

/* Load the version number of the area file if none exists, then it
 * is set to version 0 when #AREA is read in which is why we check for
 * the #AREA here.  --Shaddai
 */
void load_version( AREA_DATA * tarea, FILE * fp )
{
   tarea->version = fread_number( fp );
}

/*
 * Load an author section. Scryn 2/1/96
 */
void load_author( AREA_DATA * tarea, FILE * fp )
{
   if( !tarea )
   {
      bug( "%s: no #AREA seen yet.", __FUNCTION__ );
      shutdown_mud( "No #AREA" );
      exit( 1 );
   }

   if( tarea->author )
      STRFREE( tarea->author );
   tarea->author = fread_string( fp );
}

/*
 * Load a credits section. Edmond
 */
void load_credits( AREA_DATA * tarea, FILE * fp )
{
   if( !tarea )
   {
      bug( "%s: no #AREA seen yet.", __FUNCTION__ );
      shutdown_mud( "No #AREA" );
      exit( 1 );
   }

   if( tarea->credits )
      STRFREE( tarea->credits );
   tarea->credits = fread_string( fp );
}

/*
 * Load an economy section. Thoric
 */
void load_economy( AREA_DATA * tarea, FILE * fp )
{
   if( !tarea )
   {
      bug( "Load_economy: no #AREA seen yet." );
      shutdown_mud( "No #AREA" );
      exit( 1 );
   }

   tarea->high_economy = fread_number( fp );
   tarea->low_economy = fread_number( fp );
}

/* Reset Message Load, Rennard */
void load_resetmsg( AREA_DATA * tarea, FILE * fp )
{
   if( !tarea )
   {
      bug( "%s: no #AREA seen yet.", __FUNCTION__ );
      shutdown_mud( "No #AREA" );
      exit( 1 );
   }

   if( tarea->resetmsg )
      DISPOSE( tarea->resetmsg );
   tarea->resetmsg = fread_string_nohash( fp );
}

/*
 * Load area flags. Narn, Mar/96 
 */
void load_flags( AREA_DATA * tarea, FILE * fp )
{
   char *ln;
   int x1, x2;

   if( !tarea )
   {
      bug( "Load_flags: no #AREA seen yet." );
      shutdown_mud( "No #AREA" );
      exit( 1 );
   }
   ln = fread_line( fp );
   x1 = x2 = 0;
   sscanf( ln, "%d %d", &x1, &x2 );
   tarea->flags = x1;
   tarea->reset_frequency = x2;
   if( x2 )
      tarea->age = x2;
}

/*
 * Load a mob section.
 */
void load_mobiles( AREA_DATA * tarea, FILE * fp )
{
   MOB_INDEX_DATA *pMobIndex;
   char *ln;
   int x1, x2, x3, x4, x5, x6, x7, x8;

   if( !tarea )
   {
      bug( "Load_mobiles: no #AREA seen yet." );
      shutdown_mud( "No #AREA" );
      exit( 1 );
   }

   for( ;; )
   {
      int vnum;
      char letter;
      int iHash;
      bool oldmob;

      letter = fread_letter( fp );
      if( letter != '#' )
      {
         bug( "Load_mobiles: # not found." );
         shutdown_mud( "# not found" );
         exit( 1 );
      }

      vnum = fread_number( fp );
      if( vnum == 0 )
         break;

      if( get_mob_index( vnum ) )
      {
         bug( "Load_mobiles: vnum %d duplicated.", vnum );
         shutdown_mud( "duplicate vnum" );
         exit( 1 );
      }
      else
      {
         oldmob = false;
         CREATE( pMobIndex, MOB_INDEX_DATA, 1 );
      }

      pMobIndex->vnum = vnum;
      if( !tarea->low_m_vnum )
         tarea->low_m_vnum = vnum;
      if( vnum > tarea->hi_m_vnum )
         tarea->hi_m_vnum = vnum;
      pMobIndex->player_name = fread_string( fp );
      pMobIndex->short_descr = fread_string( fp );
      pMobIndex->long_descr = fread_string( fp );
      pMobIndex->description = fread_string( fp );

      // well, it's pretty nasty to cast, but we know that we own this
      // memory because we just created it.
      ((char*)pMobIndex->long_descr)[0] = UPPER( pMobIndex->long_descr[0] );
      ((char*)pMobIndex->description)[0] = UPPER( pMobIndex->description[0] );

      pMobIndex->act = fread_bitvector( fp );
      xSET_BIT( pMobIndex->act, ACT_IS_NPC );
      pMobIndex->affected_by = fread_bitvector( fp );
      pMobIndex->pShop = NULL;
      pMobIndex->rShop = NULL;
      pMobIndex->alignment = fread_number( fp );
      letter = fread_letter( fp );
      pMobIndex->level = fread_number( fp );

      pMobIndex->mobthac0 = fread_number( fp );
      pMobIndex->ac = fread_number( fp );
      pMobIndex->hitnodice = fread_number( fp );
      /*
       * 'd'      
       */ fread_letter( fp );
      pMobIndex->hitsizedice = fread_number( fp );
      /*
       * '+'      
       */ fread_letter( fp );
      pMobIndex->hitplus = fread_number( fp );
      pMobIndex->damnodice = fread_number( fp );
      /*
       * 'd'      
       */ fread_letter( fp );
      pMobIndex->damsizedice = fread_number( fp );
      /*
       * '+'      
       */ fread_letter( fp );
      pMobIndex->damplus = fread_number( fp );
      ln = fread_line( fp );
      x1 = x2 = 0;
      sscanf( ln, "%d %d", &x1, &x2 );
      pMobIndex->gold = x1;
      pMobIndex->exp = x2;

      /*
       * pMobIndex->position     = fread_number( fp ); 
       */
      pMobIndex->position = fread_number( fp );
      if( pMobIndex->position < 100 )
      {
         switch ( pMobIndex->position )
         {
            default:
            case 0:
            case 1:
            case 2:
            case 3:
            case 4:
               break;
            case 5:
               pMobIndex->position = 6;
               break;
            case 6:
               pMobIndex->position = 8;
               break;
            case 7:
               pMobIndex->position = 9;
               break;
            case 8:
               pMobIndex->position = 12;
               break;
            case 9:
               pMobIndex->position = 13;
               break;
            case 10:
               pMobIndex->position = 14;
               break;
            case 11:
               pMobIndex->position = 15;
               break;
         }
      }
      else
      {
         pMobIndex->position -= 100;
      }

      /*
       * pMobIndex->defposition     = fread_number( fp ); 
       */
      pMobIndex->defposition = fread_number( fp );
      if( pMobIndex->defposition < 100 )
      {
         switch ( pMobIndex->defposition )
         {
            default:
            case 0:
            case 1:
            case 2:
            case 3:
            case 4:
               break;
            case 5:
               pMobIndex->defposition = 6;
               break;
            case 6:
               pMobIndex->defposition = 8;
               break;
            case 7:
               pMobIndex->defposition = 9;
               break;
            case 8:
               pMobIndex->defposition = 12;
               break;
            case 9:
               pMobIndex->defposition = 13;
               break;
            case 10:
               pMobIndex->defposition = 14;
               break;
            case 11:
               pMobIndex->defposition = 15;
               break;
         }
      }
      else
      {
         pMobIndex->defposition -= 100;
      }

      /*
       * Back to meaningful values.
       */
      pMobIndex->sex = fread_number( fp );

      if( letter != 'S' && letter != 'C' )
      {
         bug( "Load_mobiles: vnum %d: letter '%c' not S or C.", vnum, letter );
         shutdown_mud( "bad mob data" );
         exit( 1 );
      }

      if( letter == 'C' )  /* Realms complex mob   -Thoric */
      {
         pMobIndex->perm_str = fread_number( fp );
         pMobIndex->perm_int = fread_number( fp );
         pMobIndex->perm_wis = fread_number( fp );
         pMobIndex->perm_dex = fread_number( fp );
         pMobIndex->perm_con = fread_number( fp );
         pMobIndex->perm_cha = fread_number( fp );
         pMobIndex->perm_lck = fread_number( fp );
         pMobIndex->saving_poison_death = fread_number( fp );
         pMobIndex->saving_wand = fread_number( fp );
         pMobIndex->saving_para_petri = fread_number( fp );
         pMobIndex->saving_breath = fread_number( fp );
         pMobIndex->saving_spell_staff = fread_number( fp );

         if( tarea->version < 1000 )   /* Normal Smaug zones load here */
         {
            ln = fread_line( fp );
            x1 = x2 = x3 = x4 = x5 = x6 = x7 = x8 = 0;
            sscanf( ln, "%d %d %d %d %d %d %d", &x1, &x2, &x3, &x4, &x5, &x6, &x7 );

            pMobIndex->race = x1;
            pMobIndex->Class = x2;
            pMobIndex->height = x3;
            pMobIndex->weight = x4;
            pMobIndex->speaks = x5;
            pMobIndex->speaking = x6;
            pMobIndex->numattacks = x7;
         }
         else  /* SmaugWiz zone here */
         {
            ln = fread_line( fp );
            x1 = x2 = x3 = x4 = x5 = 0;
            sscanf( ln, "%d %d %d %d %d", &x1, &x2, &x3, &x4, &x5 );
            pMobIndex->race = x1;
            pMobIndex->Class = x2;
            pMobIndex->height = x3;
            pMobIndex->weight = x4;
            pMobIndex->numattacks = x5;

            ln = fread_line( fp );

            ln = fread_line( fp );
         }

         pMobIndex->hitroll = fread_number( fp );
         pMobIndex->damroll = fread_number( fp );
         pMobIndex->xflags = fread_number( fp );
         pMobIndex->resistant = fread_number( fp );
         pMobIndex->immune = fread_number( fp );
         pMobIndex->susceptible = fread_number( fp );
         pMobIndex->attacks = fread_bitvector( fp );
         pMobIndex->defenses = fread_bitvector( fp );
      }
      else
      {
         pMobIndex->perm_str = 13;
         pMobIndex->perm_dex = 13;
         pMobIndex->perm_int = 13;
         pMobIndex->perm_wis = 13;
         pMobIndex->perm_cha = 13;
         pMobIndex->perm_con = 13;
         pMobIndex->perm_lck = 13;
         pMobIndex->race = 0;
         pMobIndex->Class = 3;
         pMobIndex->xflags = 0;
         pMobIndex->resistant = 0;
         pMobIndex->immune = 0;
         pMobIndex->susceptible = 0;
         pMobIndex->numattacks = 0;
         xCLEAR_BITS( pMobIndex->attacks );
         xCLEAR_BITS( pMobIndex->defenses );
      }

      letter = fread_letter( fp );
      if( letter == '>' )
      {
         ungetc( letter, fp );
         mprog_read_programs( fp, pMobIndex );
      }
      else
         ungetc( letter, fp );

      if( !oldmob )
      {
         iHash = vnum % MAX_KEY_HASH;
         pMobIndex->next = mob_index_hash[iHash];
         mob_index_hash[iHash] = pMobIndex;
      }
   }
}

/*
 * Load an obj section.
 */
void load_objects( AREA_DATA * tarea, FILE * fp )
{
   OBJ_INDEX_DATA *pObjIndex;
   char letter;
   char *ln;
   int x1, x2, x3, x4, x5, x6;

   if( !tarea )
   {
      bug( "Load_objects: no #AREA seen yet." );
      shutdown_mud( "No #AREA" );
      exit( 1 );
   }

   for( ;; )
   {
      int vnum;
      int iHash;
      bool oldobj;

      letter = fread_letter( fp );
      if( letter != '#' )
      {
         bug( "Load_objects: # not found." );
         shutdown_mud( "# not found" );
         exit( 1 );
      }

      vnum = fread_number( fp );
      if( vnum == 0 )
         break;

      if( get_obj_index( vnum ) )
      {
         bug( "Load_objects: vnum %d duplicated.", vnum );
         shutdown_mud( "duplicate vnum" );
         exit( 1 );
      }
      else
      {
         oldobj = false;
         CREATE( pObjIndex, OBJ_INDEX_DATA, 1 );
      }

      pObjIndex->vnum = vnum;
      if( !tarea->low_o_vnum )
         tarea->low_o_vnum = vnum;
      if( vnum > tarea->hi_o_vnum )
         tarea->hi_o_vnum = vnum;
      pObjIndex->name = fread_string( fp );
      pObjIndex->short_descr = fread_string( fp );
      pObjIndex->description = fread_string( fp );
      pObjIndex->action_desc = fread_string( fp );

      /*
       * Commented out by Narn, Apr/96 to allow item short descs like 
       * Bonecrusher and Oblivion 
       */
      /*
       * pObjIndex->short_descr[0]  = LOWER(pObjIndex->short_descr[0]);
       */

      // we just created this, so we know we can touch the string
      ((char*)pObjIndex->description)[0] = UPPER( pObjIndex->description[0] );

      pObjIndex->item_type = fread_number( fp );
      pObjIndex->extra_flags = fread_bitvector( fp );

      ln = fread_line( fp );
      x1 = x2 = x3 = 0;
      sscanf( ln, "%d %d %d", &x1, &x2, &x3 );
      pObjIndex->wear_flags = x1;
      pObjIndex->layers = x2;
      pObjIndex->level = x3;

      ln = fread_line( fp );
      x1 = x2 = x3 = x4 = x5 = x6 = 0;
      sscanf( ln, "%d %d %d %d %d %d", &x1, &x2, &x3, &x4, &x5, &x6 );
      pObjIndex->value[0] = x1;
      pObjIndex->value[1] = x2;
      pObjIndex->value[2] = x3;
      pObjIndex->value[3] = x4;
      pObjIndex->value[4] = x5;
      pObjIndex->value[5] = x6;
      if( tarea->version < 1000 )
      {
         pObjIndex->weight = fread_number( fp );
         pObjIndex->weight = UMAX( 1, pObjIndex->weight );
         pObjIndex->cost = fread_number( fp );
         pObjIndex->rent = fread_number( fp );  /* unused */
      }
      if( tarea->version > 0 )
      {
         switch ( pObjIndex->item_type )
         {
            case ITEM_PILL:
            case ITEM_POTION:
            case ITEM_SCROLL:
               pObjIndex->value[1] = skill_lookup( fread_word( fp ) );
               pObjIndex->value[2] = skill_lookup( fread_word( fp ) );
               pObjIndex->value[3] = skill_lookup( fread_word( fp ) );
               break;
            case ITEM_STAFF:
            case ITEM_WAND:
               pObjIndex->value[3] = skill_lookup( fread_word( fp ) );
               break;
            case ITEM_SALVE:
               pObjIndex->value[4] = skill_lookup( fread_word( fp ) );
               pObjIndex->value[5] = skill_lookup( fread_word( fp ) );
               break;
         }
      }
      if( tarea->version == 1000 )
      {
         while( !isdigit( letter = fread_letter( fp ) ) )
            fread_to_eol( fp );
         ungetc( letter, fp );

         pObjIndex->weight = fread_number( fp );
         pObjIndex->weight = UMAX( 1, pObjIndex->weight );
         pObjIndex->cost = fread_number( fp );
         pObjIndex->rent = fread_number( fp );  /* unused */
      }

      for( ;; )
      {
         letter = fread_letter( fp );

         if( letter == 'A' )
         {
            AFFECT_DATA *paf;

            CREATE( paf, AFFECT_DATA, 1 );
            paf->type = -1;
            paf->duration = -1;
            paf->location = fread_number( fp );
            if( paf->location == APPLY_WEAPONSPELL
                || paf->location == APPLY_WEARSPELL
                || paf->location == APPLY_REMOVESPELL
                || paf->location == APPLY_STRIPSN || paf->location == APPLY_RECURRINGSPELL )
               paf->modifier = slot_lookup( fread_number( fp ) );
            else
               paf->modifier = fread_number( fp );
            xCLEAR_BITS( paf->bitvector );
            LINK( paf, pObjIndex->first_affect, pObjIndex->last_affect, next, prev );
         }

         else if( letter == 'E' )
         {
            EXTRA_DESCR_DATA *ed;

            CREATE( ed, EXTRA_DESCR_DATA, 1 );
            ed->keyword = fread_string( fp );
            ed->description = fread_string( fp );
            LINK( ed, pObjIndex->first_extradesc, pObjIndex->last_extradesc, next, prev );
         }
         else if( letter == '>' )
         {
            ungetc( letter, fp );
            oprog_read_programs( fp, pObjIndex );
         }

         else
         {
            ungetc( letter, fp );
            break;
         }
      }

      /*
       * Translate spell "slot numbers" to internal "skill numbers."
       */
      if( tarea->version == 0 )
         switch ( pObjIndex->item_type )
         {
            case ITEM_PILL:
            case ITEM_POTION:
            case ITEM_SCROLL:
               pObjIndex->value[1] = slot_lookup( pObjIndex->value[1] );
               pObjIndex->value[2] = slot_lookup( pObjIndex->value[2] );
               pObjIndex->value[3] = slot_lookup( pObjIndex->value[3] );
               break;

            case ITEM_STAFF:
            case ITEM_WAND:
               pObjIndex->value[3] = slot_lookup( pObjIndex->value[3] );
               break;
            case ITEM_SALVE:
               pObjIndex->value[4] = slot_lookup( pObjIndex->value[4] );
               pObjIndex->value[5] = slot_lookup( pObjIndex->value[5] );
               break;
         }

      if( !oldobj )
      {
         iHash = vnum % MAX_KEY_HASH;
         pObjIndex->next = obj_index_hash[iHash];
         obj_index_hash[iHash] = pObjIndex;
      }
   }
}

/*
 * Load a reset section.
 */
void load_resets( AREA_DATA * tarea, FILE * fp )
{
   ROOM_INDEX_DATA *pRoomIndex = NULL;
   ROOM_INDEX_DATA *roomlist;
   bool not01 = false;
   int count = 0;

   if( !tarea )
   {
      bug( "%s", "Load_resets: no #AREA seen yet." );
      shutdown_mud( "No #AREA" );
      exit( 1 );
   }

   if( !tarea->first_room )
   {
      bug( "%s: No #ROOMS section found. Cannot load resets.", __FUNCTION__ );
      shutdown_mud( "No #ROOMS" );
      exit( 1 );
   }

   for( ;; )
   {
      EXIT_DATA *pexit;
      char letter;
      int extra, arg1, arg2, arg3;

      if( ( letter = fread_letter( fp ) ) == 'S' )
         break;

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

      extra = fread_number( fp );
      if( letter == 'M' || letter == 'O' )
         extra = 0;
      arg1 = fread_number( fp );
      arg2 = fread_number( fp );
      arg3 = ( letter == 'G' || letter == 'R' ) ? 0 : fread_number( fp );
      fread_to_eol( fp );
      ++count;

      /*
       * Validate parameters.
       * We're calling the index functions for the side effect.
       */
      switch ( letter )
      {
         default:
            bug( "%s: bad command '%c'.", __FUNCTION__, letter );
            boot_log( "%s: %s (%d) bad command '%c'.", __FUNCTION__, tarea->filename, count, letter );
            return;

         case 'M':
            if( get_mob_index( arg1 ) == NULL )
               boot_log( "%s: %s (%d) 'M': mobile %d doesn't exist.", __FUNCTION__, tarea->filename, count, arg1 );

            if( ( pRoomIndex = get_room_index( arg3 ) ) == NULL )
               boot_log( "%s: %s (%d) 'M': room %d doesn't exist.", __FUNCTION__, tarea->filename, count, arg3 );
            else
               add_reset( pRoomIndex, letter, extra, arg1, arg2, arg3 );
            break;

         case 'O':
            if( get_obj_index( arg1 ) == NULL )
               boot_log( "%s: %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, tarea->filename, count, letter, arg1 );

            if( ( pRoomIndex = get_room_index( arg3 ) ) == NULL )
               boot_log( "%s: %s (%d) '%c': room %d doesn't exist.", __FUNCTION__, tarea->filename, count, letter, arg3 );
            else
            {
               if( !pRoomIndex )
                  bug( "%s: Unable to add room reset - room not found.", __FUNCTION__ );
               else
                  add_reset( pRoomIndex, letter, extra, arg1, arg2, arg3 );
            }
            break;

         case 'P':
            if( get_obj_index( arg1 ) == NULL )
               boot_log( "%s: %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, tarea->filename, count, letter, arg1 );
            if( arg3 > 0 )
            {
               if( get_obj_index( arg3 ) == NULL )
                  boot_log( "%s: %s (%d) 'P': destination object %d doesn't exist.", __FUNCTION__, tarea->filename, count, arg3 );
               if( extra > 1 )
                  not01 = true;
            }
            if( !pRoomIndex )
               bug( "%s: Unable to add room reset - room not found.", __FUNCTION__ );
            else
            {
               if( arg3 == 0 )
                  arg3 = 2;
               add_reset( pRoomIndex, letter, extra, arg1, arg2, arg3 );
            }
            break;

         case 'G':
         case 'E':
            if( get_obj_index( arg1 ) == NULL )
               boot_log( "%s: %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, tarea->filename, count, letter, arg1 );
            if( !pRoomIndex )
               bug( "%s: Unable to add room reset - room not found.", __FUNCTION__ );
            else
               add_reset( pRoomIndex, letter, extra, arg1, arg2, arg3 );
            break;

         case 'T':
            if( IS_SET( extra, TRAP_OBJ ) )
               bug( "%s: Unable to add legacy object trap reset. Must be converted manually.", __FUNCTION__ );
            else
            {
               if( !( pRoomIndex = get_room_index( arg3 ) ) )
                  bug( "%s: Unable to add trap reset - room not found.", __FUNCTION__ );
               else
                  add_reset( pRoomIndex, letter, extra, arg1, arg2, arg3 );
            }
            break;

         case 'H':
            bug( "%s: Unable to convert legacy hide reset. Must be converted manually.", __FUNCTION__ );
            break;

         case 'D':
            if( !( pRoomIndex = get_room_index( arg1 ) ) )
            {
               bug( "%s: 'D': room %d doesn't exist.", __FUNCTION__, arg1 );
               bug( "Reset: %c %d %d %d %d", letter, extra, arg1, arg2, arg3 );
               boot_log( "%s: %s (%d) 'D': room %d doesn't exist.", __FUNCTION__, tarea->filename, count, arg1 );
               break;
            }

            if( arg2 < 0 || arg2 > MAX_DIR + 1
                || !( pexit = get_exit( pRoomIndex, arg2 ) ) || !IS_SET( pexit->exit_info, EX_ISDOOR ) )
            {
               bug( "%s: 'D': exit %d not door.", __FUNCTION__, arg2 );
               bug( "Reset: %c %d %d %d %d", letter, extra, arg1, arg2, arg3 );
               boot_log( "%s: %s (%d) 'D': exit %d not door.", __FUNCTION__, tarea->filename, count, arg2 );
            }

            if( arg3 < 0 || arg3 > 2 )
            {
               bug( "%s: 'D': bad 'locks': %d.", __FUNCTION__, arg3 );
               boot_log( "%s: %s (%d) 'D': bad 'locks': %d.", __FUNCTION__, tarea->filename, count, arg3 );
            }
            add_reset( pRoomIndex, letter, extra, arg1, arg2, arg3 );
            break;

         case 'R':
            if( !( pRoomIndex = get_room_index( arg1 ) ) )
               boot_log( "%s: %s (%d) 'R': room %d doesn't exist.", __FUNCTION__, tarea->filename, count, arg1 );
            else
               add_reset( pRoomIndex, letter, extra, arg1, arg2, arg3 );
            if( arg2 < 0 || arg2 > 10 )
            {
               bug( "%s: 'R': bad exit %d.", __FUNCTION__, arg2 );
               boot_log( "%s: %s (%d) 'R': bad exit %d.", __FUNCTION__, tarea->filename, count, arg2 );
               break;
            }
            break;
      }
   }
   if( !not01 )
   {
      for( roomlist = tarea->first_room; roomlist; roomlist = roomlist->next_aroom )
         renumber_put_resets( roomlist );
   }
   return;
}

void load_smaugwiz_reset( ROOM_INDEX_DATA * room, FILE * fp )
{
   EXIT_DATA *pexit;
   char letter2;
   int extra, arg1, arg2, arg3, arg4;
   int count = 0;

   letter2 = fread_letter( fp );
   extra = fread_number( fp );
   arg1 = fread_number( fp );
   arg2 = fread_number( fp );
   arg3 = fread_number( fp );
   arg4 = ( letter2 == 'G' || letter2 == 'R' ) ? 0 : fread_number( fp );
   fread_to_eol( fp );

   ++count;

   /*
    * Validate parameters.
    * We're calling the index functions for the side effect.
    */
   switch ( letter2 )
   {
      default:
         bug( "%s: SmaugWiz - bad command '%c'.", __FUNCTION__, letter2 );
         boot_log( "%s: %s (%d) bad command '%c'.", __FUNCTION__, room->area->filename, count, letter2 );
         return;

      case 'M':
         if( get_mob_index( arg2 ) == NULL )
            boot_log( "%s: SmaugWiz - %s (%d) 'M': mobile %d doesn't exist.", __FUNCTION__, room->area->filename, count, arg2 );
         break;

      case 'O':
         if( get_obj_index( arg2 ) == NULL )
            boot_log( "%s: SmaugWiz - %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, room->area->filename, count, letter2, arg2 );
         break;

      case 'P':
         if( get_obj_index( arg2 ) == NULL )
            boot_log( "%s: SmaugWiz - %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, room->area->filename, count, letter2, arg2 );
         if( arg4 > 0 )
         {
            if( get_obj_index( arg4 ) == NULL )
               boot_log( "$s: SmaugWiz - %s (%d) 'P': destination object %d doesn't exist.", __FUNCTION__, room->area->filename, count, arg4 );
         }
         break;

      case 'G':
      case 'E':
         if( get_obj_index( arg2 ) == NULL )
            boot_log( "%s: SmaugWiz - %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, room->area->filename, count, letter2, arg2 );
         break;

      case 'T':
         break;

      case 'H':
         if( arg1 > 0 )
            if( get_obj_index( arg2 ) == NULL )
               boot_log( "%s: SmaugWiz - %s (%d) 'H': object %d doesn't exist.", __FUNCTION__, room->area->filename, count, arg2 );
         break;

      case 'D':
         if( arg3 < 0 || arg3 > MAX_DIR + 1 || ( pexit = get_exit( room, arg3 ) ) == NULL
             || !IS_SET( pexit->exit_info, EX_ISDOOR ) )
         {
            bug( "%s: SmaugWiz - 'D': exit %d not door.", __FUNCTION__, arg3 );
            bug( "Reset: %c %d %d %d %d %d", letter2, extra, arg1, arg2, arg3, arg4 );
            boot_log( "%s: SmaugWiz - %s (%d) 'D': exit %d not door.", __FUNCTION__, room->area->filename, count, arg3 );
         }
         if( arg4 < 0 || arg4 > 2 )
         {
            bug( "%s: 'D': bad 'locks': %d.", __FUNCTION__, arg4 );
            boot_log( "%s: SmaugWiz - %s (%d) 'D': bad 'locks': %d.", __FUNCTION__, room->area->filename, count, arg4 );
         }
         break;

      case 'R':
         if( arg3 < 0 || arg3 > 10 )
         {
            bug( "%s: 'R': bad exit %d.", __FUNCTION__, arg3 );
            boot_log( "%s: SmaugWiz - %s (%d) 'R': bad exit %d.", __FUNCTION__, room->area->filename, count, arg3 );
            break;
         }
         break;
   }
   /*
    * Don't bother asking why arg1 isn't passed, SmaugWiz had some purpose for it, but it remains a mystery 
    */
   add_reset( room, letter2, extra, arg2, arg3, arg4 );
}  /* End SmaugWiz resets */

void load_room_reset( ROOM_INDEX_DATA * room, FILE * fp )
{
   EXIT_DATA *pexit;
   char letter;
   int extra, arg1, arg2, arg3;
   bool not01 = false;
   int count = 0;

   letter = fread_letter( fp );
   extra = fread_number( fp );
   if( letter == 'M' || letter == 'O' )
      extra = 0;
   arg1 = fread_number( fp );
   arg2 = fread_number( fp );
   arg3 = ( letter == 'G' || letter == 'R' ) ? 0 : fread_number( fp );
   fread_to_eol( fp );
   ++count;

   /*
    * Validate parameters.
    * We're calling the index functions for the side effect.
    */
   switch ( letter )
   {
      default:
         bug( "%s: bad command '%c'.", __FUNCTION__, letter );
         boot_log( "%s: %s (%d) bad command '%c'.", __FUNCTION__, room->area->filename, count, letter );
         return;

      case 'M':
         if( get_mob_index( arg1 ) == NULL )
            boot_log( "%s: %s (%d) 'M': mobile %d doesn't exist.", __FUNCTION__, room->area->filename, count, arg1 );
         break;

      case 'O':
         if( get_obj_index( arg1 ) == NULL )
            boot_log( "%s: %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, room->area->filename, count, letter, arg1 );
         break;

      case 'P':
         if( get_obj_index( arg1 ) == NULL )
            boot_log( "%s: %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, room->area->filename, count, letter, arg1 );

         if( arg3 <= 0 )
            arg3 = 2;
         if( get_obj_index( arg3 ) == NULL )
            boot_log( "%s: %s (%d) 'P': destination object %d doesn't exist.", __FUNCTION__, room->area->filename, count, arg3 );
         if( extra > 1 )
            not01 = true;
         break;

      case 'G':
      case 'E':
         if( get_obj_index( arg1 ) == NULL )
            boot_log( "%s: %s (%d) '%c': object %d doesn't exist.", __FUNCTION__, room->area->filename, count, letter, arg1 );
         break;

      case 'T':
      case 'H':
         break;

      case 'D':
         if( arg2 < 0 || arg2 > MAX_DIR + 1
         || !( pexit = get_exit( room, arg2 ) ) || !IS_SET( pexit->exit_info, EX_ISDOOR ) )
         {
            bug( "%s: 'D': exit %d not door.", __FUNCTION__, arg2 );
            bug( "Reset: %c %d %d %d %d", letter, extra, arg1, arg2, arg3 );
            boot_log( "%s: %s (%d) 'D': exit %d not door.", __FUNCTION__, room->area->filename, count, arg2 );
         }

         if( arg3 < 0 || arg3 > 2 )
         {
            bug( "%s: 'D': bad 'locks': %d.", __FUNCTION__, arg3 );
            boot_log( "%s: %s (%d) 'D': bad 'locks': %d.", __FUNCTION__, room->area->filename, count, arg3 );
         }
         break;

      case 'R':
         if( arg2 < 0 || arg2 > 10 )
         {
            bug( "%s: 'R': bad exit %d.", __FUNCTION__, arg2 );
            boot_log( "%s: %s (%d) 'R': bad exit %d.", __FUNCTION__, room->area->filename, count, arg2 );
            break;
         }
         break;
   }
   add_reset( room, letter, extra, arg1, arg2, arg3 );

   if( !not01 )
      renumber_put_resets( room );
   return;
}

/*
 * Load a room section.
 */
void load_rooms( AREA_DATA * tarea, FILE * fp )
{
   ROOM_INDEX_DATA *pRoomIndex;
   char *ln;

   if( !tarea )
   {
      bug( "Load_rooms: no #AREA seen yet." );
      shutdown_mud( "No #AREA" );
      exit( 1 );
   }

   tarea->first_room = tarea->last_room = NULL;

   for( ;; )
   {
      int vnum;
      char letter;
      int door;
      int iHash;
      bool oldroom;
      int x1, x2, x3, x4, x5, x6;

      letter = fread_letter( fp );
      if( letter != '#' )
      {
         bug( "Load_rooms: # not found." );
         shutdown_mud( "# not found" );
         exit( 1 );
      }

      vnum = fread_number( fp );
      if( vnum == 0 )
         break;

      if( get_room_index( vnum ) != NULL )
      {
         bug( "Load_rooms: vnum %d duplicated.", vnum );
         shutdown_mud( "duplicate vnum" );
         exit( 1 );
      }
      else
      {
         oldroom = false;
         CREATE( pRoomIndex, ROOM_INDEX_DATA, 1 );
      }

      pRoomIndex->area = tarea;
      pRoomIndex->vnum = vnum;
      pRoomIndex->first_extradesc = NULL;
      pRoomIndex->last_extradesc = NULL;
      pRoomIndex->first_affect = NULL;
      pRoomIndex->last_affect = NULL;
      pRoomIndex->first_permaffect = NULL;
      pRoomIndex->last_permaffect = NULL;

      if( !tarea->low_r_vnum )
         tarea->low_r_vnum = vnum;
      if( vnum > tarea->hi_r_vnum )
         tarea->hi_r_vnum = vnum;

      pRoomIndex->name = fread_string( fp );
      pRoomIndex->description = fread_string( fp );

      /*
       * Area number         fread_number( fp ); 
       */
      fread_number( fp );
      pRoomIndex->room_flags = fread_bitvector( fp );

      ln = fread_line( fp );
      x3 = x4 = x5 = x6 = 0;
      sscanf( ln, "%d %d %d %d", &x3, &x4, &x5, &x6 );

      pRoomIndex->sector_type = x3;
      pRoomIndex->tele_delay = x4;
      pRoomIndex->tele_vnum = x5;
      pRoomIndex->tunnel = x6;

      if( pRoomIndex->sector_type < 0 || pRoomIndex->sector_type >= SECT_MAX )
      {
         bug( "Fread_rooms: vnum %d has bad sector_type %d.", vnum, pRoomIndex->sector_type );
         pRoomIndex->sector_type = 1;
      }
      pRoomIndex->light = 0;
      pRoomIndex->first_exit = NULL;
      pRoomIndex->last_exit = NULL;

      for( ;; )
      {
         letter = fread_letter( fp );

         if( letter == 'S' )
            break;

         if( letter == 'D' )
         {
            EXIT_DATA *pexit;
            int locks;

            door = fread_number( fp );
            if( door < 0 || door > 10 )
            {
               bug( "Fread_rooms: vnum %d has bad door number %d.", vnum, door );
               exit( 1 );
            }
            else
            {
               pexit = make_exit( pRoomIndex, NULL, door );
               pexit->description = fread_string( fp );
               pexit->keyword = fread_string( fp );
               pexit->exit_info = 0;
               ln = fread_line( fp );
               x1 = x2 = x3 = x4 = x5 = x6 = 0;
               sscanf( ln, "%d %d %d %d %d %d", &x1, &x2, &x3, &x4, &x5, &x6 );

               locks = x1;
               pexit->key = x2;
               pexit->vnum = x3;
               pexit->vdir = door;
               pexit->distance = x4;
               pexit->pulltype = x5;
               pexit->pull = x6;

               switch ( locks )
               {
                  case 1:
                     pexit->exit_info = EX_ISDOOR;
                     break;
                  case 2:
                     pexit->exit_info = EX_ISDOOR | EX_PICKPROOF;
                     break;
                  default:
                     pexit->exit_info = locks;
               }
            }
         }
         else if( letter == 'E' )
         {
            EXTRA_DESCR_DATA *ed;

            CREATE( ed, EXTRA_DESCR_DATA, 1 );
            ed->keyword = fread_string( fp );
            ed->description = fread_string( fp );
            LINK( ed, pRoomIndex->first_extradesc, pRoomIndex->last_extradesc, next, prev );
         }
         else if( letter == 'R' )
         {
            if( tarea->version < 1000 )
               load_room_reset( pRoomIndex, fp );
            else
               load_smaugwiz_reset( pRoomIndex, fp );
         }
         else if( letter == 'M' )   /* old map stuff no longer supported */
         {
            fread_to_eol( fp );
         }
         else if( letter == '>' )
         {
            ungetc( letter, fp );
            rprog_read_programs( fp, pRoomIndex );
         }
         else
         {
            bug( "Load_rooms: vnum %d has flag '%c' not 'DES'.", vnum, letter );
            shutdown_mud( "Room flag not DES" );
            exit( 1 );
         }

      }

      if( !oldroom )
      {
         iHash = vnum % MAX_KEY_HASH;
         pRoomIndex->next = room_index_hash[iHash];
         room_index_hash[iHash] = pRoomIndex;
         LINK( pRoomIndex, tarea->first_room, tarea->last_room, next_aroom, prev_aroom );
      }
   }
}

/*
 * Load a shop section.
 */
void load_shops( FILE * fp )
{
   SHOP_DATA *pShop;

   for( ;; )
   {
      MOB_INDEX_DATA *pMobIndex;
      int iTrade;

      CREATE( pShop, SHOP_DATA, 1 );
      pShop->keeper = fread_number( fp );
      if( pShop->keeper == 0 )
      {
         DISPOSE( pShop );
         break;
      }
      for( iTrade = 0; iTrade < MAX_TRADE; iTrade++ )
         pShop->buy_type[iTrade] = fread_number( fp );
      pShop->profit_buy = fread_number( fp );
      pShop->profit_sell = fread_number( fp );
      pShop->profit_buy = URANGE( pShop->profit_sell + 5, pShop->profit_buy, 1000 );
      pShop->profit_sell = URANGE( 0, pShop->profit_sell, pShop->profit_buy - 5 );
      pShop->open_hour = fread_number( fp );
      pShop->close_hour = fread_number( fp );
      fread_to_eol( fp );
      pMobIndex = get_mob_index( pShop->keeper );
      pMobIndex->pShop = pShop;
   }
}

/*
 * Load a repair shop section.					-Thoric
 */
void load_repairs( FILE * fp )
{
   REPAIR_DATA *rShop;

   for( ;; )
   {
      MOB_INDEX_DATA *pMobIndex;
      int iFix;

      CREATE( rShop, REPAIR_DATA, 1 );
      rShop->keeper = fread_number( fp );
      if( rShop->keeper == 0 )
      {
         DISPOSE( rShop );
         break;
      }
      for( iFix = 0; iFix < MAX_FIX; iFix++ )
         rShop->fix_type[iFix] = fread_number( fp );
      rShop->profit_fix = fread_number( fp );
      rShop->shop_type = fread_number( fp );
      rShop->open_hour = fread_number( fp );
      rShop->close_hour = fread_number( fp );
      fread_to_eol( fp );
      pMobIndex = get_mob_index( rShop->keeper );
      pMobIndex->rShop = rShop;
   }
}

/*
 * Load spec proc declarations.
 */
void load_specials( FILE * fp )
{
   for( ;; )
   {
      MOB_INDEX_DATA *pMobIndex;
      char letter;

      switch ( letter = fread_letter( fp ) )
      {
         default:
            bug( "Load_specials: letter '%c' not *MS.", letter );
            exit( 1 );

         case 'S':
            return;

         case '*':
            break;

         case 'M':
         {
            char *temp;
            pMobIndex = get_mob_index( fread_number( fp ) );
            temp = fread_word( fp );
            pMobIndex->spec_funname = STRALLOC( temp );
         }
            break;
      }
      fread_to_eol( fp );
   }
}

/*
 * Load soft / hard area ranges.
 */
void load_ranges( AREA_DATA * tarea, FILE * fp )
{
   int x1, x2, x3, x4;
   char *ln;

   if( !tarea )
   {
      bug( "Load_ranges: no #AREA seen yet." );
      shutdown_mud( "No #AREA" );
      exit( 1 );
   }

   for( ;; )
   {
      ln = fread_line( fp );

      if( ln[0] == '$' )
         break;

      x1 = x2 = x3 = x4 = 0;
      sscanf( ln, "%d %d %d %d", &x1, &x2, &x3, &x4 );

      tarea->low_soft_range = x1;
      tarea->hi_soft_range = x2;
      tarea->low_hard_range = x3;
      tarea->hi_hard_range = x4;
   }
   return;

}

/*
 * With the new Weather System, these are unneeded as the weather is it's own
 * entity seperated from everything else. - Kayle 10-17-07
 */
void load_climate( AREA_DATA * tarea, FILE * fp )
{
   fread_number( fp );
   fread_number( fp );
   fread_number( fp );
}

/*
 * With the new Weather System, these are unneeded as the weather is it's own
 * entity seperated from everything else. - Kayle 10-17-07
 */
void load_neighbor( AREA_DATA * tarea, FILE * fp )
{
   fread_flagstring( fp );
}

/*
 * Get an extra description from a list.
 */
const char *get_extra_descr( const char *name, EXTRA_DESCR_DATA * ed )
{
   for( ; ed; ed = ed->next )
      if( is_name( name, ed->keyword ) )
         return ed->description;

   return NULL;
}

/*
 * Translates mob virtual number to its mob index struct.
 * Hash table lookup.
 */
MOB_INDEX_DATA *get_mob_index( int vnum )
{
   MOB_INDEX_DATA *pMobIndex;

   if( vnum < 0 )
      vnum = 0;

   for( pMobIndex = mob_index_hash[vnum % MAX_KEY_HASH]; pMobIndex; pMobIndex = pMobIndex->next )
      if( pMobIndex->vnum == vnum )
         return pMobIndex;

   return NULL;
}

/*
 * Translates obj virtual number to its obj index struct.
 * Hash table lookup.
 */
OBJ_INDEX_DATA *get_obj_index( int vnum )
{
   OBJ_INDEX_DATA *pObjIndex;

   if( vnum < 0 )
      vnum = 0;

   for( pObjIndex = obj_index_hash[vnum % MAX_KEY_HASH]; pObjIndex; pObjIndex = pObjIndex->next )
      if( pObjIndex->vnum == vnum )
         return pObjIndex;

   return NULL;
}

/*
 * Translates room virtual number to its room index struct.
 * Hash table lookup.
 */
ROOM_INDEX_DATA *get_room_index( int vnum )
{
   ROOM_INDEX_DATA *pRoomIndex;

   if( vnum < 0 )
      vnum = 0;

   for( pRoomIndex = room_index_hash[vnum % MAX_KEY_HASH]; pRoomIndex; pRoomIndex = pRoomIndex->next )
      if( pRoomIndex->vnum == vnum )
         return pRoomIndex;

   return NULL;
}

/*
 * Added lots of EOF checks, as most of the file crashes are based on them.
 * If an area file encounters EOF, the fread_* functions will shutdown the
 * MUD, as all area files should be read in in full or bad things will
 * happen during the game.  Any files loaded in without fBootDb which
 * encounter EOF will return what they have read so far.   These files
 * should include player files, and in-progress areas that are not loaded
 * upon bootup.
 * -- Altrag
 */

/*
 * Read a letter from a file.
 */
char fread_letter( FILE * fp )
{
   char c;

   do
   {
      if( feof( fp ) )
      {
         bug( "fread_letter: EOF encountered on read.\r\n" );
         exit( 1 );
         return '\0';
      }
      c = getc( fp );
   }
   while( isspace( c ) );

   return c;
}

/*
 * Read a number from a file.
 */
int fread_number( FILE * fp )
{
   int number;
   bool sign;
   char c;

   do
   {
      if( feof( fp ) )
      {
         bug( "fread_number: EOF encountered on read.\r\n" );
         exit( 1 );
         return 0;
      }
      c = getc( fp );
   }
   while( isspace( c ) );

   number = 0;

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

   if( !isdigit( c ) )
   {
      bug( "Fread_number: bad format. (%c)", c );
      exit( 1 );
      return 0;
   }

   while( isdigit( c ) )
   {
      if( feof( fp ) )
      {
         bug( "fread_number: EOF encountered on read.\r\n" );
         exit( 1 );
         return number;
      }
      number = number * 10 + c - '0';
      c = getc( fp );
   }

   if( sign )
      number = 0 - number;

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

   return number;
}

/*
 * custom str_dup using create					-Thoric
 */
char *str_dup( char const *str )
{
   static char *ret;
   int len;

   if( !str )
      return NULL;

   len = strlen( str ) + 1;

   CREATE( ret, char, len );
   mudstrlcpy( ret, str, MAX_STRING_LENGTH );
   return ret;
}

/* Read a string from file and return it */
const char *fread_flagstring( FILE * fp )
{
   static char flagstring[MAX_STRING_LENGTH];
   char *plast;
   char c;
   int ln;

   plast = flagstring;
   flagstring[0] = '\0';
   ln = 0;
   /*
    * Skip blanks. Read first char. 
    */
   do
   {
      if( feof( fp ) )
      {
         bug( "%s: EOF encountered on read.", __FUNCTION__ );
         exit( 1 );
         return "";
      }
      c = getc( fp );
   }
   while( isspace( c ) );
   if( ( *plast++ = c ) == '~' )
      return "";

   for( ;; )
   {
      if( ln >= ( MAX_STRING_LENGTH - 1 ) )
      {
         bug( "%s: string too long", __FUNCTION__ );
         *plast = '\0';
         return flagstring;
      }
      switch ( *plast = getc( fp ) )
      {
         default:
            plast++;
            ln++;
            break;

         case EOF:
            bug( "%s: EOF", __FUNCTION__ );
            exit( 1 );
            *plast = '\0';
            return flagstring;
            break;

         case '\n':
            plast++;
            ln++;
            *plast++ = '\r';
            ln++;
            break;

         case '\r':
            break;

         case '~':
            *plast = '\0';
            return flagstring;
      }
   }
}

/*
 * Read a string from file fp
 */
const char *fread_string( FILE * fp )
{
   char buf[MAX_STRING_LENGTH];
   char *plast;
   char c;
   int ln;

   plast = buf;
   buf[0] = '\0';
   ln = 0;

   /*
    * Skip blanks.
    * Read first char.
    */
   do
   {
      if( feof( fp ) )
      {
         bug( "fread_string: EOF encountered on read.\r\n" );
         exit( 1 );
         return STRALLOC( "" );
      }
      c = getc( fp );
   }
   while( isspace( c ) );

   if( ( *plast++ = c ) == '~' )
      return STRALLOC( "" );

   for( ;; )
   {
      if( ln >= ( MAX_STRING_LENGTH - 1 ) )
      {
         bug( "fread_string: string too long" );
         *plast = '\0';
         return STRALLOC( buf );
      }
      switch ( *plast = getc( fp ) )
      {
         default:
            plast++;
            ln++;
            break;

         case EOF:
            bug( "Fread_string: EOF" );
            exit( 1 );
            *plast = '\0';
            return STRALLOC( buf );
            break;

         case '\n':
            plast++;
            ln++;
            *plast++ = '\r';
            ln++;
            break;

         case '\r':
            break;

         case '~':
            *plast = '\0';
            return STRALLOC( buf );
      }
   }
}

/*
 * Read a string from file fp using str_dup (ie: no string hashing)
 */
char *fread_string_nohash( FILE * fp )
{
   char buf[MAX_STRING_LENGTH];
   char *plast;
   char c;
   int ln;

   plast = buf;
   buf[0] = '\0';
   ln = 0;

   /*
    * Skip blanks.
    * Read first char.
    */
   do
   {
      if( feof( fp ) )
      {
         bug( "fread_string_no_hash: EOF encountered on read.\r\n" );
         exit( 1 );
         return str_dup( "" );
      }
      c = getc( fp );
   }
   while( isspace( c ) );

   if( ( *plast++ = c ) == '~' )
      return str_dup( "" );

   for( ;; )
   {
      if( ln >= ( MAX_STRING_LENGTH - 1 ) )
      {
         bug( "fread_string_no_hash: string too long" );
         *plast = '\0';
         return str_dup( buf );
      }
      switch ( *plast = getc( fp ) )
      {
         default:
            plast++;
            ln++;
            break;

         case EOF:
            bug( "Fread_string_no_hash: EOF" );
            exit( 1 );
            *plast = '\0';
            return str_dup( buf );
            break;

         case '\n':
            plast++;
            ln++;
            *plast++ = '\r';
            ln++;
            break;

         case '\r':
            break;

         case '~':
            *plast = '\0';
            return str_dup( buf );
      }
   }
}

/*
 * Read to end of line (for comments).
 */
void fread_to_eol( FILE * fp )
{
   char c;

   do
   {
      if( feof( fp ) )
      {
         bug( "fread_to_eol: EOF encountered on read.\r\n" );
         exit( 1 );
         return;
      }
      c = getc( fp );
   }
   while( c != '\n' && c != '\r' );

   do
   {
      c = getc( fp );
   }
   while( c == '\n' || c == '\r' );

   ungetc( c, fp );
   return;
}

/*
 * Read to end of line into static buffer			-Thoric
 */
char *fread_line( FILE * fp )
{
   static char line[MAX_STRING_LENGTH];
   char *pline;
   char c;
   int ln;

   pline = line;
   line[0] = '\0';
   ln = 0;

   /*
    * Skip blanks.
    * Read first char.
    */
   do
   {
      if( feof( fp ) )
      {
         bug( "fread_line: EOF encountered on read.\r\n" );
         exit( 1 );
         mudstrlcpy( line, "", MAX_STRING_LENGTH );
         return line;
      }
      c = getc( fp );
   }
   while( isspace( c ) );

   ungetc( c, fp );
   do
   {
      if( feof( fp ) )
      {
         bug( "fread_line: EOF encountered on read.\r\n" );
         exit( 1 );
         *pline = '\0';
         return line;
      }
      c = getc( fp );
      *pline++ = c;
      ln++;
      if( ln >= ( MAX_STRING_LENGTH - 1 ) )
      {
         bug( "fread_line: line too long" );
         break;
      }
   }
   while( c != '\n' && c != '\r' );

   do
   {
      c = getc( fp );
   }
   while( c == '\n' || c == '\r' );

   ungetc( c, fp );
   *pline = '\0';
   return line;
}

/*
 * Read one word (into static buffer).
 */
char *fread_word( FILE * fp )
{
   static char word[MAX_INPUT_LENGTH];
   char *pword;
   char cEnd;

   do
   {
      if( feof( fp ) )
      {
         bug( "fread_word: EOF encountered on read.\r\n" );
         exit( 1 );
         word[0] = '\0';
         return word;
      }
      cEnd = getc( fp );
   }
   while( isspace( cEnd ) );

   if( cEnd == '\'' || cEnd == '"' )
   {
      pword = word;
   }
   else
   {
      word[0] = cEnd;
      pword = word + 1;
      cEnd = ' ';
   }

   for( ; pword < word + MAX_INPUT_LENGTH; pword++ )
   {
      if( feof( fp ) )
      {
         bug( "fread_word: EOF encountered on read.\r\n" );
         exit( 1 );
         word[0] = '\0';
         return word;
      }
      *pword = getc( fp );
      if( cEnd == ' ' ? isspace( *pword ) : *pword == cEnd )
      {
         if( cEnd == ' ' )
            ungetc( *pword, fp );
         *pword = '\0';
         return word;
      }
   }
   bug( "%s: word too long", __FUNCTION__ );
   return NULL;
}

/*
 * Generate a random number.
 * Ooops was (number_mm() % to) + from which doesn't work -Shaddai
 */
int number_range( int from, int to )
{
   if( ( to - from ) < 1 )
      return from;
   return ( ( number_mm(  ) % ( to - from + 1 ) ) + from );
}

/*
 * I've gotten too many bad reports on OS-supplied random number generators.
 * This is the Mitchell-Moore algorithm from Knuth Volume II.
 * Best to leave the constants alone unless you've read Knuth.
 * -- Furey
 */
static int rgiState[2 + 55];

void init_mm(  )
{
   int *piState;
   int iState;

   piState = &rgiState[2];

   piState[-2] = 55 - 55;
   piState[-1] = 55 - 24;

   piState[0] = ( ( int )current_time ) & ( ( 1 << 30 ) - 1 );
   piState[1] = 1;
   for( iState = 2; iState < 55; iState++ )
   {
      piState[iState] = ( piState[iState - 1] + piState[iState - 2] ) & ( ( 1 << 30 ) - 1 );
   }
   return;
}

int number_mm( void )
{
   int *piState;
   int iState1;
   int iState2;
   int iRand;

   piState = &rgiState[2];
   iState1 = piState[-2];
   iState2 = piState[-1];
   iRand = ( piState[iState1] + piState[iState2] ) & ( ( 1 << 30 ) - 1 );
   piState[iState1] = iRand;
   if( ++iState1 == 55 )
      iState1 = 0;
   if( ++iState2 == 55 )
      iState2 = 0;
   piState[-2] = iState1;
   piState[-1] = iState2;
   return iRand >> 6;
}

/*
 * Removes the tildes from a string.
 * Used for player-entered strings that go into disk files.
 */
void smash_tilde( char *str )
{
   for( ; *str != '\0'; str++ )
      if( *str == '~' )
         *str = '-';

   return;
}

/*
 * Compare strings, case insensitive.
 * Return true if different
 *   (compatibility with historical functions).
 */
bool str_cmp( const char *astr, const char *bstr )
{
   if( !astr )
   {
      bug( "Str_cmp: null astr." );
      if( bstr )
         fprintf( stderr, "str_cmp: astr: (null)  bstr: %s\n", bstr );
      return true;
   }

   if( !bstr )
   {
      bug( "Str_cmp: null bstr." );
      if( astr )
         fprintf( stderr, "str_cmp: astr: %s  bstr: (null)\n", astr );
      return true;
   }

   for( ; *astr || *bstr; astr++, bstr++ )
   {
      if( LOWER( *astr ) != LOWER( *bstr ) )
         return true;
   }

   return false;
}



/*
 * Compare strings, case insensitive, for prefix matching.
 * Return true if astr not a prefix of bstr
 *   (compatibility with historical functions).
 */
bool str_prefix( const char *astr, const char *bstr )
{
   if( !astr )
   {
      bug( "Strn_cmp: null astr." );
      return true;
   }

   if( !bstr )
   {
      bug( "Strn_cmp: null bstr." );
      return true;
   }

   for( ; *astr; astr++, bstr++ )
   {
      if( LOWER( *astr ) != LOWER( *bstr ) )
         return true;
   }

   return false;
}

/*
 * Returns true or false if a letter is a vowel			-Thoric
 */
bool isavowel( char letter )
{
   char c;

   c = LOWER( letter );
   if( c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' )
      return true;
   else
      return false;
}

/*
 * Reports a bug.
 */
void bug( const char *str, ... )
{
   char buf[MAX_STRING_LENGTH];
   FILE *fp;
   struct stat fst;

   mudstrlcpy( buf, "[*****] BUG: ", MAX_STRING_LENGTH );
   {
      va_list param;

      va_start( param, str );
      vsnprintf( buf + strlen( buf ), ( MAX_STRING_LENGTH - strlen( buf ) ), str, param );
      va_end( param );
   }
   log_string( buf );

   if( fpArea != NULL )
   {
      int iLine;
      int iChar;

      if( fpArea == stdin )
      {
         iLine = 0;
      }
      else
      {
         iChar = ftell( fpArea );
         fseek( fpArea, 0, 0 );
         for( iLine = 0; ftell( fpArea ) < iChar; iLine++ )
         {
            int letter;

            while( ( letter = getc( fpArea ) ) && letter != EOF && letter != '\n' )
               ;
         }
         fseek( fpArea, iChar, 0 );
      }

      log_printf( "[*****] FILE: %s LINE: %d", strArea, iLine );

      if( stat( SHUTDOWN_FILE, &fst ) != -1 )   /* file exists */
      {
         if( ( fp = fopen( SHUTDOWN_FILE, "a" ) ) != NULL )
         {
            fprintf( fp, "%s\n", buf );
            fprintf( fp, "[*****] FILE: %s LINE: %d\n", strArea, iLine );
            fclose( fp );
            fp = NULL;
         }
      }
   }
   return;
}

/*
 * Add a string to the boot-up log				-Thoric
 */
void boot_log( const char *str, ... )
{
   char buf[MAX_STRING_LENGTH];
   FILE *fp;
   va_list param;

   mudstrlcpy( buf, "[*****] BOOT: ", MAX_STRING_LENGTH );
   va_start( param, str );
   vsnprintf( buf + strlen( buf ), ( MAX_STRING_LENGTH - strlen( buf ) ), str, param );
   va_end( param );
   log_string( buf );

   if( ( fp = fopen( BOOTLOG_FILE, "a" ) ) != NULL )
   {
      fprintf( fp, "%s\n", buf );
      fclose( fp );
   }
   return;
}

/*
 * Writes a string to the log, extended version			-Thoric
 */
void log_string_plus( const char *str, short log_type, short level )
{
   char *strtime;
   struct timeval now_time;

   /*
    * Update time. 
    */
   gettimeofday( &now_time, NULL );
   current_time = ( time_t ) now_time.tv_sec;

   strtime = ctime( &current_time );
   strtime[strlen( strtime ) - 1] = '\0';
   fprintf( stderr, "%s :: %s\n", strtime, str );
}

void log_printf_plus( short log_type, short level, const char *fmt, ... )
{
   char buf[MAX_STRING_LENGTH * 2];
   va_list args;

   va_start( args, fmt );
   vsnprintf( buf, MAX_STRING_LENGTH * 2, fmt, args );
   va_end( args );

   log_string_plus( buf, log_type, level );
}

void log_printf( const char *fmt, ... )
{
   char buf[MAX_STRING_LENGTH * 2];
   va_list args;

   va_start( args, fmt );
   vsnprintf( buf, MAX_STRING_LENGTH * 2, fmt, args );
   va_end( args );

   log_string_plus( buf, LOG_NORMAL, LEVEL_LOG );
}

/* mud prog functions */

/* This routine reads in scripts of MUDprograms from a file */
int mprog_name_to_type( const char *name )
{
   if( !str_cmp( name, "in_file_prog" ) )
      return IN_FILE_PROG;
   if( !str_cmp( name, "act_prog" ) )
      return ACT_PROG;
   if( !str_cmp( name, "speech_prog" ) )
      return SPEECH_PROG;
   if( !str_cmp( name, "rand_prog" ) )
      return RAND_PROG;
   if( !str_cmp( name, "fight_prog" ) )
      return FIGHT_PROG;
   if( !str_cmp( name, "hitprcnt_prog" ) )
      return HITPRCNT_PROG;
   if( !str_cmp( name, "death_prog" ) )
      return DEATH_PROG;
   if( !str_cmp( name, "entry_prog" ) )
      return ENTRY_PROG;
   if( !str_cmp( name, "greet_prog" ) )
      return GREET_PROG;
   if( !str_cmp( name, "all_greet_prog" ) )
      return ALL_GREET_PROG;
   if( !str_cmp( name, "give_prog" ) )
      return GIVE_PROG;
   if( !str_cmp( name, "bribe_prog" ) )
      return BRIBE_PROG;
   if( !str_cmp( name, "time_prog" ) )
      return TIME_PROG;
   if( !str_cmp( name, "hour_prog" ) )
      return HOUR_PROG;
   if( !str_cmp( name, "wear_prog" ) )
      return WEAR_PROG;
   if( !str_cmp( name, "remove_prog" ) )
      return REMOVE_PROG;
   if( !str_cmp( name, "sac_prog" ) )
      return SAC_PROG;
   if( !str_cmp( name, "look_prog" ) )
      return LOOK_PROG;
   if( !str_cmp( name, "exa_prog" ) )
      return EXA_PROG;
   if( !str_cmp( name, "zap_prog" ) )
      return ZAP_PROG;
   if( !str_cmp( name, "get_prog" ) )
      return GET_PROG;
   if( !str_cmp( name, "drop_prog" ) )
      return DROP_PROG;
   if( !str_cmp( name, "damage_prog" ) )
      return DAMAGE_PROG;
   if( !str_cmp( name, "repair_prog" ) )
      return REPAIR_PROG;
   if( !str_cmp( name, "greet_prog" ) )
      return GREET_PROG;
   if( !str_cmp( name, "randiw_prog" ) )
      return RANDIW_PROG;
   if( !str_cmp( name, "speechiw_prog" ) )
      return SPEECHIW_PROG;
   if( !str_cmp( name, "pull_prog" ) )
      return PULL_PROG;
   if( !str_cmp( name, "push_prog" ) )
      return PUSH_PROG;
   if( !str_cmp( name, "sleep_prog" ) )
      return SLEEP_PROG;
   if( !str_cmp( name, "rest_prog" ) )
      return REST_PROG;
   if( !str_cmp( name, "rfight_prog" ) )
      return FIGHT_PROG;
   if( !str_cmp( name, "enter_prog" ) )
      return ENTRY_PROG;
   if( !str_cmp( name, "leave_prog" ) )
      return LEAVE_PROG;
   if( !str_cmp( name, "rdeath_prog" ) )
      return DEATH_PROG;
   if( !str_cmp( name, "script_prog" ) )
      return SCRIPT_PROG;
   if( !str_cmp( name, "use_prog" ) )
      return USE_PROG;
   return ( ERROR_PROG );
}

void mobprog_file_read( MOB_INDEX_DATA * mob, const char *f )
{
   MPROG_DATA *mprg = NULL;
   char MUDProgfile[256];
   FILE *progfile;
   char letter;

   snprintf( MUDProgfile, 256, "%s%s", PROG_DIR, f );

   if( !( progfile = fopen( MUDProgfile, "r" ) ) )
   {
      bug( "%s: couldn't open mudprog file", __FUNCTION__ );
      return;
   }

   for( ;; )
   {
      letter = fread_letter( progfile );

      if( letter == '|' )
         break;

      if( letter != '>' )
      {
         bug( "%s: MUDPROG char", __FUNCTION__ );
         break;
      }

      CREATE( mprg, MPROG_DATA, 1 );
      mprg->type = mprog_name_to_type( fread_word( progfile ) );
      switch ( mprg->type )
      {
         case ERROR_PROG:
            bug( "%s: mudprog file type error", __FUNCTION__ );
            DISPOSE( mprg );
            continue;

         case IN_FILE_PROG:
            bug( "%s: Nested file programs are not allowed.", __FUNCTION__ );
            DISPOSE( mprg );
            continue;

         default:
            mprg->arglist = fread_string( progfile );
            mprg->comlist = fread_string( progfile );
            mprg->fileprog = true;
            xSET_BIT( mob->progtypes, mprg->type );
            mprg->next = mob->mudprogs;
            mob->mudprogs = mprg;
            break;
      }
   }
   fclose( progfile );
   progfile = NULL;
   return;
}

/* This procedure is responsible for reading any in_file MUDprograms.
 */
void mprog_read_programs( FILE * fp, MOB_INDEX_DATA * mob )
{
   MPROG_DATA *mprg;
   char letter;
   char *word;

   for( ;; )
   {
      letter = fread_letter( fp );

      if( letter == '|' )
         return;

      if( letter != '>' )
      {
         bug( "%s: vnum %d MUDPROG char", __FUNCTION__, mob->vnum );
         exit( 1 );
      }
      CREATE( mprg, MPROG_DATA, 1 );
      mprg->next = mob->mudprogs;
      mob->mudprogs = mprg;

      word = fread_word( fp );
      mprg->type = mprog_name_to_type( word );

      switch ( mprg->type )
      {
         case ERROR_PROG:
            bug( "%s: vnum %d MUDPROG type.", __FUNCTION__, mob->vnum );
            exit( 1 );

         case IN_FILE_PROG:
            mprg->arglist = fread_string( fp );
            mprg->fileprog = false;
            mobprog_file_read( mob, mprg->arglist );
            break;

         default:
            xSET_BIT( mob->progtypes, mprg->type );
            mprg->fileprog = false;
            mprg->arglist = fread_string( fp );
            mprg->comlist = fread_string( fp );
            break;
      }
   }
   return;
}

/*************************************************************/
/* obj prog functions */
/* This routine transfers between alpha and numeric forms of the
 *  mob_prog bitvector types. This allows the use of the words in the
 *  mob/script files.
 */

/* This routine reads in scripts of OBJprograms from a file */
void objprog_file_read( OBJ_INDEX_DATA * obj, const char *f )
{
   MPROG_DATA *mprg = NULL;
   char MUDProgfile[256];
   FILE *progfile;
   char letter;

   snprintf( MUDProgfile, 256, "%s%s", PROG_DIR, f );

   if( !( progfile = fopen( MUDProgfile, "r" ) ) )
   {
      bug( "%s: couldn't open mudprog file", __FUNCTION__ );
      return;
   }

   for( ;; )
   {
      letter = fread_letter( progfile );

      if( letter == '|' )
         break;

      if( letter != '>' )
      {
         bug( "%s: MUDPROG char", __FUNCTION__ );
         break;
      }

      CREATE( mprg, MPROG_DATA, 1 );
      mprg->type = mprog_name_to_type( fread_word( progfile ) );
      switch ( mprg->type )
      {
         case ERROR_PROG:
            bug( "%s: mudprog file type error", __FUNCTION__ );
            DISPOSE( mprg );
            continue;

         case IN_FILE_PROG:
            bug( "%s: Nested file programs are not allowed.", __FUNCTION__ );
            DISPOSE( mprg );
            continue;

         default:
            mprg->arglist = fread_string( progfile );
            mprg->comlist = fread_string( progfile );
            mprg->fileprog = true;
            xSET_BIT( obj->progtypes, mprg->type );
            mprg->next = obj->mudprogs;
            obj->mudprogs = mprg;
            break;
      }
   }
   fclose( progfile );
   progfile = NULL;
   return;
}

/* This procedure is responsible for reading any in_file OBJprograms.
 */
void oprog_read_programs( FILE * fp, OBJ_INDEX_DATA * obj )
{
   MPROG_DATA *mprg;
   char letter;
   char *word;

   for( ;; )
   {
      letter = fread_letter( fp );

      if( letter == '|' )
         return;

      if( letter != '>' )
      {
         bug( "%s: vnum %d MUDPROG char", __FUNCTION__, obj->vnum );
         exit( 1 );
      }
      CREATE( mprg, MPROG_DATA, 1 );
      mprg->next = obj->mudprogs;
      obj->mudprogs = mprg;

      word = fread_word( fp );
      mprg->type = mprog_name_to_type( word );

      switch ( mprg->type )
      {
         case ERROR_PROG:
            bug( "%s: vnum %d MUDPROG type.", __FUNCTION__, obj->vnum );
            exit( 1 );

         case IN_FILE_PROG:
            mprg->arglist = fread_string( fp );
            mprg->fileprog = false;
            objprog_file_read( obj, mprg->arglist );
            break;

         default:
            xSET_BIT( obj->progtypes, mprg->type );
            mprg->fileprog = false;
            mprg->arglist = fread_string( fp );
            mprg->comlist = fread_string( fp );
            break;
      }
   }
   return;
}

/*************************************************************/
/* room prog functions */
/* This routine transfers between alpha and numeric forms of the
 *  mob_prog bitvector types. This allows the use of the words in the
 *  mob/script files.
 */

/* This routine reads in scripts of OBJprograms from a file */
void roomprog_file_read( ROOM_INDEX_DATA * room, const char *f )
{
   MPROG_DATA *mprg = NULL;
   char MUDProgfile[256];
   FILE *progfile;
   char letter;

   snprintf( MUDProgfile, 256, "%s%s", PROG_DIR, f );

   if( !( progfile = fopen( MUDProgfile, "r" ) ) )
   {
      bug( "%s: couldn't open mudprog file", __FUNCTION__ );
      return;
   }

   for( ;; )
   {
      letter = fread_letter( progfile );

      if( letter == '|' )
         break;

      if( letter != '>' )
      {
         bug( "%s: MUDPROG char", __FUNCTION__ );
         break;
      }

      CREATE( mprg, MPROG_DATA, 1 );
      mprg->type = mprog_name_to_type( fread_word( progfile ) );
      switch ( mprg->type )
      {
         case ERROR_PROG:
            bug( "%s: mudprog file type error", __FUNCTION__ );
            DISPOSE( mprg );
            continue;

         case IN_FILE_PROG:
            bug( "%s: Nested file programs are not allowed.", __FUNCTION__ );
            DISPOSE( mprg );
            continue;

         default:
            mprg->arglist = fread_string( progfile );
            mprg->comlist = fread_string( progfile );
            mprg->fileprog = true;
            xSET_BIT( room->progtypes, mprg->type );
            mprg->next = room->mudprogs;
            room->mudprogs = mprg;
            break;
      }
   }
   fclose( progfile );
   progfile = NULL;
   return;
}

/* This procedure is responsible for reading any in_file ROOMprograms.
 */
void rprog_read_programs( FILE * fp, ROOM_INDEX_DATA * room )
{
   MPROG_DATA *mprg;
   char letter;
   char *word;

   for( ;; )
   {
      letter = fread_letter( fp );

      if( letter == '|' )
         return;

      if( letter != '>' )
      {
         bug( "%s: vnum %d MUDPROG char", __FUNCTION__, room->vnum );
         exit( 1 );
      }
      CREATE( mprg, MPROG_DATA, 1 );
      mprg->next = room->mudprogs;
      room->mudprogs = mprg;

      word = fread_word( fp );
      mprg->type = mprog_name_to_type( word );

      switch ( mprg->type )
      {
         case ERROR_PROG:
            bug( "%s: vnum %d MUDPROG type.", __FUNCTION__, room->vnum );
            exit( 1 );

         case IN_FILE_PROG:
            mprg->arglist = fread_string( fp );
            mprg->fileprog = false;
            roomprog_file_read( room, mprg->arglist );
            break;

         default:
            xSET_BIT( room->progtypes, mprg->type );
            mprg->fileprog = false;
            mprg->arglist = fread_string( fp );
            mprg->comlist = fread_string( fp );
            break;
      }
   }
   return;
}

/*
 * Creates a simple exit with no fields filled but rvnum and optionally
 * to_room and vnum.						-Thoric
 * Exits are inserted into the linked list based on vdir.
 */
EXIT_DATA *make_exit( ROOM_INDEX_DATA * pRoomIndex, ROOM_INDEX_DATA * to_room, short door )
{
   EXIT_DATA *pexit, *texit;
   bool broke;

   CREATE( pexit, EXIT_DATA, 1 );
   pexit->vdir = door;
   pexit->rvnum = pRoomIndex->vnum;
   pexit->to_room = to_room;
   pexit->distance = 1;
   pexit->key = -1;
   if( to_room )
   {
      pexit->vnum = to_room->vnum;
      texit = get_exit_to( to_room, rev_dir[door], pRoomIndex->vnum );
      if( texit ) /* assign reverse exit pointers */
      {
         texit->rexit = pexit;
         pexit->rexit = texit;
      }
   }
   broke = false;
   for( texit = pRoomIndex->first_exit; texit; texit = texit->next )
      if( door < texit->vdir )
      {
         broke = true;
         break;
      }
   if( !pRoomIndex->first_exit )
      pRoomIndex->first_exit = pexit;
   else
   {
      /*
       * keep exits in incremental order - insert exit into list 
       */
      if( broke && texit )
      {
         if( !texit->prev )
            pRoomIndex->first_exit = pexit;
         else
            texit->prev->next = pexit;
         pexit->prev = texit->prev;
         pexit->next = texit;
         texit->prev = pexit;
         return pexit;
      }
      pRoomIndex->last_exit->next = pexit;
   }
   pexit->next = NULL;
   pexit->prev = pRoomIndex->last_exit;
   pRoomIndex->last_exit = pexit;
   return pexit;
}

void process_sorting( AREA_DATA * tarea )
{
   fprintf( stderr, "%-14s: Rooms: %5d - %-5d Objs: %5d - %-5d Mobs: %5d - %d\n",
      tarea->filename, tarea->low_r_vnum, tarea->hi_r_vnum, tarea->low_o_vnum, tarea->hi_o_vnum, tarea->low_m_vnum, tarea->hi_m_vnum );
   if( !tarea->author )
      tarea->author = STRALLOC( "" );
}

EXTRA_DESCR_DATA *fread_fuss_exdesc( FILE * fp )
{
   EXTRA_DESCR_DATA *ed;
   bool fMatch;   // Unused, but needed to shut the compiler up about the KEY macro

   CREATE( ed, EXTRA_DESCR_DATA, 1 );

   for( ;; )
   {
      const char *word = ( feof( fp ) ? "#ENDEXDESC" : fread_word( fp ) );

      if( word[0] == '\0' )
      {
         log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
         word = "#ENDEXDESC";
      }

      switch ( word[0] )
      {
         default:
            log_printf( "%s: no match: %s", __FUNCTION__, word );
            fread_to_eol( fp );
            break;

         case '#':
            if( !str_cmp( word, "#ENDEXDESC" ) )
            {
               if( !ed->keyword )
               {
                  bug( "%s: Missing ExDesc keyword. Returning NULL.", __FUNCTION__ );
                  STRFREE( ed->description );
                  DISPOSE( ed );
                  return NULL;
               }

               if( !ed->description )
                  ed->description = STRALLOC( "" );

               return ed;
            }
            break;

         case 'E':
            KEY( "ExDescKey", ed->keyword, fread_string( fp ) );
            KEY( "ExDesc", ed->description, fread_string( fp ) );
            break;
      }
   }

   // Reach this point, you fell through somehow. The data is no longer valid.
   bug( "%s: Reached fallout point! ExtraDesc data invalid.", __FUNCTION__ );
   DISPOSE( ed );
   return NULL;
}

AFFECT_DATA *fread_fuss_affect( FILE * fp, const char *word )
{
   AFFECT_DATA *paf;
   int pafmod;

   CREATE( paf, AFFECT_DATA, 1 );
   if( !strcmp( word, "Affect" ) )
   {
      paf->type = fread_number( fp );
   }
   else
   {
      int sn;

      sn = skill_lookup( fread_word( fp ) );
      if( sn < 0 )
         bug( "%s: unknown skill.", __FUNCTION__ );
      else
         paf->type = sn;
   }
   paf->duration = fread_number( fp );
   pafmod = fread_number( fp );
   paf->location = fread_number( fp );
   paf->bitvector = fread_bitvector( fp );

   if( paf->location == APPLY_WEAPONSPELL
       || paf->location == APPLY_WEARSPELL
       || paf->location == APPLY_STRIPSN || paf->location == APPLY_REMOVESPELL || paf->location == APPLY_RECURRINGSPELL )
      paf->modifier = slot_lookup( pafmod );
   else
      paf->modifier = pafmod;

   return paf;
}

void fread_fuss_exit( FILE * fp, ROOM_INDEX_DATA * pRoomIndex )
{
   EXIT_DATA *pexit = NULL;
   bool fMatch;   // Unused, but needed to shut the compiler up about the KEY macro

   for( ;; )
   {
      const char *word = ( feof( fp ) ? "#ENDEXIT" : fread_word( fp ) );

      if( word[0] == '\0' )
      {
         log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
         word = "#ENDEXIT";
      }

      switch ( word[0] )
      {
         default:
            log_printf( "%s: no match: %s", __FUNCTION__, word );
            fread_to_eol( fp );
            break;

         case '#':
            if( !str_cmp( word, "#ENDEXIT" ) )
            {
               if( !pexit->description )
                  pexit->description = STRALLOC( "" );
               if( !pexit->keyword )
                  pexit->keyword = STRALLOC( "" );

               return;
            }
            break;

         case 'D':
            KEY( "Desc", pexit->description, fread_string( fp ) );
            KEY( "Distance", pexit->distance, fread_number( fp ) );
            if( !str_cmp( word, "Direction" ) )
            {
               int door = get_dir( fread_flagstring( fp ) );

               if( door < 0 || door > DIR_SOMEWHERE )
               {
                  bug( "%s: vnum %d has bad door number %d.", __FUNCTION__, pRoomIndex->vnum, door );
                  return;
               }
               pexit = make_exit( pRoomIndex, NULL, door );
            }
            break;

         case 'F':
            if( !str_cmp( word, "Flags" ) )
            {
               const char *exitflags = NULL;
               char flag[MAX_INPUT_LENGTH];
               int value;

               exitflags = fread_flagstring( fp );

               while( exitflags[0] != '\0' )
               {
                  exitflags = one_argument( exitflags, flag );
                  value = get_exflag( flag );
                  if( value < 0 || value > 31 )
                     bug( "Unknown exitflag: %s", flag );
                  else
                     SET_BIT( pexit->exit_info, 1 << value );
               }
               break;
            }
            break;

         case 'K':
            KEY( "Key", pexit->key, fread_number( fp ) );
            KEY( "Keywords", pexit->keyword, fread_string( fp ) );
            break;

         case 'P':
            if( !str_cmp( word, "Pull" ) )
            {
               pexit->pulltype = fread_number( fp );
               pexit->pull = fread_number( fp );
               break;
            }
            break;

         case 'T':
            KEY( "ToRoom", pexit->vnum, fread_number( fp ) );
            break;
      }
   }

   // Reach this point, you fell through somehow. The data is no longer valid.
   bug( "%s: Reached fallout point! Exit data invalid.", __FUNCTION__ );
   if( pexit )
      extract_exit( pRoomIndex, pexit );
}

void rprog_file_read( ROOM_INDEX_DATA * prog_target, const char *f )
{
   MPROG_DATA *mprg = NULL;
   char MUDProgfile[256];
   FILE *progfile;
   char letter;
   bool fMatch;   // Unused, but needed to shut the compiler up about the KEY macro

   snprintf( MUDProgfile, 256, "%s%s", PROG_DIR, f );

   if( !( progfile = fopen( MUDProgfile, "r" ) ) )
   {
      bug( "%s: couldn't open mudprog file", __FUNCTION__ );
      return;
   }

   for( ;; )
   {
      letter = fread_letter( progfile );

      if( letter != '#' )
      {
         bug( "%s: MUDPROG char", __FUNCTION__ );
         break;
      }

      const char *word = ( feof( progfile ) ? "ENDFILE" : fread_word( progfile ) );

      if( word[0] == '\0' )
      {
         log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
         word = "ENDFILE";
      }

      if( !str_cmp( word, "ENDFILE" ) )
         break;

      if( !str_cmp( word, "MUDPROG" ) )
      {
         CREATE( mprg, MPROG_DATA, 1 );

         for( ;; )
         {
            word = ( feof( progfile ) ? "#ENDPROG" : fread_word( progfile ) );

            if( word[0] == '\0' )
            {
               log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
               word = "#ENDPROG";
            }

            if( !str_cmp( word, "#ENDPROG" ) )
            {
               mprg->next = prog_target->mudprogs;
               prog_target->mudprogs = mprg;
               break;
            }

            switch ( word[0] )
            {
               default:
                  log_printf( "%s: no match: %s", __FUNCTION__, word );
                  fread_to_eol( progfile );
                  break;

               case 'A':
                  if( !str_cmp( word, "Arglist" ) )
                  {
                     mprg->arglist = fread_string( progfile );
                     mprg->fileprog = true;

                     switch ( mprg->type )
                     {
                        case IN_FILE_PROG:
                           bug( "%s: Nested file programs are not allowed.", __FUNCTION__ );
                           DISPOSE( mprg );
                           break;

                        default:
                           break;
                     }
                     break;
                  }
                  break;

               case 'C':
                  KEY( "Comlist", mprg->comlist, fread_string( progfile ) );
                  break;

               case 'P':
                  if( !str_cmp( word, "Progtype" ) )
                  {
                     mprg->type = mprog_name_to_type( fread_flagstring( progfile ) );
                     break;
                  }
                  break;
            }
         }
      }
   }
   fclose( progfile );
   progfile = NULL;
   return;
}

void fread_fuss_roomprog( FILE * fp, MPROG_DATA * mprg, ROOM_INDEX_DATA * prog_target )
{
   bool fMatch;   // Unused, but needed to shut the compiler up about the KEY macro

   for( ;; )
   {
      const char *word = ( feof( fp ) ? "#ENDPROG" : fread_word( fp ) );

      if( word[0] == '\0' )
      {
         log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
         word = "#ENDPROG";
      }

      if( !str_cmp( word, "#ENDPROG" ) )
         return;

      switch ( word[0] )
      {
         default:
            log_printf( "%s: no match: %s", __FUNCTION__, word );
            fread_to_eol( fp );
            break;

         case 'A':
            if( !str_cmp( word, "Arglist" ) )
            {
               mprg->arglist = fread_string( fp );
               mprg->fileprog = false;

               switch ( mprg->type )
               {
                  case IN_FILE_PROG:
                     rprog_file_read( prog_target, mprg->arglist );
                     break;
                  default:
                     break;
               }
               break;
            }
            break;

         case 'C':
            KEY( "Comlist", mprg->comlist, fread_string( fp ) );
            break;

         case 'P':
            if( !str_cmp( word, "Progtype" ) )
            {
               mprg->type = mprog_name_to_type( fread_flagstring( fp ) );
               xSET_BIT( prog_target->progtypes, mprg->type );
               break;
            }
            break;
      }
   }
}

void fread_fuss_room( FILE * fp, AREA_DATA * tarea )
{
   ROOM_INDEX_DATA *pRoomIndex = NULL;
   bool oldroom = false;
   bool fMatch;   // Unused, but needed to shut the compiler up about the KEY macro

   for( ;; )
   {
      const char *word = ( feof( fp ) ? "#ENDROOM" : fread_word( fp ) );

      if( word[0] == '\0' )
      {
         log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
         word = "#ENDROOM";
      }

      switch ( word[0] )
      {
         default:
            bug( "%s: no match: %s", __FUNCTION__, word );
            fread_to_eol( fp );
            break;

         case '#':
            if( !str_cmp( word, "#ENDROOM" ) )
            {
               if( !pRoomIndex->description )
                  pRoomIndex->description = STRALLOC( "" );

               if( !oldroom )
               {
                  int iHash = pRoomIndex->vnum % MAX_KEY_HASH;
                  pRoomIndex->next = room_index_hash[iHash];
                  room_index_hash[iHash] = pRoomIndex;
                  LINK( pRoomIndex, tarea->first_room, tarea->last_room, next_aroom, prev_aroom );
               }
               return;
            }

            if( !str_cmp( word, "#EXIT" ) )
            {
               fread_fuss_exit( fp, pRoomIndex );
               break;
            }

            if( !str_cmp( word, "#EXDESC" ) )
            {
               EXTRA_DESCR_DATA *ed = fread_fuss_exdesc( fp );

               if( ed )
                  LINK( ed, pRoomIndex->first_extradesc, pRoomIndex->last_extradesc, next, prev );
               break;
            }

            if( !str_cmp( word, "#MUDPROG" ) )
            {
               MPROG_DATA *mprg;

               CREATE( mprg, MPROG_DATA, 1 );
               fread_fuss_roomprog( fp, mprg, pRoomIndex );
               mprg->next = pRoomIndex->mudprogs;
               pRoomIndex->mudprogs = mprg;
               break;
            }
            break;

         case 'A':
            if( !str_cmp( word, "Affect" ) || !str_cmp( word, "AffectData" ) )
            {
               AFFECT_DATA *af = fread_fuss_affect( fp, word );

               if( af )
                  LINK( af, pRoomIndex->first_permaffect, pRoomIndex->last_permaffect, next, prev );
               break;
            }
            break;

         case 'D':
            KEY( "Desc", pRoomIndex->description, fread_string( fp ) );
            break;

         case 'F':
            if( !str_cmp( word, "Flags" ) )
            {
               const char *roomflags = NULL;
               char flag[MAX_INPUT_LENGTH];
               int value;

               roomflags = fread_flagstring( fp );

               while( roomflags[0] != '\0' )
               {
                  roomflags = one_argument( roomflags, flag );
                  value = get_rflag( flag );
                  if( value < 0 || value >= MAX_BITS )
                     bug( "Unknown roomflag: %s", flag );
                  else
                     xSET_BIT( pRoomIndex->room_flags, value );
               }
               break;
            }
            break;

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

         case 'R':
            if( !str_cmp( word, "Reset" ) )
            {
               load_room_reset( pRoomIndex, fp );
               break;
            }
            break;

         case 'S':
            if( !str_cmp( word, "Sector" ) )
            {
               int sector = get_secflag( fread_flagstring( fp ) );

               if( sector < 0 || sector >= SECT_MAX )
               {
                  bug( "%s: Room #%d has bad sector type.", __FUNCTION__, pRoomIndex->vnum );
                  sector = 1;
               }

               pRoomIndex->sector_type = sector;
               break;
            }

            if( !str_cmp( word, "Stats" ) )
            {
               char *ln = fread_line( fp );
               int x1, x2, x3;

               x1 = x2 = x3 = 0;
               sscanf( ln, "%d %d %d", &x1, &x2, &x3 );

               pRoomIndex->tele_delay = x1;
               pRoomIndex->tele_vnum = x2;
               pRoomIndex->tunnel = x3;

               break;
            }
            break;

         case 'V':
            if( !str_cmp( word, "Vnum" ) )
            {
               int vnum = fread_number( fp );

               if( get_room_index( vnum ) )
               {
                  bug( "%s: vnum %d duplicated.", __FUNCTION__, vnum );

                  // Try to recover, read to end of duplicated room and then bail out
                  for( ;; )
                  {
                     word = feof( fp ) ? "#ENDROOM" : fread_word( fp );
                     if( !str_cmp( word, "#ENDROOM" ) )
                        return;
                  }
               }
               else
               {
                  CREATE( pRoomIndex, ROOM_INDEX_DATA, 1 );
                  oldroom = false;
               }
               pRoomIndex->vnum = vnum;
               pRoomIndex->area = tarea;
               if( !tarea->low_r_vnum )
                  tarea->low_r_vnum = vnum;
               if( vnum > tarea->hi_r_vnum )
                  tarea->hi_r_vnum = vnum;
               break;
            }
            break;
      }
   }
}

void oprog_file_read( OBJ_INDEX_DATA * prog_target, const char *f )
{
   MPROG_DATA *mprg = NULL;
   char MUDProgfile[256];
   FILE *progfile;
   char letter;
   bool fMatch;   // Unused, but needed to shut the compiler up about the KEY macro

   snprintf( MUDProgfile, 256, "%s%s", PROG_DIR, f );

   if( !( progfile = fopen( MUDProgfile, "r" ) ) )
   {
      bug( "%s: couldn't open mudprog file", __FUNCTION__ );
      return;
   }

   for( ;; )
   {
      letter = fread_letter( progfile );

      if( letter != '#' )
      {
         bug( "%s: MUDPROG char", __FUNCTION__ );
         break;
      }

      const char *word = ( feof( progfile ) ? "ENDFILE" : fread_word( progfile ) );

      if( word[0] == '\0' )
      {
         log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
         word = "ENDFILE";
      }

      if( !str_cmp( word, "ENDFILE" ) )
         break;

      if( !str_cmp( word, "MUDPROG" ) )
      {
         CREATE( mprg, MPROG_DATA, 1 );

         for( ;; )
         {
            word = ( feof( progfile ) ? "#ENDPROG" : fread_word( progfile ) );

            if( word[0] == '\0' )
            {
               log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
               word = "#ENDPROG";
            }

            if( !str_cmp( word, "#ENDPROG" ) )
            {
               mprg->next = prog_target->mudprogs;
               prog_target->mudprogs = mprg;
               break;
            }

            switch ( word[0] )
            {
               default:
                  log_printf( "%s: no match: %s", __FUNCTION__, word );
                  fread_to_eol( progfile );
                  break;

               case 'A':
                  if( !str_cmp( word, "Arglist" ) )
                  {
                     mprg->arglist = fread_string( progfile );
                     mprg->fileprog = true;

                     switch ( mprg->type )
                     {
                        case IN_FILE_PROG:
                           bug( "%s: Nested file programs are not allowed.", __FUNCTION__ );
                           DISPOSE( mprg );
                           break;

                        default:
                           break;
                     }
                     break;
                  }
                  break;

               case 'C':
                  KEY( "Comlist", mprg->comlist, fread_string( progfile ) );
                  break;

               case 'P':
                  if( !str_cmp( word, "Progtype" ) )
                  {
                     mprg->type = mprog_name_to_type( fread_flagstring( progfile ) );
                     break;
                  }
                  break;
            }
         }
      }
   }
   fclose( progfile );
   progfile = NULL;
   return;
}

void fread_fuss_objprog( FILE * fp, MPROG_DATA * mprg, OBJ_INDEX_DATA * prog_target )
{
   bool fMatch;   // Unused, but needed to shut the compiler up about the KEY macro

   for( ;; )
   {
      const char *word = ( feof( fp ) ? "#ENDPROG" : fread_word( fp ) );

      if( word[0] == '\0' )
      {
         log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
         word = "#ENDPROG";
      }

      if( !str_cmp( word, "#ENDPROG" ) )
         return;

      switch ( word[0] )
      {
         default:
            log_printf( "%s: no match: %s", __FUNCTION__, word );
            fread_to_eol( fp );
            break;

         case 'A':
            if( !str_cmp( word, "Arglist" ) )
            {
               mprg->arglist = fread_string( fp );
               mprg->fileprog = false;

               switch ( mprg->type )
               {
                  case IN_FILE_PROG:
                     oprog_file_read( prog_target, mprg->arglist );
                     break;
                  default:
                     break;
               }
               break;
            }
            break;

         case 'C':
            KEY( "Comlist", mprg->comlist, fread_string( fp ) );
            break;

         case 'P':
            if( !str_cmp( word, "Progtype" ) )
            {
               mprg->type = mprog_name_to_type( fread_flagstring( fp ) );
               xSET_BIT( prog_target->progtypes, mprg->type );
               break;
            }
            break;
      }
   }
}

void fread_fuss_object( FILE * fp, AREA_DATA * tarea )
{
   OBJ_INDEX_DATA *pObjIndex = NULL;
   bool oldobj = false;
   bool fMatch;   // Unused, but needed to shut the compiler up about the KEY macro

   for( ;; )
   {
      const char *word = ( feof( fp ) ? "#ENDOBJECT" : fread_word( fp ) );
      char flag[MAX_INPUT_LENGTH];
      int value = 0;

      if( word[0] == '\0' )
      {
         log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
         word = "#ENDOBJECT";
      }

      switch ( word[0] )
      {
         default:
            bug( "%s: no match: %s", __FUNCTION__, word );
            fread_to_eol( fp );
            break;

         case '#':
            if( !str_cmp( word, "#ENDOBJECT" ) )
            {
               if( !pObjIndex->description )
                  pObjIndex->description = STRALLOC( "" );
               if( !pObjIndex->action_desc )
                  pObjIndex->action_desc = STRALLOC( "" );

               if( !oldobj )
               {
                  int iHash = pObjIndex->vnum % MAX_KEY_HASH;
                  pObjIndex->next = obj_index_hash[iHash];
                  obj_index_hash[iHash] = pObjIndex;
               }
               return;
            }

            if( !str_cmp( word, "#EXDESC" ) )
            {
               EXTRA_DESCR_DATA *ed = fread_fuss_exdesc( fp );
               if( ed )
                  LINK( ed, pObjIndex->first_extradesc, pObjIndex->last_extradesc, next, prev );
               break;
            }

            if( !str_cmp( word, "#MUDPROG" ) )
            {
               MPROG_DATA *mprg;

               CREATE( mprg, MPROG_DATA, 1 );
               fread_fuss_objprog( fp, mprg, pObjIndex );
               mprg->next = pObjIndex->mudprogs;
               pObjIndex->mudprogs = mprg;
               break;
            }
            break;

         case 'A':
            KEY( "Action", pObjIndex->action_desc, fread_string( fp ) );

            if( !str_cmp( word, "Affect" ) || !str_cmp( word, "AffectData" ) )
            {
               AFFECT_DATA *af = fread_fuss_affect( fp, word );

               if( af )
                  LINK( af, pObjIndex->first_affect, pObjIndex->last_affect, next, prev );
               break;
            }
            break;

         case 'F':
            if( !str_cmp( word, "Flags" ) )
            {
               const char *eflags = fread_flagstring( fp );

               while( eflags[0] != '\0' )
               {
                  eflags = one_argument( eflags, flag );
                  value = get_oflag( flag );
                  if( value < 0 || value >= MAX_BITS )
                     bug( "Unknown object extraflag: %s", flag );
                  else
                     xSET_BIT( pObjIndex->extra_flags, value );
               }
               break;
            }
            break;

         case 'K':
            KEY( "Keywords", pObjIndex->name, fread_string( fp ) );
            break;

         case 'L':
            KEY( "Long", pObjIndex->description, fread_string( fp ) );
            break;

         case 'S':
            KEY( "Short", pObjIndex->short_descr, fread_string( fp ) );
            if( !str_cmp( word, "Spells" ) )
            {
               switch ( pObjIndex->item_type )
               {
                  default:
                     break;

                  case ITEM_PILL:
                  case ITEM_POTION:
                  case ITEM_SCROLL:
                     pObjIndex->value[1] = skill_lookup( fread_word( fp ) );
                     pObjIndex->value[2] = skill_lookup( fread_word( fp ) );
                     pObjIndex->value[3] = skill_lookup( fread_word( fp ) );
                     break;

                  case ITEM_STAFF:
                  case ITEM_WAND:
                     pObjIndex->value[3] = skill_lookup( fread_word( fp ) );
                     break;

                  case ITEM_SALVE:
                     pObjIndex->value[4] = skill_lookup( fread_word( fp ) );
                     pObjIndex->value[5] = skill_lookup( fread_word( fp ) );
                     break;
               }
               break;
            }

            if( !str_cmp( word, "Stats" ) )
            {
               char *ln = fread_line( fp );
               int x1, x2, x3, x4, x5;

               x1 = x2 = x3 = x4 = x5 = 0;
               sscanf( ln, "%d %d %d %d %d", &x1, &x2, &x3, &x4, &x5 );

               pObjIndex->weight = x1;
               pObjIndex->cost = x2;
               pObjIndex->rent = x3;
               pObjIndex->level = x4;
               pObjIndex->layers = x5;

               break;
            }
            break;

         case 'T':
            if( !str_cmp( word, "Type" ) )
            {
               value = get_otype( fread_flagstring( fp ) );

               if( value < 0 )
               {
                  bug( "%s: vnum %d: Object has invalid type! Defaulting to trash.", __FUNCTION__, pObjIndex->vnum );
                  value = get_otype( "trash" );
               }
               pObjIndex->item_type = value;
               break;
            }
            break;

         case 'V':
            if( !str_cmp( word, "Values" ) )
            {
               char *ln = fread_line( fp );
               int x1, x2, x3, x4, x5, x6;
               x1 = x2 = x3 = x4 = x5 = x6 = 0;

               sscanf( ln, "%d %d %d %d %d %d", &x1, &x2, &x3, &x4, &x5, &x6 );

               pObjIndex->value[0] = x1;
               pObjIndex->value[1] = x2;
               pObjIndex->value[2] = x3;
               pObjIndex->value[3] = x4;
               pObjIndex->value[4] = x5;
               pObjIndex->value[5] = x6;

               break;
            }

            if( !str_cmp( word, "Vnum" ) )
            {
               int vnum = fread_number( fp );

               if( get_obj_index( vnum ) )
               {
                  bug( "%s: vnum %d duplicated.", __FUNCTION__, vnum );

                  // Try to recover, read to end of duplicated object and then bail out
                  for( ;; )
                  {
                     word = feof( fp ) ? "#ENDOBJECT" : fread_word( fp );

                     if( !str_cmp( word, "#ENDOBJECT" ) )
                        return;
                  }
               }
               else
               {
                  CREATE( pObjIndex, OBJ_INDEX_DATA, 1 );
                  oldobj = false;
               }
               pObjIndex->vnum = vnum;
               if( !tarea->low_o_vnum )
                  tarea->low_o_vnum = vnum;
               if( vnum > tarea->hi_o_vnum )
                  tarea->hi_o_vnum = vnum;
               break;
            }
            break;

         case 'W':
            if( !str_cmp( word, "WFlags" ) )
            {
               const char *wflags = fread_flagstring( fp );

               while( wflags[0] != '\0' )
               {
                  wflags = one_argument( wflags, flag );
                  value = get_wflag( flag );
                  if( value < 0 || value > 31 )
                     bug( "Unknown wear flag: %s", flag );
                  else
                     SET_BIT( pObjIndex->wear_flags, 1 << value );
               }
               break;
            }
            break;
      }
   }
}

void mprog_file_read( MOB_INDEX_DATA * prog_target, const char *f )
{
   MPROG_DATA *mprg = NULL;
   char MUDProgfile[256];
   FILE *progfile;
   char letter;
   bool fMatch;   // Unused, but needed to shut the compiler up about the KEY macro

   snprintf( MUDProgfile, 256, "%s%s", PROG_DIR, f );

   if( !( progfile = fopen( MUDProgfile, "r" ) ) )
   {
      bug( "%s: couldn't open mudprog file", __FUNCTION__ );
      return;
   }

   for( ;; )
   {
      letter = fread_letter( progfile );

      if( letter != '#' )
      {
         bug( "%s: MUDPROG char", __FUNCTION__ );
         break;
      }

      const char *word = ( feof( progfile ) ? "ENDFILE" : fread_word( progfile ) );

      if( word[0] == '\0' )
      {
         log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
         word = "ENDFILE";
      }

      if( !str_cmp( word, "ENDFILE" ) )
         break;

      if( !str_cmp( word, "MUDPROG" ) )
      {
         CREATE( mprg, MPROG_DATA, 1 );

         for( ;; )
         {
            word = ( feof( progfile ) ? "#ENDPROG" : fread_word( progfile ) );

            if( word[0] == '\0' )
            {
               log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
               word = "#ENDPROG";
            }

            if( !str_cmp( word, "#ENDPROG" ) )
            {
               mprg->next = prog_target->mudprogs;
               prog_target->mudprogs = mprg;
               break;
            }

            switch ( word[0] )
            {
               default:
                  log_printf( "%s: no match: %s", __FUNCTION__, word );
                  fread_to_eol( progfile );
                  break;

               case 'A':
                  if( !str_cmp( word, "Arglist" ) )
                  {
                     mprg->arglist = fread_string( progfile );
                     mprg->fileprog = true;

                     switch ( mprg->type )
                     {
                        case IN_FILE_PROG:
                           bug( "%s: Nested file programs are not allowed.", __FUNCTION__ );
                           DISPOSE( mprg );
                           break;

                        default:
                           break;
                     }
                     break;
                  }
                  break;

               case 'C':
                  KEY( "Comlist", mprg->comlist, fread_string( progfile ) );
                  break;

               case 'P':
                  if( !str_cmp( word, "Progtype" ) )
                  {
                     mprg->type = mprog_name_to_type( fread_flagstring( progfile ) );
                     break;
                  }
                  break;
            }
         }
      }
   }
   fclose( progfile );
   progfile = NULL;
   return;
}

void fread_fuss_mobprog( FILE * fp, MPROG_DATA * mprg, MOB_INDEX_DATA * prog_target )
{
   bool fMatch;   // Unused, but needed to shut the compiler up about the KEY macro

   for( ;; )
   {
      const char *word = ( feof( fp ) ? "#ENDPROG" : fread_word( fp ) );

      if( word[0] == '\0' )
      {
         log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
         word = "#ENDPROG";
      }

      if( !str_cmp( word, "#ENDPROG" ) )
         return;

      switch ( word[0] )
      {
         default:
            log_printf( "%s: no match: %s", __FUNCTION__, word );
            fread_to_eol( fp );
            break;

         case 'A':
            if( !str_cmp( word, "Arglist" ) )
            {
               mprg->arglist = fread_string( fp );
               mprg->fileprog = false;

               switch ( mprg->type )
               {
                  case IN_FILE_PROG:
                     mprog_file_read( prog_target, mprg->arglist );
                     break;
                  default:
                     break;
               }
               break;
            }
            break;

         case 'C':
            KEY( "Comlist", mprg->comlist, fread_string( fp ) );
            break;

         case 'P':
            if( !str_cmp( word, "Progtype" ) )
            {
               mprg->type = mprog_name_to_type( fread_flagstring( fp ) );
               xSET_BIT( prog_target->progtypes, mprg->type );
               break;
            }
            break;
      }
   }
}

void fread_fuss_mobile( FILE * fp, AREA_DATA * tarea )
{
   MOB_INDEX_DATA *pMobIndex = NULL;
   bool oldmob = false;
   bool fMatch;   // Unused, but needed to shut the compiler up about the KEY macro

   for( ;; )
   {
      const char *word = ( feof( fp ) ? "#ENDMOBILE" : fread_word( fp ) );
      char flag[MAX_INPUT_LENGTH];
      int value = 0;

      if( word[0] == '\0' )
      {
         log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
         word = "#ENDMOBILE";
      }

      switch ( word[0] )
      {
         default:
            log_printf( "%s: no match: %s", __FUNCTION__, word );
            fread_to_eol( fp );
            break;

         case '#':
            if( !str_cmp( word, "#MUDPROG" ) )
            {
               MPROG_DATA *mprg;
               CREATE( mprg, MPROG_DATA, 1 );
               fread_fuss_mobprog( fp, mprg, pMobIndex );
               mprg->next = pMobIndex->mudprogs;
               pMobIndex->mudprogs = mprg;
               break;
            }

            if( !str_cmp( word, "#ENDMOBILE" ) )
            {
               if( !pMobIndex->long_descr )
                  pMobIndex->long_descr = STRALLOC( "" );
               if( !pMobIndex->description )
                  pMobIndex->description = STRALLOC( "" );

               if( !oldmob )
               {
                  int iHash = pMobIndex->vnum % MAX_KEY_HASH;
                  pMobIndex->next = mob_index_hash[iHash];
                  mob_index_hash[iHash] = pMobIndex;
               }
               return;
            }
            break;

         case 'A':
            if( !str_cmp( word, "Actflags" ) )
            {
               const char *actflags = NULL;

               actflags = fread_flagstring( fp );

               while( actflags[0] != '\0' )
               {
                  actflags = one_argument( actflags, flag );
                  value = get_actflag( flag );
                  if( value < 0 || value >= MAX_BITS )
                     bug( "Unknown actflag: %s", flag );
                  else
                     xSET_BIT( pMobIndex->act, value );
               }
               break;
            }

            if( !str_cmp( word, "Affected" ) )
            {
               const char *affectflags = NULL;

               affectflags = fread_flagstring( fp );

               while( affectflags[0] != '\0' )
               {
                  affectflags = one_argument( affectflags, flag );
                  value = get_aflag( flag );
                  if( value < 0 || value >= MAX_BITS )
                     bug( "Unknown affectflag: %s", flag );
                  else
                     xSET_BIT( pMobIndex->affected_by, value );
               }
               break;
            }

            if( !str_cmp( word, "Attacks" ) )
            {
               const char *attacks = fread_flagstring( fp );

               while( attacks[0] != '\0' )
               {
                  attacks = one_argument( attacks, flag );
                  value = get_attackflag( flag );
                  if( value < 0 || value >= MAX_BITS )
                     bug( "Unknown attackflag: %s", flag );
                  else
                     xSET_BIT( pMobIndex->attacks, value );
               }
               break;
            }

            if( !str_cmp( word, "Attribs" ) )
            {
               char *ln = fread_line( fp );
               int x1, x2, x3, x4, x5, x6, x7;

               x1 = x2 = x3 = x4 = x5 = x6 = x7 = 0;
               sscanf( ln, "%d %d %d %d %d %d %d", &x1, &x2, &x3, &x4, &x5, &x6, &x7 );

               pMobIndex->perm_str = x1;
               pMobIndex->perm_int = x2;
               pMobIndex->perm_wis = x3;
               pMobIndex->perm_dex = x4;
               pMobIndex->perm_con = x5;
               pMobIndex->perm_cha = x6;
               pMobIndex->perm_lck = x7;

               break;
            }
            break;

         case 'B':
            if( !str_cmp( word, "Bodyparts" ) )
            {
               const char *bodyparts = fread_flagstring( fp );

               while( bodyparts[0] != '\0' )
               {
                  bodyparts = one_argument( bodyparts, flag );
                  value = get_partflag( flag );
                  if( value < 0 || value > 31 )
                     bug( "Unknown bodypart: %s", flag );
                  else
                     SET_BIT( pMobIndex->xflags, 1 << value );
               }
               break;
            }
            break;

         case 'C':
            if( !str_cmp( word, "Class" ) )
            {
               fread_flagstring( fp );
               break;
            }
            break;

         case 'D':
            if( !str_cmp( word, "Defenses" ) )
            {
               const char *defenses = fread_flagstring( fp );

               while( defenses[0] != '\0' )
               {
                  defenses = one_argument( defenses, flag );
                  value = get_defenseflag( flag );
                  if( value < 0 || value >= MAX_BITS )
                     bug( "Unknown defenseflag: %s", flag );
                  else
                     xSET_BIT( pMobIndex->defenses, value );
               }
               break;
            }

            if( !str_cmp( word, "DefPos" ) )
            {
               short position = get_npc_position( fread_flagstring( fp ) );

               if( position < 0 || position > POS_DRAG )
               {
                  bug( "%s: vnum %d: Mobile in invalid default position! Defaulting to standing.", __FUNCTION__,
                       pMobIndex->vnum );
                  position = POS_STANDING;
               }
               pMobIndex->defposition = position;
               break;
            }

            KEY( "Desc", pMobIndex->description, fread_string( fp ) );
            break;

         case 'G':
            if( !str_cmp( word, "Gender" ) )
            {
               short sex = get_npc_sex( fread_flagstring( fp ) );

               if( sex < 0 || sex > SEX_FEMALE )
               {
                  bug( "%s: vnum %d: Mobile has invalid sex! Defaulting to neuter.", __FUNCTION__, pMobIndex->vnum );
                  sex = SEX_NEUTRAL;
               }
               pMobIndex->sex = sex;
               break;
            }
            break;

         case 'I':
            if( !str_cmp( word, "Immune" ) )
            {
               const char *immune = fread_flagstring( fp );

               while( immune[0] != '\0' )
               {
                  immune = one_argument( immune, flag );
                  value = get_risflag( flag );
                  if( value < 0 || value > 31 )
                     bug( "Unknown RIS flag (I): %s", flag );
                  else
                     SET_BIT( pMobIndex->immune, 1 << value );
               }
               break;
            }
            break;

         case 'K':
            KEY( "Keywords", pMobIndex->player_name, fread_string( fp ) );
            break;

         case 'L':
            KEY( "Long", pMobIndex->long_descr, fread_string( fp ) );
            break;

         case 'P':
            if( !str_cmp( word, "Position" ) )
            {
               short position = get_npc_position( fread_flagstring( fp ) );

               if( position < 0 || position > POS_DRAG )
               {
                  bug( "%s: vnum %d: Mobile in invalid position! Defaulting to standing.", __FUNCTION__, pMobIndex->vnum );
                  position = POS_STANDING;
               }
               pMobIndex->position = position;
               break;
            }
            break;

         case 'R':
            if( !str_cmp( word, "Race" ) )
            {
               fread_flagstring( fp );
               break;
            }

            if( !str_cmp( word, "RepairData" ) )
            {
               int iFix;
               REPAIR_DATA *rShop;

               CREATE( rShop, REPAIR_DATA, 1 );
               rShop->keeper = pMobIndex->vnum;
               for( iFix = 0; iFix < MAX_FIX; ++iFix )
                  rShop->fix_type[iFix] = fread_number( fp );
               rShop->profit_fix = fread_number( fp );
               rShop->shop_type = fread_number( fp );
               rShop->open_hour = fread_number( fp );
               rShop->close_hour = fread_number( fp );
               pMobIndex->rShop = rShop;
               break;
            }

            if( !str_cmp( word, "Resist" ) )
            {
               const char *resist = fread_flagstring( fp );

               while( resist[0] != '\0' )
               {
                  resist = one_argument( resist, flag );
                  value = get_risflag( flag );
                  if( value < 0 || value > 31 )
                     bug( "Unknown RIS flag (R): %s", flag );
                  else
                     SET_BIT( pMobIndex->resistant, 1 << value );
               }
               break;
            }
            break;

         case 'S':
            if( !str_cmp( word, "Saves" ) )
            {
               char *ln = fread_line( fp );
               int x1, x2, x3, x4, x5;

               x1 = x2 = x3 = x4 = x5 = 0;
               sscanf( ln, "%d %d %d %d %d", &x1, &x2, &x3, &x4, &x5 );

               pMobIndex->saving_poison_death = x1;
               pMobIndex->saving_wand = x2;
               pMobIndex->saving_para_petri = x3;
               pMobIndex->saving_breath = x4;
               pMobIndex->saving_spell_staff = x5;

               break;
            }

            KEY( "Short", pMobIndex->short_descr, fread_string( fp ) );

            if( !str_cmp( word, "ShopData" ) )
            {
               int iTrade;
               SHOP_DATA *pShop;

               CREATE( pShop, SHOP_DATA, 1 );
               pShop->keeper = pMobIndex->vnum;
               for( iTrade = 0; iTrade < MAX_TRADE; ++iTrade )
                  pShop->buy_type[iTrade] = fread_number( fp );
               pShop->profit_buy = fread_number( fp );
               pShop->profit_sell = fread_number( fp );
               pShop->profit_buy = URANGE( pShop->profit_sell + 5, pShop->profit_buy, 1000 );
               pShop->profit_sell = URANGE( 0, pShop->profit_sell, pShop->profit_buy - 5 );
               pShop->open_hour = fread_number( fp );
               pShop->close_hour = fread_number( fp );
               pMobIndex->pShop = pShop;
               break;
            }

            if( !str_cmp( word, "Speaks" ) )
            {
               fread_flagstring( fp );
               break;
            }

            if( !str_cmp( word, "Speaking" ) )
            {
               fread_flagstring( fp );
               break;
            }

            if( !str_cmp( word, "Specfun" ) )
            {
               pMobIndex->spec_funname = fread_string( fp );
               break;
            }

            if( !str_cmp( word, "Stats1" ) )
            {
               char *ln = fread_line( fp );
               int x1, x2, x3, x4, x5, x6;

               x1 = x2 = x3 = x4 = x5 = x6 = 0;
               sscanf( ln, "%d %d %d %d %d %d", &x1, &x2, &x3, &x4, &x5, &x6 );

               pMobIndex->alignment = x1;
               pMobIndex->level = x2;
               pMobIndex->mobthac0 = x3;
               pMobIndex->ac = x4;
               pMobIndex->gold = x5;
               pMobIndex->exp = x6;

               break;
            }

            if( !str_cmp( word, "Stats2" ) )
            {
               char *ln = fread_line( fp );
               int x1, x2, x3;
               x1 = x2 = x3 = 0;
               sscanf( ln, "%d %d %d", &x1, &x2, &x3 );

               pMobIndex->hitnodice = x1;
               pMobIndex->hitsizedice = x2;
               pMobIndex->hitplus = x3;

               break;
            }

            if( !str_cmp( word, "Stats3" ) )
            {
               char *ln = fread_line( fp );
               int x1, x2, x3;
               x1 = x2 = x3 = 0;
               sscanf( ln, "%d %d %d", &x1, &x2, &x3 );

               pMobIndex->damnodice = x1;
               pMobIndex->damsizedice = x2;
               pMobIndex->damplus = x3;

               break;
            }

            if( !str_cmp( word, "Stats4" ) )
            {
               char *ln = fread_line( fp );
               int x1, x2, x3, x4, x5;

               x1 = x2 = x3 = x4 = x5 = 0;
               sscanf( ln, "%d %d %d %d %d", &x1, &x2, &x3, &x4, &x5 );

               pMobIndex->height = x1;
               pMobIndex->weight = x2;
               pMobIndex->numattacks = x3;
               pMobIndex->hitroll = x4;
               pMobIndex->damroll = x5;

               break;
            }

            if( !str_cmp( word, "Suscept" ) )
            {
               const char *suscep = fread_flagstring( fp );

               while( suscep[0] != '\0' )
               {
                  suscep = one_argument( suscep, flag );
                  value = get_risflag( flag );
                  if( value < 0 || value > 31 )
                     bug( "Unknown RIS flag (S): %s", flag );
                  else
                     SET_BIT( pMobIndex->susceptible, 1 << value );
               }
               break;
            }
            break;

         case 'V':
            if( !str_cmp( word, "Vnum" ) )
            {
               int vnum = fread_number( fp );

               if( get_mob_index( vnum ) )
               {
                  bug( "%s: vnum %d duplicated.", __FUNCTION__, vnum );

                  // Try to recover, read to end of duplicated mobile and then bail out
                  for( ;; )
                  {
                     word = feof( fp ) ? "#ENDMOBILE" : fread_word( fp );

                     if( !str_cmp( word, "#ENDMOBILE" ) )
                        return;
                  }
               }
               else
               {
                  CREATE( pMobIndex, MOB_INDEX_DATA, 1 );
                  oldmob = false;
               }
               pMobIndex->vnum = vnum;
               if( !tarea->low_m_vnum )
                  tarea->low_m_vnum = vnum;
               if( vnum > tarea->hi_m_vnum )
                  tarea->hi_m_vnum = vnum;
               break;
            }
            break;
      }
   }
}

void fread_fuss_areadata( FILE * fp, AREA_DATA * tarea )
{
   bool fMatch;   // Unused, but needed to shut the compiler up about the KEY macro

   for( ;; )
   {
      const char *word = ( feof( fp ) ? "#ENDAREADATA" : fread_word( fp ) );

      if( word[0] == '\0' )
      {
         log_printf( "%s: EOF encountered reading file!", __FUNCTION__ );
         word = "#ENDAREADATA";
      }

      switch ( word[0] )
      {
         default:
            log_printf( "%s: no match: %s", __FUNCTION__, word );
            fread_to_eol( fp );
            break;

         case '#':
            if( !str_cmp( word, "#ENDAREADATA" ) )
            {
               tarea->age = tarea->reset_frequency;
               return;
            }
            break;

         case 'A':
            KEY( "Author", tarea->author, fread_string( fp ) );
            break;

         case 'C':
            KEY( "Credits", tarea->credits, fread_string( fp ) );
            break;

         case 'E':
            if( !str_cmp( word, "Economy" ) )
            {
               tarea->high_economy = fread_number( fp );
               tarea->low_economy = fread_number( fp );
               break;
            }
            break;

         case 'F':
            if( !str_cmp( word, "Flags" ) )
            {
               const char *areaflags = NULL;
               char flag[MAX_INPUT_LENGTH];
               int value;

               areaflags = fread_flagstring( fp );

               while( areaflags[0] != '\0' )
               {
                  areaflags = one_argument( areaflags, flag );
                  value = get_areaflag( flag );
                  if( value < 0 || value > 31 )
                     bug( "Unknown area flag: %s", flag );
                  else
                     SET_BIT( tarea->flags, 1 << value );
               }
               break;
            }
            break;

         case 'N':
            KEY( "Name", tarea->name, fread_string_nohash( fp ) );
            break;

         case 'R':
            if( !str_cmp( word, "Ranges" ) )
            {
               int x1, x2, x3, x4;
               char *ln;

               ln = fread_line( fp );

               x1 = x2 = x3 = x4 = 0;
               sscanf( ln, "%d %d %d %d", &x1, &x2, &x3, &x4 );

               tarea->low_soft_range = x1;
               tarea->hi_soft_range = x2;
               tarea->low_hard_range = x3;
               tarea->hi_hard_range = x4;

               break;
            }
            KEY( "ResetMsg", tarea->resetmsg, fread_string_nohash( fp ) );
            KEY( "ResetFreq", tarea->reset_frequency, fread_number( fp ) );
            break;

         case 'S':
            KEY( "Spelllimit", tarea->spelllimit, fread_number( fp ) );
            break;

         case 'V':
            KEY( "Version", tarea->version, fread_number( fp ) );
            break;

         case 'W':
            KEY( "WeatherX", tarea->weatherx, fread_number( fp ) );
            KEY( "WeatherY", tarea->weathery, fread_number( fp ) );
            break;
      }
   }
}

/*
 * Load an 'area' header line.
 */
AREA_DATA *create_area( void )
{
   AREA_DATA *pArea;

   CREATE( pArea, AREA_DATA, 1 );
   pArea->first_room = pArea->last_room = NULL;
   pArea->name = NULL;
   pArea->author = NULL;
   pArea->credits = NULL;
   pArea->filename = str_dup( strArea );
   pArea->age = 15;
   pArea->reset_frequency = 15;
   pArea->nplayer = 0;
   pArea->low_r_vnum = 0;
   pArea->low_o_vnum = 0;
   pArea->low_m_vnum = 0;
   pArea->hi_r_vnum = 0;
   pArea->hi_o_vnum = 0;
   pArea->hi_m_vnum = 0;
   pArea->low_soft_range = 0;
   pArea->hi_soft_range = MAX_LEVEL;
   pArea->low_hard_range = 0;
   pArea->hi_hard_range = MAX_LEVEL;
   pArea->spelllimit = 0;
   pArea->weatherx = 0;
   pArea->weathery = 0;
   pArea->version = 1;

   LINK( pArea, first_area, last_area, next, prev );
   return pArea;
}

AREA_DATA *fread_fuss_area( AREA_DATA * tarea, FILE * fp )
{
   for( ;; )
   {
      char letter;
      const char *word;

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

      if( letter != '#' )
      {
         bug( "%s: # not found. Invalid format.", __FUNCTION__ );
         exit( 1 );
         break;
      }

      word = ( feof( fp ) ? "ENDAREA" : fread_word( fp ) );

      if( word[0] == '\0' )
      {
         bug( "%s: EOF encountered reading file!", __FUNCTION__ );
         word = "ENDAREA";
      }

      if( !str_cmp( word, "AREADATA" ) )
      {
         if( !tarea )
            tarea = create_area(  );
         fread_fuss_areadata( fp, tarea );
      }
      else if( !str_cmp( word, "MOBILE" ) )
         fread_fuss_mobile( fp, tarea );
      else if( !str_cmp( word, "OBJECT" ) )
         fread_fuss_object( fp, tarea );
      else if( !str_cmp( word, "ROOM" ) )
         fread_fuss_room( fp, tarea );
      else if( !str_cmp( word, "ENDAREA" ) )
         break;
      else
      {
         bug( "%s: Bad section header: %s", __FUNCTION__, word );
         fread_to_eol( fp );
      }
   }
   return tarea;
}

void load_helps( FILE * fp )
{
   char *help;

   for( ;; )
   {
      fread_number( fp );
      help = ( char * )fread_flagstring( fp );
      if( help[0] == '$' )
         break;
      fread_flagstring( fp );
   }
}

void load_area_file( AREA_DATA * tarea, const char *filename )
{
   char *word, nfilename[MSL];
   int aversion = 0;

   snprintf( nfilename, sizeof( nfilename ), "../area/%s", filename );
   if( !( fpArea = fopen( nfilename, "r" ) ) )
   {
      perror( nfilename );
      bug( "%s: error loading file (can't open) %s", __FUNCTION__, nfilename );
      return;
   }

   if( fread_letter( fpArea ) != '#' )
   {
      bug( "%s: No # found at start of area file.", __FUNCTION__ );
      exit( 1 );
   }

   word = fread_word( fpArea );

   // New FUSS area format support -- Samson 7/5/07
   if( !str_cmp( word, "FUSSAREA" ) )
   {
      tarea = fread_fuss_area( tarea, fpArea );
      fclose( fpArea );
      fpArea = NULL;

      if( tarea )
         process_sorting( tarea );
      return;
   }

   // Drop through to the old format processor
   if( !str_cmp( word, "AREA" ) )
   {
      tarea = load_area( fpArea, 0 );
   }
   // Only seen at this stage for help.are
   else if( !str_cmp( word, "HELPS" ) )
      load_helps( fpArea );
   // Only seen at this stage for SmaugWiz areas
   else if( !str_cmp( word, "VERSION" ) )
      aversion = fread_number( fpArea );

   for( ;; )
   {
      if( fread_letter( fpArea ) != '#' )
      {
         bug( "%s: # not found", __FUNCTION__ );
         exit( 1 );
      }

      word = fread_word( fpArea );

      if( word[0] == '$' )
         break;
      
      // Only seen at this stage for SmaugWiz areas. The format had better be right or there'll be trouble here!
      else if( !str_cmp( word, "AREA" ) )
         tarea = load_area( fpArea, aversion );
      else if( !str_cmp( word, "AUTHOR" ) )
         load_author( tarea, fpArea );
      else if( !str_cmp( word, "FLAGS" ) )
         load_flags( tarea, fpArea );
      else if( !str_cmp( word, "RANGES" ) )
         load_ranges( tarea, fpArea );
      else if( !str_cmp( word, "ECONOMY" ) )
         load_economy( tarea, fpArea );
      else if( !str_cmp( word, "RESETMSG" ) )
         load_resetmsg( tarea, fpArea );
      /*
       * Rennard 
       */
      else if( !str_cmp( word, "HELPS" ) )
         load_helps( fpArea );
      else if( !str_cmp( word, "MOBILES" ) )
         load_mobiles( tarea, fpArea );
      else if( !str_cmp( word, "OBJECTS" ) )
         load_objects( tarea, fpArea );
      else if( !str_cmp( word, "RESETS" ) )
         load_resets( tarea, fpArea );
      else if( !str_cmp( word, "ROOMS" ) )
         load_rooms( tarea, fpArea );
      else if( !str_cmp( word, "SHOPS" ) )
         load_shops( fpArea );
      else if( !str_cmp( word, "REPAIRS" ) )
         load_repairs( fpArea );
      else if( !str_cmp( word, "SPECIALS" ) )
         load_specials( fpArea );
      else if( !str_cmp( word, "CLIMATE" ) )
         load_climate( tarea, fpArea );
      else if( !str_cmp( word, "NEIGHBOR" ) )
         load_neighbor( tarea, fpArea );
      else if( !str_cmp( word, "VERSION" ) )
         load_version( tarea, fpArea );
      else if( !str_cmp( word, "SPELLLIMIT" ) )
      {
         tarea->spelllimit = fread_number( fpArea );
      }
      else
      {
         bug( "%s: bad section name: %s", __FUNCTION__, word );
         exit( 1 );
      }
   }
   fclose( fpArea );
   fpArea = NULL;

   if( !tarea )
      fprintf( stderr, "(%s)\n", filename );
}

size_t mudstrlcpy( char *dst, const char *src, size_t siz )
{
   register char *d = dst;
   register const char *s = src;
   register size_t n = siz;

   if( !src )
   {
      bug( "%s: NULL src string passed!", __FUNCTION__ );
      return 0;
   }

   if( !dst )
   {
      bug( "%s: NULL dst string being passed!", __FUNCTION__ );
      return 0;
   }

   /*
    * Copy as many bytes as will fit
    */
   if( n != 0 && --n != 0 )
   {
      do
      {
         if( ( *d++ = *s++ ) == 0 )
            break;
      }
      while( --n != 0 );
   }

   /*
    * Not enough room in dst, add NUL and traverse rest of src
    */
   if( n == 0 )
   {
      if( siz != 0 )
         *d = '\0';  /* NUL-terminate dst */
      while( *s++ )
         ;
   }
   return ( s - src - 1 ); /* count does not include NUL */
}

size_t mudstrlcat( char *dst, const char *src, size_t siz )
{
   register char *d = dst;
   register const char *s = src;
   register size_t n = siz;
   size_t dlen;

   if( !src )
   {
      bug( "%s: NULL src string passed!", __FUNCTION__ );
      return 0;
   }

   /*
    * Find the end of dst and adjust bytes left but don't go past end
    */
   while( n-- != 0 && *d != '\0' )
      d++;
   dlen = d - dst;
   n = siz - dlen;

   if( n == 0 )
      return ( dlen + strlen( s ) );
   while( *s != '\0' )
   {
      if( n != 1 )
      {
         *d++ = *s;
         n--;
      }
      s++;
   }
   *d = '\0';
   return ( dlen + ( s - src ) );   /* count does not include NUL */
}

time_t current_time; /* Time of this pulse      */

#ifdef WIN32
int mainthread( int argc, char **argv )
#else
int main( int argc, char **argv )
#endif
{
   struct timeval now_time;

   gettimeofday( &now_time, NULL );
   current_time = ( time_t ) now_time.tv_sec;

   log_string( "Booting Database" );
   boot_db( );

   exit( 0 );
}