eldhamud/boards/
eldhamud/clans/
eldhamud/classes/
eldhamud/councils/
eldhamud/deity/
eldhamud/doc/
eldhamud/doc/DIKU/
eldhamud/doc/MERC/
eldhamud/doc/mudprogs/
eldhamud/houses/
eldhamud/src/o/CVS/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.4 (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.     *
 * ------------------------------------------------------------------------ *
 * 			Table load/save Module				    *
 ****************************************************************************/
#include <ctype.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include "mud.h"
#include <dlfcn.h>

#if defined(KEY)
#undef KEY
#endif

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

bool load_race_file( char *fname );
void write_race_file( int ra );


/* global variables */
int top_sn;
int top_herb;
int MAX_PC_CLASS;
int MAX_PC_RACE;

SKILLTYPE *		skill_table	[MAX_SKILL];
struct	class_type *	class_table	[MAX_CLASS];
RACE_TYPE *		race_table	[MAX_RACE];
char *			title_table	[MAX_CLASS]
[MAX_LEVEL+1]
[2];
SKILLTYPE *		herb_table	[MAX_HERB];
SKILLTYPE *		disease_table	[MAX_DISEASE];

LANG_DATA *		first_lang;
LANG_DATA *		last_lang;

char * const skill_tname[] =
  { "unknown", "Spell", "Skill", "Weapon", "Tongue", "Herb", "Racial", "Disease"
  };

  SPELL_FUN *spell_function( char *name )
{
   void *funHandle;
   const char *error;

   funHandle = dlsym( sysdata.dlHandle, name );
   if( ( error = dlerror() ) != NULL )
   {
	bug( "Error locating %s in symbol table. %s", name, error );
      return spell_notfound;
   }
   return (SPELL_FUN*)funHandle;
}

DO_FUN *skill_function( char *name )
{
   void *funHandle;
   const char *error;

   funHandle = dlsym( sysdata.dlHandle, name );
   if( ( error = dlerror() ) != NULL )
   {
	bug( "Error locating %s in symbol table. %s", name, error );
	return skill_notfound;
   }
   return (DO_FUN*)funHandle;
}


bool load_class_file( char *fname )
{
  char buf[MAX_STRING_LENGTH];
  char *word;
  bool fMatch;
  struct class_type *class;
  int cl = -1;
  int tlev = 0;
  FILE *fp;

  sprintf( buf, "%s%s", CLASS_DIR, fname );
  if ( ( fp = fopen( buf, "r" ) ) == NULL )
    {
      perror( buf );
      return FALSE;
    }

  CREATE( class, struct class_type, 1 );

  /* Setup defaults for additions to class structure */
  class->attr_second = 0;
  class->attr_deficient = 0;
  xCLEAR_BITS(class->affected);
  class->resist = 0;
  class->suscept = 0;

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

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

        case 'A':
          KEY( "Affected",	class->affected,	fread_bitvector(fp));
          KEY( "AttrPrime",	class->attr_prime,	fread_number( fp )	);
          KEY( "AttrSecond",	class->attr_second,	fread_number( fp )	);
          KEY( "AttrDeficient",class->attr_deficient,	fread_number( fp )	);
          break;

        case 'C':
          KEY( "Class",	cl,			fread_number( fp )	);
          break;

        case 'E':
          if ( !str_cmp( word, "End" ) )
            {
              fclose( fp );
              if ( cl < 0 || cl >= MAX_CLASS )
                {
                  sprintf( buf, "Load_class_file: Class (%s) bad/not found (%d)",
                           class->who_name ? class->who_name : "name not found", cl );
                  bug( buf, 0 );
                  if ( class->who_name )
                    STRFREE( class->who_name );
                  DISPOSE( class );
                  return FALSE;
                }
              class_table[cl] = class;
              return TRUE;
            }
          KEY( "ExpBase",	class->exp_base,	fread_number( fp )	);
          break;

        case 'G':
          KEY( "Guild",	class->guild,		fread_number( fp )	);
          break;

        case 'H':
          KEY( "HpMax",	class->hp_max,		fread_number( fp )	);
          KEY( "HpMin",	class->hp_min,		fread_number( fp )	);
          break;

        case 'M':
          KEY( "Mana",	class->fMana,		fread_number( fp )	);
          break;

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

        case 'R':
          KEY( "Resist",	class->resist,		fread_number( fp ) );
          break;

        case 'S':
          if ( !str_cmp( word, "Skill" ) )
            {
              int sn, lev, adp;

              word = fread_word( fp );
              lev = fread_number( fp );
              adp = fread_number( fp );
              sn = skill_lookup( word );
              if ( cl < 0 || cl >= MAX_CLASS )
                {
                  sprintf( buf, "load_class_file: Skill %s -- class bad/not found (%d)", word, cl );
                  bug( buf, 0 );
                }
              else
                if ( !IS_VALID_SN(sn) )
                  {
                    sprintf( buf, "load_class_file: Skill %s unknown", word);
                    bug( buf, 0 );
                  }
                else
                  {
                    skill_table[sn]->skill_level[cl] = lev;
                    skill_table[sn]->skill_adept[cl] = adp;
                  }
              fMatch = TRUE;
              break;
            }
          KEY( "Skilladept",	class->skill_adept,	fread_number( fp )	);
          KEY( "Suscept",	class->suscept,		fread_number( fp ) );
          break;

        case 'T':
          if ( !str_cmp( word, "Title" ) )
            {
              if ( cl < 0 || cl >= MAX_CLASS )
                {
                  char *tmp;

                  sprintf( buf, "load_class_file: Title -- class bad/not found (%d)", cl );
                  bug( buf, 0 );
                  tmp = fread_string_nohash( fp );
                  DISPOSE( tmp );
                  tmp = fread_string_nohash( fp );
                  DISPOSE( tmp );
                }
              else
                if ( tlev < MAX_LEVEL+1 )
                  {
                    title_table[cl][tlev][0] = fread_string_nohash( fp );
                    title_table[cl][tlev][1] = fread_string_nohash( fp );
                    ++tlev;
                  }
                else
                  bug( "load_class_file: Too many titles" );
              fMatch = TRUE;
              break;
            }
          KEY( "Thac0",	class->thac0_00,	fread_number( fp )	);
          KEY( "Thac32",	class->thac0_32,	fread_number( fp )	);
          break;

        case 'W':
          KEY( "Weapon",	class->weapon,	fread_number( fp )	);
          break;
        }
      if ( !fMatch )
        {
          sprintf( buf, "load_class_file: no match: %s", word );
          bug( buf, 0 );
        }
    }
  return FALSE;
}

/*
 * Load in all the class files.
 */
void load_classes( )
{
  FILE *fpList;
  char *filename;
  char classlist[256];
  char buf[MAX_STRING_LENGTH];
  int i;

  MAX_PC_CLASS = 0;

  /*
   * Pre-init the class_table with blank classes
   */
  for ( i=0;i<MAX_CLASS;i++ )
    class_table[i] = NULL;

  sprintf( classlist, "%s%s", CLASS_DIR, CLASS_LIST );
  if ( ( fpList = fopen( classlist, "r" ) ) == NULL )
    {
      perror( classlist );
      exit( 1 );
    }

  for ( ; ; )
    {
      filename = feof( fpList ) ? "$" : fread_word( fpList );
      if ( filename[0] == '$' )
        break;

      if ( !load_class_file( filename ) )
        {
          sprintf( buf, "Cannot load class file: %s", filename );
          bug( buf, 0 );
        }
      else
        MAX_PC_CLASS++;
    }
  fclose( fpList );
  for ( i=0;i<MAX_CLASS;i++ )
    {
      if ( class_table[i] == NULL )
        {
          CREATE( class_table[i], struct class_type, 1 );
          create_new_class( i, "" );
        }
    }
  return;
}


void write_class_file( int cl )
{
  FILE *fpout;
  char buf[MAX_STRING_LENGTH];
  char filename[MAX_INPUT_LENGTH];
  struct class_type *class = class_table[cl];
  int x, y;

  sprintf( filename, "%s%s.class", CLASSDIR, class->who_name );
  if ( (fpout=fopen(filename, "w")) == NULL )
    {
      sprintf( buf, "Cannot open: %s for writing", filename );
      bug( buf, 0 );
      return;
    }
  fprintf( fpout, "Name        %s~\n",	class->who_name		);
  fprintf( fpout, "Class       %d\n",		cl			);
  fprintf( fpout, "AttrPrime   %d\n",		class->attr_prime	);
  fprintf( fpout, "AttrSecond   %d\n",	class->attr_second	);
  fprintf( fpout, "AttrDeficient   %d\n",	class->attr_deficient	);
  fprintf( fpout, "Weapon      %d\n",		class->weapon		);
  fprintf( fpout, "Guild       %d\n",		class->guild		);
  fprintf( fpout, "Skilladept  %d\n",		class->skill_adept	);
  fprintf( fpout, "Thac0       %d\n",		class->thac0_00		);
  fprintf( fpout, "Thac32      %d\n",		class->thac0_32		);
  fprintf( fpout, "Hpmin       %d\n",		class->hp_min		);
  fprintf( fpout, "Hpmax       %d\n",		class->hp_max		);
  fprintf( fpout, "Mana        %d\n",		class->fMana		);
  fprintf( fpout, "Expbase     %d\n",		class->exp_base		);
  fprintf( fpout, "Affected    %s\n",		print_bitvector(&class->affected));
  fprintf( fpout, "Resist	 %d\n",		class->resist		);
  fprintf( fpout, "Suscept	 %d\n",		class->suscept		);
  for ( x = 0; x < top_sn; x++ )
    {
      if ( !skill_table[x]->name || skill_table[x]->name[0] == '\0' )
        break;
      if ( (y=skill_table[x]->skill_level[cl]) < LEVEL_IMMORTAL )
        fprintf( fpout, "Skill '%s' %d %d\n",
                 skill_table[x]->name, y, skill_table[x]->skill_adept[cl] );
    }
  for ( x = 0; x <= MAX_LEVEL; x++ )
    fprintf( fpout, "Title\n%s~\n%s~\n",
             title_table[cl][x][0], title_table[cl][x][1] );
  fprintf( fpout, "End\n" );
  fclose( fpout );
}


/*
 * Load in all the race files.
 */
void load_races( )
{
  FILE *fpList;
  char *filename;
  char racelist[256];
  char buf[MAX_STRING_LENGTH];
  int i;

  MAX_PC_RACE = 0;
  /*
   * Pre-init the race_table with blank races
   */
  for(i=0;i<MAX_RACE;i++)
    race_table[i] = NULL;

  sprintf( racelist, "%s%s", RACEDIR, RACE_LIST );
  if ( ( fpList = fopen( racelist, "r" ) ) == NULL )
    {
      perror( racelist );
      exit( 1 );
    }

  for ( ; ; )
    {
      filename = feof( fpList ) ? "$" : fread_word( fpList );
      if ( filename[0] == '$' )
        break;

      if ( !load_race_file( filename ) )
        {
          sprintf( buf, "Cannot load race file: %s", filename );
          bug( buf, 0 );
        }
      else
        MAX_PC_RACE++;
    }
  for ( i = 0; i < MAX_RACE; i++ )
    {
      if ( race_table[i] == NULL )
        {
          CREATE( race_table[i], struct race_type, 1 );
          sprintf( race_table[i]->race_name, "%s", "unused" );
        }
    }
  fclose( fpList );
  return;
}

void write_race_file( int ra )
{
  FILE *fpout;
  char buf[MAX_STRING_LENGTH];
  char filename[MAX_INPUT_LENGTH];
  struct race_type *race = race_table[ra];
  int i;
  int x,y;

  if( !race->race_name)
    {
      sprintf( buf, "Race %d has null name, not writing .race file.", ra );
      bug( buf, 0 );
      return;
    }

  sprintf( filename, "%s%s.race", RACEDIR, race->race_name );
  if ( (fpout=fopen(filename, "w+")) == NULL )
    {
      sprintf( buf, "Cannot open: %s for writing", filename );
      bug( buf, 0 );
      return;
    }

  fprintf( fpout, "Name        %s~\n",	race->race_name		);
  fprintf( fpout, "Race        %d\n",		ra  			);
  fprintf( fpout, "Classes     %d\n",		race->class_restriction );
  fprintf( fpout, "Str_Plus    %d\n",		race->str_plus		);
  fprintf( fpout, "Dex_Plus    %d\n",		race->dex_plus		);
  fprintf( fpout, "Wis_Plus    %d\n",		race->wis_plus		);
  fprintf( fpout, "Int_Plus    %d\n",		race->int_plus		);
  fprintf( fpout, "Con_Plus    %d\n",		race->con_plus		);
  fprintf( fpout, "Cha_Plus    %d\n",		race->cha_plus		);
  fprintf( fpout, "Lck_Plus    %d\n",		race->lck_plus		);
  fprintf( fpout, "Hit         %d\n",		race->hit		);
  fprintf( fpout, "Mana        %d\n",		race->mana		);
  fprintf( fpout, "Affected    %s\n",		print_bitvector(&race->affected)	);
  fprintf( fpout, "Resist      %d\n",		race->resist		);
  fprintf( fpout, "Suscept     %d\n",		race->suscept		);
  fprintf( fpout, "Language    %d\n",		race->language		);
  fprintf( fpout, "Align       %d\n",		race->alignment		);
  fprintf( fpout, "Min_Align  %d\n",		race->minalign		);
  fprintf( fpout, "Max_Align	%d\n",		race->maxalign		);
  fprintf( fpout, "AC_Plus    %d\n",		race->ac_plus		);
  fprintf( fpout, "Exp_Mult   %d\n",		race->exp_multiplier	);
  fprintf( fpout, "Attacks    %s\n",		print_bitvector(&race->attacks)	);
  fprintf( fpout, "Defenses   %s\n",		print_bitvector(&race->defenses) );
  fprintf( fpout, "Height     %d\n",		race->height		);
  fprintf( fpout, "Weight     %d\n",		race->weight		);
  fprintf( fpout, "Hunger_Mod  %d\n",		race->hunger_mod	);
  fprintf( fpout, "Thirst_mod  %d\n",		race->thirst_mod	);
  fprintf( fpout, "Mana_Regen  %d\n",		race->mana_regen	);
  fprintf( fpout, "HP_Regen    %d\n",		race->hp_regen		);
  fprintf( fpout, "Race_Recall %d\n",		race->race_recall	);
  for ( i = 0; i < MAX_WHERE_NAME; i++ )
    fprintf( fpout, "WhereName  %s~\n",
             race->where_name[i] );

  for ( x = 0; x < top_sn; x++ )
    {
      if ( !skill_table[x]->name || skill_table[x]->name[0] == '\0' )
        break;
      if ( (y=skill_table[x]->race_level[ra]) < LEVEL_IMMORTAL )
        fprintf( fpout, "Skill '%s' %d %d\n",
                 skill_table[x]->name, y, skill_table[x]->race_adept[ra] );
    }
  fprintf( fpout, "End\n" );
  fclose( fpout );
}

bool load_race_file( char *fname )
{
  char buf[MAX_STRING_LENGTH];
  char *word;
  char *race_name = NULL;
  bool fMatch;
  struct race_type *race;
  int ra = -1;
  FILE *fp;
  int i, wear=0;

  sprintf( buf, "%s%s", RACEDIR, fname );
  if ( ( fp = fopen( buf, "r" ) ) == NULL )
    {
      perror( buf );
      return FALSE;
    }

  CREATE( race, struct race_type, 1 );
  for(i=0;i<MAX_WHERE_NAME;i++)
    race->where_name[i] = where_name[i];

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

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

        case 'A':
          KEY( "Align",	race->alignment,		fread_number( fp )	);
          KEY( "AC_Plus",	race->ac_plus,		fread_number( fp )	);
          KEY( "Affected",	race->affected,		fread_bitvector( fp )	);
          KEY( "Attacks",	race->attacks,		fread_bitvector( fp )	);
          break;

        case 'C':
          KEY( "Con_Plus",	race->con_plus,		fread_number( fp )	);
          KEY( "Cha_Plus",	race->cha_plus,		fread_number( fp )	);
          KEY( "Classes",	race->class_restriction,		fread_number( fp )	);
          break;


        case 'D':
          KEY( "Dex_Plus",	race->dex_plus,		fread_number( fp )	);
          KEY( "Defenses",	race->defenses,		fread_bitvector( fp )	);
          break;

        case 'E':
          if ( !str_cmp( word, "End" ) )
            {
              fclose( fp );
              if ( ra < 0 || ra >= MAX_RACE )
                {
                  bug( "Load_race_file: Race (%s) bad/not found (%d)",
                       race->race_name ? race->race_name : "name not found", ra );
                  if( race_name )
                    STRFREE( race_name );
                  DISPOSE( race );
                  return FALSE;
                }
              race_table[ra] = race;
              if( race_name )
                STRFREE( race_name );
              return TRUE;
            }

          KEY( "Exp_Mult",	race->exp_multiplier,	fread_number( fp )	);

          break;


        case 'I':
          KEY( "Int_Plus",	race->int_plus,		fread_number( fp )	);
          break;

        case 'H':
          KEY( "Height",	race->height,		fread_number( fp )	);
          KEY( "Hit",		race->hit,		fread_number( fp )	);
          KEY( "HP_Regen",	race->hp_regen,		fread_number( fp )	);
          KEY( "Hunger_Mod",	race->hunger_mod,	fread_number( fp )	);
          break;

        case 'L':
          KEY( "Language",	race->language,		fread_number( fp )	);
          KEY( "Lck_Plus",	race->lck_plus,		fread_number( fp )	);
          break;


        case 'M':
          KEY( "Mana",	race->mana,		fread_number( fp )	);
          KEY( "Mana_Regen",	race->mana_regen,	fread_number( fp )	);
          KEY( "Min_Align",	race->minalign,		fread_number( fp )	);
          race->minalign = -1000;
          KEY( "Max_Align",	race->maxalign,		fread_number( fp )	);
          race->maxalign = -1000;
          break;

        case 'N':
          KEY( "Name",	race_name,	fread_string( fp )	);
          break;

        case 'R':
          KEY( "Race",	ra,			fread_number( fp )	);
          KEY( "Race_Recall",	race->race_recall,		fread_number( fp )	);
          KEY( "Resist",	race->resist,		fread_number( fp )	);
          break;

        case 'S':
          KEY( "Str_Plus",	race->str_plus,		fread_number( fp )	);
          KEY( "Suscept",	race->suscept,		fread_number( fp )	);
          if ( !str_cmp( word, "Skill" ) )
            {
              int sn, lev, adp;

              word = fread_word( fp );
              lev = fread_number( fp );
              adp = fread_number( fp );
              sn = skill_lookup( word );
              if ( ra < 0 || ra >= MAX_RACE )
                {
                  sprintf( buf, "load_race_file: Skill %s -- race bad/not found (%d)", word, ra );
                  bug( buf, 0 );
                }
              else
                if ( !IS_VALID_SN(sn) )
                  {
                    sprintf( buf, "load_race_file: Skill %s unknown", word);
                    bug( buf, 0 );
                  }
                else
                  {
                    skill_table[sn]->race_level[ra] = lev;
                    skill_table[sn]->race_adept[ra] = adp;
                  }
              fMatch = TRUE;
              break;
            }
          break;

        case 'T':
          KEY( "Thirst_Mod",	race->thirst_mod,	fread_number( fp )	);
          break;

        case 'W':
          KEY( "Weight",	race->weight,		fread_number( fp )	);
          KEY( "Wis_Plus",	race->wis_plus,		fread_number( fp )	);
          if ( !str_cmp( word, "WhereName" ) )
            {
              if ( ra < 0 || ra >= MAX_RACE )
                {
                  char *tmp;

                  sprintf( buf, "load_race_file: Title -- race bad/not found (%d)", ra );
                  bug( buf, 0 );
                  tmp = fread_string_nohash( fp );
                  DISPOSE( tmp );
                  tmp = fread_string_nohash( fp );
                  DISPOSE( tmp );
                }
              else
                if ( wear < MAX_WHERE_NAME )
                  {
                    race->where_name[wear] = fread_string_nohash( fp );
                    ++wear;
                  }
                else
                  bug( "load_race_file: Too many where_names" );
              fMatch = TRUE;
              break;
            }
          break;
        }

      if ( race_name != NULL)
        sprintf(race->race_name,"%-.16s",race_name);

      if ( !fMatch )
        {
          sprintf( buf, "load_race_file: no match: %s", word );
          bug( buf, 0 );
        }
    }
  return FALSE;
}

/*
 * Function used by qsort to sort skills
 */
int skill_comp( SKILLTYPE **sk1, SKILLTYPE **sk2 )
{
  SKILLTYPE *skill1 = (*sk1);
  SKILLTYPE *skill2 = (*sk2);

  if ( !skill1 && skill2 )
    return 1;
  if ( skill1 && !skill2 )
    return -1;
  if ( !skill1 && !skill2 )
    return 0;
  if ( skill1->type < skill2->type )
    return -1;
  if ( skill1->type > skill2->type )
    return 1;
  return strcmp( skill1->name, skill2->name );
}

/*
 * Sort the skill table with qsort
 */
void sort_skill_table()
{
  log_string( "Sorting skill table..." );
  qsort( &skill_table[1], top_sn-1, sizeof( SKILLTYPE * ),
         (int(*)(const void *, const void *)) skill_comp );
}


/*
 * Remap slot numbers to sn values
 */
void remap_slot_numbers()
{
  SKILLTYPE *skill;
  SMAUG_AFF *aff;
  char tmp[32];
  int sn;

  log_string( "Remapping slots to sns" );

  for ( sn = 0; sn <= top_sn; sn++ )
    {
      if ( (skill=skill_table[sn]) != NULL )
        {
          for ( aff = skill->affects; aff; aff = aff->next )
            if ( aff->location == APPLY_WEAPONSPELL
                 ||   aff->location == APPLY_WEARSPELL
                 ||   aff->location == APPLY_REMOVESPELL
                 ||   aff->location == APPLY_STRIPSN
                 ||   aff->location == APPLY_RECURRINGSPELL )
              {
                sprintf( tmp, "%d", slot_lookup(atoi(aff->modifier)) );
                DISPOSE(aff->modifier);
                aff->modifier = str_dup(tmp);
              }
        }
    }
}

/*
 * Write skill data to a file
 */
void fwrite_skill( FILE *fpout, SKILLTYPE *skill )
{
  SMAUG_AFF *aff;
  int modifier;

  fprintf( fpout, "Name         %s~\n",	skill->name	);
  fprintf( fpout, "Type         %s\n",	skill_tname[skill->type]);
  fprintf( fpout, "Info         %d\n",	skill->info	);
  fprintf( fpout, "Flags        %d\n",	skill->flags	);
  if ( skill->target )
    fprintf( fpout, "Target       %d\n",	skill->target	);
  /*
   * store as new minpos (minpos>=100 flags new style in character loading)
   */
  if ( skill->minimum_position )
    fprintf( fpout, "Minpos       %d\n",	skill->minimum_position+100 );
  if ( skill->spell_sector )
    fprintf( fpout, "Ssector      %d\n",	skill->spell_sector );
  if ( skill->saves )
    fprintf( fpout, "Saves        %d\n",	skill->saves    );
  if ( skill->slot )
    fprintf( fpout, "Slot         %d\n",	skill->slot	);
  if ( skill->min_mana )
    fprintf( fpout, "Mana         %d\n",	skill->min_mana );
  if ( skill->beats )
    fprintf( fpout, "Rounds       %d\n",	skill->beats	);
  if ( skill->range )
    fprintf( fpout, "Range        %d\n",	skill->range	);
  if ( skill->guild != -1 )
    fprintf( fpout, "Guild        %d\n",	skill->guild	);
  if( skill->skill_fun )
      fprintf( fpout, "Code         %s\n", skill->skill_fun_name );
   else if( skill->spell_fun )
      fprintf( fpout, "Code         %s\n", skill->spell_fun_name );

  fprintf( fpout, "Dammsg       %s~\n",	skill->noun_damage );
  if ( skill->msg_off && skill->msg_off[0] != '\0' )
    fprintf( fpout, "Wearoff      %s~\n",	skill->msg_off	);

  if ( skill->hit_char && skill->hit_char[0] != '\0' )
    fprintf( fpout, "Hitchar      %s~\n",	skill->hit_char );
  if ( skill->hit_vict && skill->hit_vict[0] != '\0' )
    fprintf( fpout, "Hitvict      %s~\n",	skill->hit_vict );
  if ( skill->hit_room && skill->hit_room[0] != '\0' )
    fprintf( fpout, "Hitroom      %s~\n",	skill->hit_room );
  if ( skill->hit_dest && skill->hit_dest[0] != '\0' )
    fprintf( fpout, "Hitdest      %s~\n",	skill->hit_dest );

  if ( skill->miss_char && skill->miss_char[0] != '\0' )
    fprintf( fpout, "Misschar     %s~\n",	skill->miss_char );
  if ( skill->miss_vict && skill->miss_vict[0] != '\0' )
    fprintf( fpout, "Missvict     %s~\n",	skill->miss_vict );
  if ( skill->miss_room && skill->miss_room[0] != '\0' )
    fprintf( fpout, "Missroom     %s~\n",	skill->miss_room );

  if ( skill->die_char && skill->die_char[0] != '\0' )
    fprintf( fpout, "Diechar      %s~\n",	skill->die_char );
  if ( skill->die_vict && skill->die_vict[0] != '\0' )
    fprintf( fpout, "Dievict      %s~\n",	skill->die_vict );
  if ( skill->die_room && skill->die_room[0] != '\0' )
    fprintf( fpout, "Dieroom      %s~\n",	skill->die_room );

  if ( skill->imm_char && skill->imm_char[0] != '\0' )
    fprintf( fpout, "Immchar      %s~\n",	skill->imm_char );
  if ( skill->imm_vict && skill->imm_vict[0] != '\0' )
    fprintf( fpout, "Immvict      %s~\n",	skill->imm_vict );
  if ( skill->imm_room && skill->imm_room[0] != '\0' )
    fprintf( fpout, "Immroom      %s~\n",	skill->imm_room );

  if ( skill->dice && skill->dice[0] != '\0' )
    fprintf( fpout, "Dice         %s~\n",	skill->dice );
  if ( skill->value )
    fprintf( fpout, "Value        %d\n",	skill->value );
  if ( skill->difficulty )
    fprintf( fpout, "Difficulty   %d\n",	skill->difficulty );
  if ( skill->participants )
    fprintf( fpout, "Participants %d\n",	skill->participants );
  if ( skill->components && skill->components[0] != '\0' )
    fprintf( fpout, "Components   %s~\n",	skill->components );
  if ( skill->teachers && skill->teachers[0] != '\0' )
    fprintf( fpout, "Teachers     %s~\n",	skill->teachers );
  for ( aff = skill->affects; aff; aff = aff->next )
    {
      fprintf( fpout, "Affect       '%s' %d ", aff->duration, aff->location );
      modifier = atoi(aff->modifier);
      if ((aff->location == APPLY_WEAPONSPELL
           ||   aff->location == APPLY_WEARSPELL
           ||   aff->location == APPLY_REMOVESPELL
           ||   aff->location == APPLY_STRIPSN
           ||   aff->location == APPLY_RECURRINGSPELL)
          &&   IS_VALID_SN(modifier) )
        fprintf( fpout, "'%d' ", skill_table[modifier]->slot );
      else
        fprintf( fpout, "'%s' ", aff->modifier );
      fprintf( fpout, "%d\n", aff->bitvector );
    }

  if ( skill->type != SKILL_HERB )
    {
      int y;
      int min = 1000;
      for ( y = 0; y < MAX_CLASS; y++ )
        if ( skill->skill_level[y] < min )
          min = skill->skill_level[y];

      fprintf( fpout, "Minlevel     %d\n",	min		);

      min = 1000;
      for ( y = 0; y < MAX_RACE; y++ )
        if ( skill->race_level[y] < min )
          min = skill->race_level[y];

    }
  fprintf( fpout, "End\n\n" );
}


/*
 * Save the skill table to disk
 */
void save_skill_table()
{
  int x;
  FILE *fpout;

  if ( (fpout=fopen( SKILL_FILE, "w" )) == NULL )
    {
      perror( SKILL_FILE );
      bug( "Cannot open skills.dat for writting", 0 );
      return;
    }

  for ( x = 0; x < top_sn; x++ )
    {
      if ( !skill_table[x]->name || skill_table[x]->name[0] == '\0' )
        break;
      fprintf( fpout, "#SKILL\n" );
      fwrite_skill( fpout, skill_table[x] );
    }
  fprintf( fpout, "#END\n" );
  fclose( fpout );
}

/*
 * Save the herb table to disk
 */
void save_herb_table()
{
  int x;
  FILE *fpout;

  if ( (fpout=fopen( HERB_FILE, "w" )) == NULL )
    {
      bug( "Cannot open herbs.dat for writting", 0 );
      perror( HERB_FILE );
      return;
    }

  for ( x = 0; x < top_herb; x++ )
    {
      if ( !herb_table[x]->name || herb_table[x]->name[0] == '\0' )
        break;
      fprintf( fpout, "#HERB\n" );
      fwrite_skill( fpout, herb_table[x] );
    }
  fprintf( fpout, "#END\n" );
  fclose( fpout );
}

/*
 * Save the socials to disk
 */
void save_socials()
{
  FILE *fpout;
  SOCIALTYPE *social;
  int x;

  if ( (fpout=fopen( SOCIAL_FILE, "w" )) == NULL )
    {
      bug( "Cannot open socials.dat for writting", 0 );
      perror( SOCIAL_FILE );
      return;
    }

  for ( x = 0; x < 27; x++ )
    {
      for ( social = social_index[x]; social; social = social->next )
        {
          if ( !social->name || social->name[0] == '\0' )
            {
              bug( "Save_socials: blank social in hash bucket %d", x );
              continue;
            }
          fprintf( fpout, "#SOCIAL\n" );
          fprintf( fpout, "Name        %s~\n",	social->name );
          if ( social->char_no_arg )
            fprintf( fpout, "CharNoArg   %s~\n",	social->char_no_arg );
          else
            bug( "Save_socials: NULL char_no_arg in hash bucket %d", x );
          if ( social->others_no_arg )
            fprintf( fpout, "OthersNoArg %s~\n",	social->others_no_arg );
          if ( social->char_found )
            fprintf( fpout, "CharFound   %s~\n",	social->char_found );
          if ( social->others_found )
            fprintf( fpout, "OthersFound %s~\n",	social->others_found );
          if ( social->vict_found )
            fprintf( fpout, "VictFound   %s~\n",	social->vict_found );
          if ( social->char_auto )
            fprintf( fpout, "CharAuto    %s~\n",	social->char_auto );
          if ( social->others_auto )
            fprintf( fpout, "OthersAuto  %s~\n",	social->others_auto );
          fprintf( fpout, "End\n\n" );
        }
    }
  fprintf( fpout, "#END\n" );
  fclose( fpout );
}

int get_skill( char *skilltype )
{
  if ( !str_cmp( skilltype, "Race" ) )
    return SKILL_RACIAL;
  if ( !str_cmp( skilltype, "Spell" ) )
    return SKILL_SPELL;
  if ( !str_cmp( skilltype, "Skill" ) )
    return SKILL_SKILL;
  if ( !str_cmp( skilltype, "Weapon" ) )
    return SKILL_WEAPON;
  if ( !str_cmp( skilltype, "Tongue" ) )
    return SKILL_TONGUE;
  if ( !str_cmp( skilltype, "Herb" ) )
    return SKILL_HERB;
  return SKILL_UNKNOWN;
}

/*
 * Save the commands to disk
 * Added flags Aug 25, 1997 --Shaddai
 */
void save_commands()
{
  FILE *fpout;
  CMDTYPE *command;
  int x;

  if ( (fpout=fopen( COMMAND_FILE, "w" )) == NULL )
    {
      bug( "Cannot open commands.dat for writing", 0 );
      perror( COMMAND_FILE );
      return;
    }

  for ( x = 0; x < 126; x++ )
    {
      for ( command = command_hash[x]; command; command = command->next )
        {
          if ( !command->name || command->name[0] == '\0' )
            {
              bug( "Save_commands: blank command in hash bucket %d", x );
              continue;
            }
          fprintf( fpout, "#COMMAND\n" );
          fprintf( fpout, "Name        %s~\n", command->name		);
          fprintf( fpout, "Code        %s\n", command->fun_name?command->fun_name:"" ); // Modded to use new field - Trax
          /* Oops I think this may be a bad thing so I changed it -- Shaddai */
          if ( command->position < 100 )
            fprintf( fpout, "Position    %d\n",	 command->position+100);
          else
            fprintf( fpout, "Position    %d\n",	 command->position);
          fprintf( fpout, "Level       %d\n",	 command->level		);
          fprintf( fpout, "Log         %d\n",	 command->log		);
          if ( command->flags )
            fprintf( fpout, "Flags       %d\n",  command->flags);
          fprintf( fpout, "End\n\n" );
        }
    }
  fprintf( fpout, "#END\n" );
  fclose( fpout );
}

SKILLTYPE *fread_skill( FILE *fp )
{
  char buf[MAX_STRING_LENGTH];
  char *word;
  bool fMatch;
  bool got_info = FALSE;
  SKILLTYPE *skill;
  int x;

  CREATE( skill, SKILLTYPE, 1 );
  skill->slot = 0;
  skill->min_mana = 0;
  for ( x = 0; x < MAX_CLASS; x++ )
    {
      skill->skill_level[x] = LEVEL_IMMORTAL;
      skill->skill_adept[x] = 95;
    }
  for ( x = 0; x < MAX_RACE; x++ )
    {
      skill->race_level[x] = LEVEL_IMMORTAL;
      skill->race_adept[x] = 95;
    }
  skill->guild = -1;
  skill->target = 0;
  skill->skill_fun = NULL;
  skill->spell_fun = NULL;
  skill->spell_sector = 0;


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

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

        case 'A':
          if ( !str_cmp( word, "Affect" ) )
            {
              SMAUG_AFF *aff;

              CREATE( aff, SMAUG_AFF, 1 );
              aff->duration = str_dup( fread_word( fp ) );
              aff->location = fread_number( fp );
              aff->modifier = str_dup( fread_word( fp ) );
              aff->bitvector = fread_number( fp );

              if ( !got_info )
                {
                  for ( x = 0; x < 32; x++ )
                    {
                      if ( IS_SET(aff->bitvector, 1 << x) )
                        {
                          aff->bitvector = x;
                          break;
                        }
                    }
                  if ( x == 32 )
                    aff->bitvector = -1;
                }
              aff->next = skill->affects;
              skill->affects = aff;
              fMatch = TRUE;
              break;
            }
          break;

        case 'C':
          if ( !str_cmp( word, "Class" ) )
            {
              int class = fread_number( fp );

              skill->skill_level[class] = fread_number( fp );
              skill->skill_adept[class] = fread_number( fp );
              fMatch = TRUE;
              break;
            }
          if ( !str_cmp( word, "Code" ) )
	    {
		SPELL_FUN *spellfun;
		DO_FUN *dofun;
		char *w = fread_word( fp );
		
		fMatch = TRUE;
		if( !str_prefix( "do_", w ) && ( dofun = skill_function(w) ) != skill_notfound )
		{
		   skill->skill_fun = dofun;
		   skill->spell_fun = NULL;
		   skill->skill_fun_name = str_dup(w);
		}
		else if( str_prefix( "do_", w ) && ( spellfun = spell_function(w) ) != spell_notfound )
		{
		   skill->spell_fun = spellfun;
		   skill->skill_fun = NULL;
		   skill->spell_fun_name = str_dup(w);
		}
		else
		{
		   bug( "fread_skill: unknown skill/spell %s", w );
		   skill->spell_fun = spell_null;
		}
		break;
	    }
          KEY( "Code",		skill->spell_fun, 	spell_function(fread_word(fp)) );
          KEY( "Components",	skill->components,	fread_string_nohash( fp ) );
          break;

        case 'D':
          KEY( "Dammsg",	skill->noun_damage,	fread_string_nohash( fp ) );
          KEY( "Dice",	skill->dice,		fread_string_nohash( fp ) );
          KEY( "Diechar",	skill->die_char,	fread_string_nohash( fp ) );
          KEY( "Dieroom",	skill->die_room,	fread_string_nohash( fp ) );
          KEY( "Dievict",	skill->die_vict,	fread_string_nohash( fp ) );
          KEY( "Difficulty",	skill->difficulty,	fread_number( fp ) );
          break;

        case 'E':
          if ( !str_cmp( word, "End" ) )
            {
              if ( skill->saves != 0 && SPELL_SAVE(skill) == SE_NONE )
                {
                  bug( "fread_skill(%s):  Has saving throw (%d) with no saving effect.",
                       skill->name, skill->saves );
                  SET_SSAV(skill, SE_NEGATE);
                }
              return skill;
            }
          break;

        case 'F':
          if ( !str_cmp( word, "Flags" ) )
            {
              skill->flags = fread_number(fp);
              /*
               * convert to new style			-Thoric
               */
              if ( !got_info )
                {
                  skill->info = skill->flags & (BV11-1);
                  if ( IS_SET(skill->flags, OLD_SF_SAVE_NEGATES) )
                    {
                      if ( IS_SET(skill->flags, OLD_SF_SAVE_HALF_DAMAGE) )
                        {
                          SET_SSAV(skill, SE_QUARTERDAM);
                          REMOVE_BIT(skill->flags, OLD_SF_SAVE_HALF_DAMAGE);
                        }
                      else
                        SET_SSAV(skill, SE_NEGATE);
                      REMOVE_BIT(skill->flags, OLD_SF_SAVE_NEGATES);
                    }
                  else
                    if ( IS_SET(skill->flags, OLD_SF_SAVE_HALF_DAMAGE) )
                      {
                        SET_SSAV(skill, SE_HALFDAM);
                        REMOVE_BIT(skill->flags, OLD_SF_SAVE_HALF_DAMAGE);
                      }
                  skill->flags >>= 11;
                }
              fMatch = TRUE;
              break;
            }
          break;

        case 'G':
          KEY( "Guild",	skill->guild,		fread_number( fp ) );
          break;

        case 'H':
          KEY( "Hitchar",	skill->hit_char,	fread_string_nohash( fp ) );
          KEY( "Hitdest",	skill->hit_dest,	fread_string_nohash( fp ) );
          KEY( "Hitroom",	skill->hit_room,	fread_string_nohash( fp ) );
          KEY( "Hitvict",	skill->hit_vict,	fread_string_nohash( fp ) );
          break;

        case 'I':
          KEY( "Immchar",	skill->imm_char,	fread_string_nohash( fp ) );
          KEY( "Immroom",	skill->imm_room,	fread_string_nohash( fp ) );
          KEY( "Immvict",	skill->imm_vict,	fread_string_nohash( fp ) );
          if ( !str_cmp( word, "Info" ) )
            {
              skill->info = fread_number(fp);
              got_info = TRUE;
              fMatch = TRUE;
              break;
            }
          break;

        case 'M':
          KEY( "Mana",	skill->min_mana,	fread_number( fp ) );
          if ( !str_cmp( word, "Minlevel" ) )
            {
              fread_to_eol( fp );
              fMatch = TRUE;
              break;
            }
          /*KEY( "Minpos",	skill->minimum_position, fread_number( fp ) ); */
          /*
           * 
           */
          if ( !str_cmp(word, "Minpos") )
            {
              fMatch = TRUE;
              skill->minimum_position = fread_number(fp);
              if ( skill->minimum_position < 100 )
                {
                  switch( skill->minimum_position )
                    {
                    default:
                    case 0:
                    case 1:
                    case 2:
                    case 3:
                    case 4:
                      break;
                    case 5:
                      skill->minimum_position=6;
                      break;
                    case 6:
                      skill->minimum_position=8;
                      break;
                    case 7:
                      skill->minimum_position=9;
                      break;
                    case 8:
                      skill->minimum_position=12;
                      break;
                    case 9:
                      skill->minimum_position=13;
                      break;
                    case 10:
                      skill->minimum_position=14;
                      break;
                    case 11:
                      skill->minimum_position=15;
                      break;
                    }
                }
              else
                skill->minimum_position-=100;
              break;
            }

          KEY( "Misschar",	skill->miss_char,	fread_string_nohash( fp ) );
          KEY( "Missroom",	skill->miss_room,	fread_string_nohash( fp ) );
          KEY( "Missvict",	skill->miss_vict,	fread_string_nohash( fp ) );
          break;

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

        case 'P':
          KEY( "Participants",skill->participants,	fread_number( fp ) );
          break;

        case 'R':
          KEY( "Range",	skill->range,		fread_number( fp ) );
          KEY( "Rounds",	skill->beats,		fread_number( fp ) );
          if ( !str_cmp( word, "Race" ) )
            {
              int race = fread_number( fp );

              skill->race_level[race] = fread_number( fp );
              skill->race_adept[race] = fread_number( fp );
              fMatch = TRUE;
              break;
            }
          break;

        case 'S':
          KEY( "Saves",	skill->saves,		fread_number( fp ) );
          KEY( "Slot",	skill->slot,		fread_number( fp ) );
          KEY( "Ssector",     skill->spell_sector,    fread_number( fp ) );
          break;

        case 'T':
          KEY( "Target",	skill->target,		fread_number( fp ) );
          KEY( "Teachers",	skill->teachers,	fread_string_nohash( fp ) );
          KEY( "Type",	skill->type,  get_skill(fread_word( fp ))  );
          break;

        case 'V':
          KEY( "Value",	skill->value,		fread_number( fp ) );
          break;

        case 'W':
          KEY( "Wearoff",	skill->msg_off,		fread_string_nohash( fp ) );
          break;
        }

      if ( !fMatch )
        {
          sprintf( buf, "Fread_skill: no match: %s", word );
          bug( buf, 0 );
        }
    }
}

void load_skill_table()
{
  FILE *fp;

  if ( ( fp = fopen( SKILL_FILE, "r" ) ) != NULL )
    {
      top_sn = 0;
      for ( ;; )
        {
          char letter;
          char *word;

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

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

          word = fread_word( fp );
          if ( !str_cmp( word, "SKILL"      ) )
            {
              if ( top_sn >= MAX_SKILL )
                {
                  bug( "load_skill_table: more skills than MAX_SKILL %d", MAX_SKILL );
                  fclose( fp );
                  return;
                }
              skill_table[top_sn++] = fread_skill( fp );
              continue;
            }
          else
            if ( !str_cmp( word, "END"	) )
              break;
            else
              {
                bug( "Load_skill_table: bad section.", 0 );
                continue;
              }
        }
      fclose( fp );
    }
  else
    {
      perror( SKILL_FILE );
      bug( "Cannot open skills.dat", 0 );
      exit(0);
    }
}


void load_herb_table()
{
  FILE *fp;

  if ( ( fp = fopen( HERB_FILE, "r" ) ) != NULL )
    {
      top_herb = 0;
      for ( ;; )
        {
          char letter;
          char *word;

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

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

          word = fread_word( fp );
          if ( !str_cmp( word, "HERB"      ) )
            {
              if ( top_herb >= MAX_HERB )
                {
                  bug( "load_herb_table: more herbs than MAX_HERB %d", MAX_HERB );
                  fclose( fp );
                  return;
                }
              herb_table[top_herb++] = fread_skill( fp );
              if ( herb_table[top_herb-1]->slot == 0 )
                herb_table[top_herb-1]->slot = top_herb-1;
              continue;
            }
          else
            if ( !str_cmp( word, "END"	) )
              break;
            else
              {
                bug( "Load_herb_table: bad section.", 0 );
                continue;
              }
        }
      fclose( fp );
    }
  else
    {
      bug( "Cannot open herbs.dat", 0 );
      exit(0);
    }
}

void fread_social( FILE *fp )
{
  char buf[MAX_STRING_LENGTH];
  char *word;
  bool fMatch;
  SOCIALTYPE *social;

  CREATE( social, SOCIALTYPE, 1 );

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

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

        case 'C':
          KEY( "CharNoArg",	social->char_no_arg,	fread_string_nohash(fp) );
          KEY( "CharFound",	social->char_found,	fread_string_nohash(fp) );
          KEY( "CharAuto",	social->char_auto,	fread_string_nohash(fp) );
          break;

        case 'E':
          if ( !str_cmp( word, "End" ) )
            {
              if ( !social->name )
                {
                  bug( "Fread_social: Name not found", 0 );
                  free_social( social );
                  return;
                }
              if ( !social->char_no_arg )
                {
                  bug( "Fread_social: CharNoArg not found", 0 );
                  free_social( social );
                  return;
                }
              add_social( social );
              return;
            }
          break;

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

        case 'O':
          KEY( "OthersNoArg",	social->others_no_arg,	fread_string_nohash(fp) );
          KEY( "OthersFound",	social->others_found,	fread_string_nohash(fp) );
          KEY( "OthersAuto",	social->others_auto,	fread_string_nohash(fp) );
          break;

        case 'V':
          KEY( "VictFound",	social->vict_found,	fread_string_nohash(fp) );
          break;
        }

      if ( !fMatch )
        {
          sprintf( buf, "Fread_social: no match: %s", word );
          bug( buf, 0 );
        }
    }
}

void load_socials()
{
  FILE *fp;

  if ( ( fp = fopen( SOCIAL_FILE, "r" ) ) != NULL )
    {
      top_sn = 0;
      for ( ;; )
        {
          char letter;
          char *word;

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

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

          word = fread_word( fp );
          if ( !str_cmp( word, "SOCIAL"      ) )
            {
              fread_social( fp );
              continue;
            }
          else
            if ( !str_cmp( word, "END"	) )
              break;
            else
              {
                bug( "Load_socials: bad section.", 0 );
                continue;
              }
        }
      fclose( fp );
    }
  else
    {
      bug( "Cannot open socials.dat", 0 );
      exit(0);
    }
}

/*
 *  Added the flags Aug 25, 1997 --Shaddai
 */

void fread_command( FILE *fp )
{
  char buf[MAX_STRING_LENGTH];
  char *word;
  bool fMatch;
  CMDTYPE *command;

  CREATE( command, CMDTYPE, 1 );
  command->lag_count = 0; /* can't have caused lag yet... FB */
  command->flags   = 0;  /* Default to no flags set */

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

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

        case 'C':
	    KEY( "Code",	command->fun_name, str_dup( fread_word( fp ) ) );
	    break;

	case 'E':
	    if ( !str_cmp( word, "End" ) )
	    {
		if( !command->name )
		{
		   bug( "%s", "Fread_command: Name not found" );
		   free_command( command );
		   return;
		}
		if( !command->fun_name )
		{
		   bug( "fread_command: No function name supplied for %s", command->name );
		   free_command( command );
		   return;
		}
		/*
	 	 * Mods by Trax
		 * Fread in code into char* and try linkage here then
		 * deal in the "usual" way I suppose..
		 */
	      command->do_fun = skill_function( command->fun_name );
		if( command->do_fun == skill_notfound )
		{
		   bug( "Fread_command: Function %s not found for %s", command->fun_name, command->name );
		   free_command( command );
		   return;
		}
		add_command( command );
		return;
	    }
	    break;

        case 'F':
          KEY ("Flags", command->flags, fread_number (fp));
          break;

        case 'L':
          KEY( "Level",	command->level,		fread_number(fp) );
          KEY( "Log",		command->log,		fread_number(fp) );
          break;

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

        case 'P':
          /* KEY( "Position",	command->position,	fread_number(fp) ); */
          if ( !str_cmp(word, "Position") )
            {
              fMatch = TRUE;
              command->position = fread_number(fp);
              if( command->position<100 )
                {
                  switch(command->position)
                    {
                    default:
                    case 0:
                    case 1:
                    case 2:
                    case 3:
                    case 4:
                      break;
                    case 5:
                      command->position=6;
                      break;
                    case 6:
                      command->position=8;
                      break;
                    case 7:
                      command->position=9;
                      break;
                    case 8:
                      command->position=12;
                      break;
                    case 9:
                      command->position=13;
                      break;
                    case 10:
                      command->position=14;
                      break;
                    case 11:
                      command->position=15;
                      break;
                    }
                }
              else
                command->position-=100;
              break;
            }
          break;
        }

      if ( !fMatch )
        {
          sprintf( buf, "Fread_command: no match: %s", word );
          bug( buf, 0 );
        }
    }
}

void load_commands()
{
  FILE *fp;

  if ( ( fp = fopen( COMMAND_FILE, "r" ) ) != NULL )
    {
      top_sn = 0;
      for ( ;; )
        {
          char letter;
          char *word;

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

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

          word = fread_word( fp );
          if ( !str_cmp( word, "COMMAND"      ) )
            {
              fread_command( fp );
              continue;
            }
          else
            if ( !str_cmp( word, "END"	) )
              break;
            else
              {
                bug( "Load_commands: bad section.", 0 );
                continue;
              }
        }
      fclose( fp );
    }
  else
    {
      bug( "Cannot open commands.dat", 0 );
      exit(0);
    }

}

void save_classes()
{
  int x;

  for ( x = 0; x < MAX_PC_CLASS; x++ )
    write_class_file( x );
}

/*
 * Tongues / Languages loading/saving functions			-Altrag
 */
void fread_cnv(FILE * fp, LCNV_DATA **first_cnv, LCNV_DATA **last_cnv)
{
  LCNV_DATA *cnv;
  char letter;

  for (;;)
    {
      letter = fread_letter(fp);
      if (letter == '~' || letter == EOF)
        break;
      ungetc(letter, fp);
      CREATE(cnv, LCNV_DATA, 1);

      cnv->old = str_dup(fread_word(fp));
      cnv->olen = strlen(cnv->old);
      cnv->new = str_dup(fread_word(fp));
      cnv->nlen = strlen(cnv->new);
      fread_to_eol(fp);
      LINK(cnv, *first_cnv, *last_cnv, next, prev);
    }
}

void load_tongues()
{
  FILE *fp;
  LANG_DATA *lng;
  char *word;
  char letter;

  if (!(fp=fopen(TONGUE_FILE, "r")))
    {
      perror("Load_tongues");
      return;
    }
  for (;;)
    {
      letter = fread_letter(fp);
      if (letter == EOF)
        return;
      else if (letter == '*')
        {
          fread_to_eol(fp);
          continue;
        }
      else if (letter != '#')
        {
          bug("Letter '%c' not #.", letter);
          exit(0);
        }
      word = fread_word(fp);
      if (!str_cmp(word, "end"))
        break;
      fread_to_eol(fp);
      CREATE(lng, LANG_DATA, 1);
      lng->name = STRALLOC(word);
      fread_cnv(fp, &lng->first_precnv, &lng->last_precnv);
      lng->alphabet = fread_string(fp);
      fread_cnv(fp, &lng->first_cnv, &lng->last_cnv);
      fread_to_eol(fp);
      LINK(lng, first_lang, last_lang, next, prev);
    }
  fclose(fp);
  fp=NULL;
  return;
}

void fwrite_langs(void)
{
  FILE *fp;
  LANG_DATA *lng;
  LCNV_DATA *cnv;

  if (!(fp=fopen(TONGUE_FILE, "w")))
    {
      perror("fwrite_langs");
      return;
    }
  for (lng = first_lang; lng; lng = lng->next)
    {
      fprintf(fp, "#%s\n", lng->name);
      for (cnv = lng->first_precnv; cnv; cnv = cnv->next)
        fprintf(fp, "'%s' '%s'\n", cnv->old, cnv->new);
      fprintf(fp, "~\n%s~\n", lng->alphabet);
      for (cnv = lng->first_cnv; cnv; cnv = cnv->next)
        fprintf(fp, "'%s' '%s'\n", cnv->old, cnv->new);
      fprintf(fp, "\n");
    }
  fprintf(fp, "#end\n\n");
  fclose(fp);
  return;
}

void copy_files_contents(FILE *fsource, FILE *fdestination)
{
  int ch;
  int cnt = 1;

  for (;;)
    {
      ch = fgetc( fsource );
      if (!feof(fsource))
        {
          fputc( ch, fdestination);
          if (ch == '\n')
            {
              cnt++;
              if (cnt >= LAST_FILE_SIZE) //limit size of this file please :-)
                break;
            }
        }
      else
        break;
    }
}

void write_last_file(char *entry)
{
  FILE *fpout;
  FILE *fptemp;
  char filename[MAX_INPUT_LENGTH];
  char tempname[MAX_INPUT_LENGTH];

  sprintf(filename, "%s", LAST_LIST);
  sprintf(tempname, "%s", LAST_TEMP_LIST);
  if ((fptemp = fopen(tempname, "w")) == NULL)
    {
      bug("Cannot open: %s for writing", tempname);
      return;
    }
  fprintf(fptemp, "%s\n", entry); //adds new entry to top of the file
  if ((fpout = fopen(filename, "r")) != NULL)
    {
      copy_files_contents(fpout, fptemp); //copy the rest to the file
      fclose(fpout); //close the files since writing is done
    }
  fclose(fptemp);

  if (remove
      (filename) != 0 && fopen(filename, "r") != NULL)
    {
      bug("Do not have permission to delete the %s file", filename);
      return;
    }
  if (rename(tempname, filename) != 0)
    {
      bug("Do not have permission to rename the %s file", tempname);
      return;
    }
  return;
}

void read_last_file(CHAR_DATA *ch, int count, char *name)
{
  FILE *fpout;
  char filename[MAX_INPUT_LENGTH];
  char charname[100];
  int cnt = 0;
  int letter = 0;
  char *ln;
  char *c;
  char d, e;
  struct tm *tme;
  time_t now;
  char day[MAX_INPUT_LENGTH];
  char sday[5];
  int fnd = 0;

  sprintf(filename, "%s", LAST_LIST);
  if ((fpout = fopen(filename, "r")) == NULL)
    {
      send_to_char("There is no last file to look at.\n\r", ch);
      return;
    }

  for (;;)
    {
      if (feof(fpout))
        {
          fclose(fpout);
          ch_printf(ch, "---------------------------------------------------------------------------\n\r%d Entries Listed.\n\r", cnt);
          return;
        }
      else
        {
          if (count == -2 || ++cnt <= count || count == -1)
            {
              ln = fread_line(fpout);
              strcpy(charname, "");
              if (name) //looking for a certain name
                {
                  c = ln;
                  for (;;)
                    {
                      if (isalpha(*c) && !isspace(*c))
                        {
                          charname[letter] = *c;
                          letter++;
                          c++;
                        }
                      else
                        {
                          charname[letter] = '\0';
                          if (!str_cmp(charname, name))
                            {
                              ch_printf(ch, "%s", ln);
                              letter = 0;
                              strcpy(charname, "");
                              break;
                            }
                          else
                            {
                              if (!feof(fpout))
                                {
                                  fread_line(fpout);
                                  c = ln;
                                  letter = 0;
                                  strcpy(charname, "");
                                  continue;
                                }
                              else
                                {
                                  cnt--;
                                  break;
                                }
                            }
                        }
                    }
                }
              else if (count == -2) //only today's entries
                {
                  c = ln;
                  now = time(0);
                  tme = localtime(&now);
                  strftime(day, 10, "%d", tme);
                  for (;;)
                    {
                      if (!isdigit(*c))
                        {
                          c++;
                        }
                      else
                        {
                          d = *c;
                          c++;
                          e = *c;
                          sprintf(sday, "%c%c", d, e);
                          if (!str_cmp(sday, day))
                            {
                              fnd = 1;
                              cnt++;
                              ch_printf(ch, "%s", ln);
                              break;
                            }
                          else
                            {
                              if (fnd == 1)
                                {
                                  fclose(fpout);
                                  ch_printf(ch, "---------------------------------------------------------------------------\n\r%d Entries Listed.\n\r", cnt);
                                  return;
                                }
                              else
                                break;
                            }
                        }
                    }
                }
              else
                {
                  ch_printf(ch, "%s", ln);
                }

            }
          else
            {
              fclose(fpout);
              ch_printf(ch, "--------------------------------------------------------------------------\n\r%d Entries Listed.\n\r", count);
              return;
            }
        }
    }
}