/
Sapphire/bin/
Sapphire/db/
Sapphire/db/OLC_rooms/
Sapphire/db/abi/
Sapphire/db/em_src/
Sapphire/db/helps/
Sapphire/db/helps/emman/ifunc/
Sapphire/db/npcs/Tatt/
Sapphire/db/objects/Tatt/
Sapphire/db/q_data/
Sapphire/db/rooms/Tatt/
Sapphire/doc/
Sapphire/doc/em/
Sapphire/etc/
Sapphire/src/abic/
Sapphire/src/areacon/
Sapphire/src/client/
Sapphire/src/embc/
Sapphire/src/emi/
Sapphire/src/emi/test/
Sapphire/src/include/
Sapphire/src/sapphire/em/
Sapphire/src/tcon/
/*
 * Copyright (C) 1995-1997 Christopher D. Granz
 *
 * This header may not be removed.
 *
 * Refer to the file "License" included in this package for further
 * information and before using any of the following.
 */

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <time.h>
#include <fcntl.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/resource.h>

#include "sapphire.h"
#include "emerald.h"


/*
 * Prototypes
 */

#define NPCID                                             NPC_INDEX_DATA
#define OID                                               OBJ_INDEX_DATA
#define RID                                              ROOM_INDEX_DATA
#define NPCRD                                             NPC_RESET_DATA
#define ORD                                               OBJ_RESET_DATA
#define OD                                                      OBJ_DATA

static void      main_loop                                     ( void );
static void      update                                        ( void );
static void      translate_numbers                             ( void );
static OD *      translate_obj_resets                  ( ORD *, RID * );
static void      free_obj_reset_list                         ( ORD ** );
static int       npc_inherit                                ( NPCID * );
static int       obj_inherit                                  ( OID * );
static int       fix_exit                                ( RID *, int );

#undef NPCID
#undef OID
#undef RID
#undef NPCRD
#undef ORD
#undef OD


/*
 * Globals
 */
extern char *                                          pStrBuffersBegin;
extern char *                                               pStrBuffers;
extern int                                               iMaxStrBuffers;
extern int                                               iNumStrBuffers;

extern int                                                iMaxFragments;

extern bool                                                   bIsPrompt;

#include <kvm.h>
#include <sys/param.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>

/*
 * Functions
 */
int main( int iArgCount, char *pArgV[], char *pEnvV[] )
{
    struct timeval tTimeNow;
    struct rlimit rl;
    bool bLogToScreen    = FALSE;

    if ( pArgV[1] == NULL )
    {
        fprintf( stderr, "\nUsage: %s <boot file> [-l]\n", pArgV[0] );
        return ( 0 );
    }

    if ( pArgV[2] != NULL && pArgV[2][0] == '-' )
    {
        if ( pArgV[2][1] == 'l' && pArgV[2][2] == '\0' )
            bLogToScreen = TRUE;
        else
        {
            fprintf( stderr, "\nInvalid switch: `%s'.\n", pArgV[2] );
            return ( 1 );
        }
    }

    pLogFile             = stdout;
    bErrorsFound         = FALSE;
    bWarningsFound       = FALSE;
    bStrComparing        = FALSE;

    /*
     * Set max coredump size.
     */
    getrlimit( RLIMIT_CORE, &rl );
    rl.rlim_cur          = rl.rlim_max;
    setrlimit( RLIMIT_CORE, &rl );

    init_signal_handlers( );

    pProgramName         = str_dup( pArgV[0] );
    pBootFilename        = str_dup( pArgV[1] );

    gettimeofday( &tTimeNow, NULL );
    tCurrentTime         = (time_t) tTimeNow.tv_sec;
    init_random( );

    load_config( pBootFilename );

    if ( iTelnetPort <= 1024 || iBinaryPort <= 1024
      || iMUDCommPort <= 1024 )
        sap_fatal( "Port numbers must be above 1024." );

    if ( iTelnetPort == iBinaryPort || iTelnetPort == iMUDCommPort
      || iBinaryPort == iMUDCommPort )
        sap_fatal(
          "Telnet, binary and MUD-Comm ports must be different." );

    /*
     * Initialize memory based on settings from the config file.
     */
    mem_init( );
    atexit( mem_clean_up );

    /*
     * Create a log file (if needed).
     */
    if ( bLogToScreen != TRUE )
        log_init( );

    fprintf( stderr, COPYRIGHT_NOTICE );

    if ( bLogToScreen != TRUE )
        fprintf( stderr, "Making inititial outgoing MUD-Comm connections."
          "  Please wait ...\n" );

    load_mudcomm_file( pMUDCommFilename );

    if ( bLogToScreen != TRUE )
        fprintf( stderr,
          "Finished making inititial outgoing MUD-Comm connections.\n\n" );

    lprintf(
      "\n  Finished making initital outgoing MUD-Comm connections." );

    if ( bLogToScreen != TRUE )
        fprintf( stderr, "Opening telnet socket.  Please wait ...\n" );

    init_telnet_socket( iTelnetPort );

    if ( bLogToScreen != TRUE )
        fprintf( stderr, "Telnet socket successfully opened.\n\n" );

    lprintf( "Telnet socket successfully opened." );

    if ( bLogToScreen != TRUE )
        fprintf( stderr, "Opening binary socket.  Please wait ...\n" );

    init_binary_socket( iBinaryPort );

    if ( bLogToScreen != TRUE )
        fprintf( stderr, "Binary socket successfully opened.\n\n" );

    lprintf( "Binary socket successfully opened." );

    if ( bLogToScreen != TRUE )
        fprintf( stderr, "Opening MUD-Comm socket.  Please wait ...\n" );

    init_mudcomm_socket( iMUDCommPort );

    if ( bLogToScreen != TRUE )
        fprintf( stderr, "MUD-Comm socket successfully opened.\n\n" );

    lprintf( "MUD-Comm socket successfully opened." );

    if ( bLogToScreen != TRUE )
        fprintf( stderr, "Loading database files.  Please wait ...\n" );

    load_database( );

    if ( bLogToScreen != TRUE )
        fprintf( stderr, "All database files successfully loaded.\n\n" );

    lprintf( "All database files successfully loaded." );

    if ( bLogToScreen != TRUE )
        fprintf( stderr,
          "Ready to run (pid %ld) using ports %d, %d and %d.\n",
          (long) getpid( ), iTelnetPort, iBinaryPort, iMUDCommPort );

    lprintf( "\n  "
      "Ready to run (pid %ld) using ports %d, %d and %d.",
      (long) getpid( ), iTelnetPort, iBinaryPort, iMUDCommPort );

    bRunning             = TRUE;
    main_loop( );

    if ( sTelnetControl > -1 )
        close( sTelnetControl );

    if ( sBinaryControl > -1 )
        close( sBinaryControl );

    if ( bLogToScreen != TRUE )
        fprintf( stderr, "Game Over.\n" );

    lprintf( "Game Over.\n" );
    return ( 0 );
}


static void main_loop( void )
{
    static time_t tWaitTime;
    struct timeval tNullTime;
    struct timeval tStallTime;
    struct timeval tTime;
    TERM_DATA *pTerm;
    TERM_DATA *pTermNext;
    MUD_COMM_DATA *pMUDComm;
    MUD_COMM_DATA *pMUDCommNext;
    CHILDP_DATA *pChild;
    CHILDP_DATA *pChildNext;
    fd_set fdsStdin;
    fd_set fdsTelnetIn;
    fd_set fdsTelnetOut;
    fd_set fdsTelnetExc;
    fd_set fdsBinaryIn;
    fd_set fdsBinaryOut;
    fd_set fdsBinaryExc;
    fd_set fdsMUDCommIn;
    fd_set fdsMUDCommOut;
    fd_set fdsMUDCommExc;
    sapsocket sTopTelnet;
    sapsocket sTopBinary;
    sapsocket sTopMUDComm;
    int i;

    if ( setjmp( jbReboot ) != 0 ) /* Not currently used. */
        bRunning     = TRUE;

    zero_out( &tNullTime, sizeof( tNullTime ) );
    tStallTime.tv_sec  = 0;
    tStallTime.tv_usec = ( lLoopStallTime / 3 );
    tWaitTime          = 0;

    while ( bRunning == TRUE )
    {
        /*
         * Deal with reading stdin if the user is in command mode.
         */
        if ( bIsPrompt == TRUE )
        {
            FD_ZERO( &fdsStdin );
            FD_SET( STDIN_FILENO, &fdsStdin );

            if ( select( ( STDIN_FILENO + 1 ), &fdsStdin, NULL, NULL,
              &tNullTime ) < 0 )
                sap_error( "Poll: %s.", strerror( errno ) );

            if ( FD_ISSET( STDIN_FILENO, &fdsStdin ) )
                process_mucs_cmd( );
        }

        if ( gettimeofday( &tTime, NULL ) < 0 )
            sap_fatal( "Get time: %s.", strerror( errno ) );

        tCurrentTime = (time_t) tTime.tv_sec;

        /*
         * Poll all socket descriptors.
         */
        FD_ZERO( &fdsTelnetIn );
        FD_ZERO( &fdsTelnetOut );
        FD_ZERO( &fdsTelnetExc );
        FD_ZERO( &fdsBinaryIn );
        FD_ZERO( &fdsBinaryOut );
        FD_ZERO( &fdsBinaryExc );
        FD_ZERO( &fdsMUDCommIn );
        FD_ZERO( &fdsMUDCommOut );
        FD_ZERO( &fdsMUDCommExc );

        FD_SET( sTelnetControl, &fdsTelnetIn );
        FD_SET( sBinaryControl, &fdsBinaryIn );
        FD_SET( sMUDCommControl, &fdsMUDCommIn );

        sTopTelnet         = sTelnetControl;
        sTopBinary         = sBinaryControl;
        sTopMUDComm        = sMUDCommControl;

        for ( pTerm = pTermList; pTerm != NULL; pTerm = pTerm->pNext )
        {
            sTopTelnet     = ( sTopTelnet > pTerm->sTelnetSocket
                             ? sTopTelnet : pTerm->sTelnetSocket );

            FD_SET( pTerm->sTelnetSocket, &fdsTelnetIn );
            FD_SET( pTerm->sTelnetSocket, &fdsTelnetOut );
            FD_SET( pTerm->sTelnetSocket, &fdsTelnetExc );

            if ( pTerm->iTermType == CLIENT_SAPPHIRE_COMPATIBLE )
            {
                sTopBinary = ( sTopBinary > pTerm->sBinarySocket
                             ? sTopBinary : pTerm->sBinarySocket );

                FD_SET( pTerm->sBinarySocket, &fdsBinaryIn );
                FD_SET( pTerm->sBinarySocket, &fdsBinaryOut );
                FD_SET( pTerm->sBinarySocket, &fdsBinaryExc );
            }
        }

        for ( pMUDComm = pMUDCommList; pMUDComm != NULL;
          pMUDComm = pMUDComm->pNext )
        {
            sTopMUDComm    = ( sTopMUDComm > pMUDComm->sMUDCommSocket
                             ? sTopMUDComm : pMUDComm->sMUDCommSocket );

            FD_SET( pMUDComm->sMUDCommSocket, &fdsMUDCommIn );
            FD_SET( pMUDComm->sMUDCommSocket, &fdsMUDCommOut );
            FD_SET( pMUDComm->sMUDCommSocket, &fdsMUDCommExc );
        }

        do
        {
            errno          = 0;
            select( ( sTopBinary + 1 ), &fdsBinaryIn, &fdsBinaryOut,
              &fdsBinaryExc, &tStallTime );
        }
        while ( errno == EINTR );

        if ( errno != 0 )
            sap_error( "Poll: %s.", strerror( errno ) );

        do
        {
            errno          = 0;
            select( ( sTopTelnet + 1 ), &fdsTelnetIn, &fdsTelnetOut,
              &fdsTelnetExc, &tStallTime );
        }
        while ( errno == EINTR );

        if ( errno != 0 )
            sap_error( "Poll: %s.", strerror( errno ) );

        do
        {
            errno          = 0;
            select( ( sTopMUDComm + 1 ), &fdsMUDCommIn, &fdsMUDCommOut,
              &fdsMUDCommExc, &tStallTime );
        }
        while ( errno == EINTR );

        if ( errno != 0 )
            sap_error( "Poll: %s.", strerror( errno ) );

        /*
         * Deal with new connections.
         */
        if ( FD_ISSET( sTelnetControl, &fdsTelnetIn ) )
            accept_connection( );

        if ( FD_ISSET( sBinaryControl, &fdsBinaryIn ) )
            accept_binary_connection( );

        if ( FD_ISSET( sMUDCommControl, &fdsMUDCommIn ) )
            accept_mudcomm_connection( );

        for ( pTerm = pTermList; pTerm != NULL; pTerm = pTermNext )
        {
            pTermNext                = pTerm->pNext;

            if ( FD_ISSET( pTerm->sTelnetSocket, &fdsTelnetExc ) )
            {
                FD_CLR( pTerm->sTelnetSocket, &fdsTelnetIn );
                FD_CLR( pTerm->sTelnetSocket, &fdsTelnetOut );
                pTerm->pOutBuffer[0] = '\0';
                clear_binary_transfer( pTerm );
                close_connection( &pTerm );
                continue;
            }

            if ( pTerm->iTermType == CLIENT_SAPPHIRE_COMPATIBLE
              && FD_ISSET( pTerm->sBinarySocket, &fdsBinaryExc ) )
            {
                FD_CLR( pTerm->sBinarySocket, &fdsBinaryIn );
                FD_CLR( pTerm->sBinarySocket, &fdsBinaryOut );
                pTerm->pOutBuffer[0] = '\0';
                clear_binary_transfer( pTerm );
                close_connection( &pTerm );
            }
        }

        for ( pMUDComm = pMUDCommList; pMUDComm; pMUDComm = pMUDCommNext )
        {
            pMUDCommNext             = pMUDComm->pNext;

            if ( FD_ISSET( pMUDComm->sMUDCommSocket, &fdsMUDCommExc ) )
            {
                FD_CLR( pMUDComm->sMUDCommSocket, &fdsMUDCommIn );
                FD_CLR( pMUDComm->sMUDCommSocket, &fdsMUDCommOut );
                close_mudcomm_connection( &pMUDComm );
                continue;
            }
        }

        free_buffers( );

        /*
         * Input.
         */
        for ( pTerm = pTermList; pTerm != NULL; pTerm = pTermNext )
        {
            pTermNext                      = pTerm->pNext;
            pTerm->bFoundCmd               = FALSE;

            if ( FD_ISSET( pTerm->sTelnetSocket, &fdsTelnetIn )
              && read_string( pTerm ) != TRUE )
            {
                FD_CLR( pTerm->sTelnetSocket, &fdsTelnetOut );
                pTerm->pOutBuffer[0]       = '\0';
                clear_binary_transfer( pTerm );
                close_connection( &pTerm );
                continue;
            }

            /*
             * If a terminal has a child running all input is sent
             * directly to the child's stdin; no buffering is done.
             */
            if ( pTerm->pChildP != NULL )
            {
                write( pTerm->pChildP->iStdin[1], pTerm->pInBuffer,
                  strlen( pTerm->pInBuffer ) );
                pTerm->pInBuffer[0]        = '\0';
                continue;
            }

            if ( pTerm->iTermType == CLIENT_SAPPHIRE_COMPATIBLE
              && FD_ISSET( pTerm->sBinarySocket, &fdsBinaryIn )
              && read_binary_data( pTerm ) != TRUE )
            {
                FD_CLR( pTerm->sBinarySocket, &fdsBinaryOut );
                pTerm->pOutBuffer[0]       = '\0';
                clear_binary_transfer( pTerm );
                close_connection( &pTerm );
                continue;
            }

            /*
             * Get the next command.
             */
            read_string_buffer( pTerm );

            if ( pTerm->pInCommand[0] != '\0' )
            {
                if ( pTerm->pPageString != NULL )
                {
                    pTerm->bFoundCmd   = TRUE;
                    process_page_string( pTerm, pTerm->pInCommand );
                }
                else if ( pTerm->ppEditString != NULL )
                {
                    pTerm->bFoundCmd   = TRUE;
                    process_text_editor_string( pTerm,
                      pTerm->pInCommand );
                }
                else
                {
                    switch ( pTerm->iConState )
                    {
                      case CON_PLAYING      :
                          pTerm->bFoundCmd   = TRUE;
                          process_cmd( ( pTerm->pControlled != NULL
                            ? pTerm->pControlled : pTerm->pChar ),
                            pTerm->pInCommand );
                          break;

                      case CON_NPC_EDITOR   :
                          pTerm->bFoundCmd   = TRUE;
                          process_npc_editor_cmd( pTerm,
                            pTerm->pInCommand );
                          break;

                      case CON_OBJECT_EDITOR:
                          pTerm->bFoundCmd   = TRUE;
                          process_object_editor_cmd( pTerm,
                            pTerm->pInCommand );
                          break;

                      case CON_ROOM_EDITOR  :
                          pTerm->bFoundCmd   = TRUE;
                          process_room_editor_cmd( pTerm,
                            pTerm->pInCommand );
                          break;

                      case CON_SYSTEM_SHELL :
                          pTerm->bFoundCmd   = TRUE;
                          process_shell_cmd( pTerm, pTerm->pInCommand );
                          break;

                      default               :
                          process_login( pTerm, pTerm->pInCommand );
                          break;
                    }
                }

                if ( pTerm->pInCommand != NULL )
                {
                    pTerm->pInCommand[0]   = '\0';
                    pTermNext              = pTerm->pNext;
                }
            }
        }

        for ( pMUDComm = pMUDCommList; pMUDComm; pMUDComm = pMUDCommNext )
        {
            pMUDCommNext         = pMUDComm->pNext;

            if ( FD_ISSET( pMUDComm->sMUDCommSocket, &fdsMUDCommIn )
              && read_mudcomm_data( pMUDComm ) != TRUE )
            {
                FD_CLR( pMUDComm->sMUDCommSocket, &fdsMUDCommOut );
                close_mudcomm_connection( &pMUDComm );
                continue;
            }

            if ( pMUDComm->iInBufSize == 0 )
                continue;

            if ( pMUDComm->iConState == MUD_COMM_CON_IDENT )
            {
                MUD_COMM_DATA *pMUDComm2;
                char cBuf[MAX_STRING];

                strncpy( cBuf, pMUDComm->pInBuffer, MAX_STRING );
                cBuf[MAX_STRING - 1] = '\0';

                /*
                 * Hmm... a MUD with no name?  I don't think so.
                 */
                if ( cBuf[0] == '\0' )
                    close_mudcomm_connection( &pMUDComm );
                else
                {
                    for ( pMUDComm2 = pMUDCommList; pMUDComm2 != NULL;
                      pMUDComm2 = pMUDComm2->pNext )
                    {
                        /*
                         * We don't want to have two connections from the
                         * same MUD :)
                         */
                        if ( strcmp( cBuf, pMUDComm2->pMUDName ) == 0 )
                        {
                            close_mudcomm_connection( &pMUDComm2 );
                            break;
                        }
                    }

                    pMUDComm->pMUDName   = str_dup( cap_first( cBuf ) );
                    pMUDComm->iConState  = MUD_COMM_CON_READY;
                }
            }
            else if ( pMUDComm->iConState == MUD_COMM_CON_READY )
            {
                switch ( pMUDComm->pInBuffer[0] )
                {
                  case MUD_COMM_CMD_WIZ_CHANNEL :
                      mudcomm_cmd_wiz_channel( pMUDComm );
                      break;

                  case MUD_COMM_CMD_CHAT_CHANNEL:
                      mudcomm_cmd_chat_channel( pMUDComm );
                      break;

                  case MUD_COMM_CMD_TELL_CHANNEL:
                      mudcomm_cmd_tell_channel( pMUDComm );
                      break;

                  case MUD_COMM_CMD_MAIL        :
                      mudcomm_cmd_mail( pMUDComm );
                      break;

                  case MUD_COMM_CMD_WHO         :
                      mudcomm_cmd_who( pMUDComm );
                      break;

                  case MUD_COMM_CMD_USERS       :
                      mudcomm_cmd_users( pMUDComm );
                      break;

                  case MUD_COMM_CMD_REPLY_MAIL  :
                      mudcomm_cmd_reply_mail( pMUDComm );
                      break;

                  case MUD_COMM_CMD_REPLY_TELL  :
                      mudcomm_cmd_reply_tell( pMUDComm );
                      break;

                  case MUD_COMM_CMD_REPLY_WHO   :
                      mudcomm_cmd_reply_who( pMUDComm );
                      break;

                  case MUD_COMM_CMD_REPLY_USERS :
                      mudcomm_cmd_reply_users( pMUDComm );
                      break;

                  default                       : break;
                }
            }

            pMUDComm->iInBufSize = 0;
        }

        free_buffers( );

        /*
         * If at least PULSE_HEART_BEAT seconds have gone by; update.
         */
        if ( tCurrentTime > ( tWaitTime + PULSE_HEART_BEAT - 1 ) )
        {
            update( );
            tWaitTime = tCurrentTime;
        }

        /*
         * Deal with zombie processes.
         */
        for ( pChild = pChildren; pChild != NULL; pChild = pChildNext )
        {
            pChildNext                   = pChild->pNext;

            if ( die_child( pChild, &i ) == TRUE )
            {
                for ( pTerm = pTermList; pTerm; pTerm = pTerm->pNext )
                {
                    if ( pTerm->pChildP == pChild )
                    {
                        pTerm->iConState = pTerm->iConStateOld;
                        pTerm->pChildP   = NULL;
                        pTerm->bFoundCmd = TRUE;
                        break;
                    }
                }
            }
        }

        /*
         * Output.
         */
        for ( pTerm = pTermList; pTerm != NULL; pTerm = pTermNext )
        {
            pTermNext                    = pTerm->pNext;

            if ( ( pTerm->bFoundCmd != FALSE
              || pTerm->pOutBuffer[0] != '\0' )
              && FD_ISSET( pTerm->sTelnetSocket, &fdsTelnetOut )
              && process_output( pTerm ) == FALSE )
            {
                pTerm->pOutBuffer[0]     = '\0';
                clear_binary_transfer( pTerm );
                close_connection( &pTerm );
                continue;
            }

            if ( pTerm->iTermType == CLIENT_SAPPHIRE_COMPATIBLE
              && pTerm->pBinData != NULL
              && FD_ISSET( pTerm->sBinarySocket, &fdsBinaryOut ) )
            {
                if ( write_binary_data( pTerm ) == FALSE )
                {
                    pTerm->pOutBuffer[0] = '\0';
                    clear_binary_transfer( pTerm );
                    close_connection( &pTerm );
                }
                else if ( pTerm->lBinRemainSize == 0 )
                    clear_binary_transfer( pTerm );
            }
        }

        for ( pMUDComm = pMUDCommList; pMUDComm; pMUDComm = pMUDCommNext )
        {
            pMUDCommNext             = pMUDComm->pNext;

            if ( FD_ISSET( pMUDComm->sMUDCommSocket, &fdsMUDCommOut )
              && process_mudcomm_output( pMUDComm ) != TRUE )
            {
                close_mudcomm_connection( &pMUDComm );
                continue;
            }
        }

        /*
         * Stall to save CPU time.
         *
        do
        {
            errno = 0;
            select( 0, NULL, NULL, NULL, &tStallTime );
        }
        while ( errno == EINTR );

        if ( errno != 0 )
            sap_error( "Stall: %s.", strerror( errno ) ); */
    }
}


static void update( void )
{
    free_buffers( );

    if ( iCreationCodeTimer > 0 )
        iCreationCodeTimer--;

    /*
     * Put together a new creation code if needed.
     */
    if ( iCreationCodeTimer == 0 )
    {
        int i;

        iCreationCodeTimer = 300;
        cCreationCode[0]   = '\0';

        for ( i = 0; i < 6; i++ )
            strcat( cCreationCode,
              pCreationCodeTable[random_range( 0, CCNP )] );
    }

    game_update( );
    free_buffers( );
}


void process_mucs_cmd( void )
{
    struct sigaction sSignalAction;
    MUD_COMM_DATA *pMUDComm;
    TERM_DATA *pTerm;
    char c, c2;

    c = getc( stdin );

    if ( c != '\n' )
        for ( c2 = getc( stdin ); c2 != '\n'; c2 = getc( stdin ) );

    switch ( c )
    {
      case '0' :
          fprintf( stderr, "[Main Control Menu]\n"
            "\n"
            "  1) Exit menu\n"
            "  2) Shutdown\n"
            "\n"
            "  3) Show connected sockets\n"
            "  4) Show character creation code\n"
            "\n"
            "  5) Enter shell (`%s')\n"
#ifdef DEBUG
            "  6) Core dump\n"
#endif
            , pShellProgramName );

          break;

      case '1' :
          bIsPrompt                = FALSE;

          /*
           * Setup the original handler again.
           */
          sSignalAction.sa_handler = &sigint_handler;
          sSignalAction.sa_mask    = 0;
          sSignalAction.sa_flags   = 0;
          sigaction( SIGINT, &sSignalAction, NULL );

          return;

      case '2' :
          if ( pLogFile != stdout )
              fprintf( stderr, "Server shutdown.\n" );

          lprintf( "Server shutdown." );

          for ( pTerm = pTermList; pTerm != NULL; pTerm = pTerm->pNext )
          {
              write_string_buffer( pTerm, "\n\rServer shutdown.\n\r" );
              tflush( pTerm );
          }

          exit( 0 );
          break;

      case '3' :
          if ( pTermList == NULL && pMUDCommList == NULL )
          {
              fprintf( stderr,
                "There are currently no connected sockets.\n" );
              break;
          }

          for ( pTerm = pTermList; pTerm != NULL; pTerm = pTerm->pNext )
              fprintf( stderr, "[%3d %3d] %-50.50s\n",
                pTerm->sTelnetSocket, pTerm->sBinarySocket,
                pTerm->pHostname );

          for ( pMUDComm = pMUDCommList; pMUDComm != NULL;
            pMUDComm = pMUDComm->pNext )
              fprintf( stderr, "[%3d    ] %-50.50s\n",
                pMUDComm->sMUDCommSocket, pMUDComm->pHostname );

          break;

      case '4' :
          if ( iCreationCodeTimer < 0 )
          {
              fprintf( stderr,
                "Character creation codes are not activated.\n" );
              break;
          }

          fprintf( stderr, "The current character creation code is: %s\n",
            cCreationCode );
          iCreationCodeTimer = 300;
          break;

      case '5' :
          if ( pShellProgramName == NULL )
          {
              fprintf( stderr, "No shell program is currently setup.\n" );
              break;
          }

          if ( fork( ) == 0 )
          {
              char *pArg[1]  = { NULL };

              for ( pTerm = pTermList; pTerm; pTerm = pTerm->pNext )
              {
                  close( pTerm->sTelnetSocket );

                  if ( pTerm->sBinarySocket > -1 )
                      close( pTerm->sBinarySocket );
              }

              for ( pMUDComm = pMUDCommList; pMUDComm != NULL;
                pMUDComm = pMUDComm->pNext )
                  close( pMUDComm->sMUDCommSocket );

              if ( sTelnetControl > -1 )
                  close( sTelnetControl );

              if ( sBinaryControl > -1 )
                  close( sBinaryControl );

              if ( sMUDCommControl > -1 )
                  close( sMUDCommControl );

              if ( pLogFile != stdout )
                  fclose( pLogFile );

              if ( execv( pShellProgramName, pArg ) < 0 )
                  exit( 1 );
          }
          else
          {
              int iExitValue;

              if ( wait( &iExitValue ) < 0 )
                  sap_error( "Wait: %s.", strerror( errno ) );

              if ( WIFSIGNALED( iExitValue ) )
#ifdef DEBUG
                  fprintf( stderr,
                    "Shell program `%s' terminated do to signal %d.\n",
                    pShellProgramName, WTERMSIG( iExitValue ) );
#else
                  fprintf( stderr, "The shell program (`%s') failed.\n",
                    pShellProgramName );
#endif

              break;
          }

      case '\0':
      case '\n':
      case '\r':
          fprintf( stderr, "%s> ", pMUDName );
          return;

      default  :
          fprintf( stderr,
            "Invalid option.  Type `0' for a list of options.\n" );
          break;
    }

    fprintf( stderr, "\n%s> ", pMUDName );
    fflush( stderr );
}


void load_config( char *pFilename )
{
    FILE *pFile;
    FILE *pFile2;
    char *pBuf;
    char c[2]                = { '\0', '\0' };
    int i;
    bool bFoundMatch;

    if ( ( pFile = fopen( pFilename, "r" ) ) == NULL )
        sap_fatal( "%s: %s.", pFilename, strerror( errno ) );

    iTelnetPort              = 4000;
    iBinaryPort              = 4001;
    iMUDCommPort             = 4444;
    iMaxPlayers              = 32;
    iMode                    = MODE_GENERIC;
    iMemoryMode              = MEMORY_MODE_NORMAL;
    iMaxStrBuffers           = 256;
    lStringSpace             = ( 512 * 64 );
    iMaxFragments            = 64;
    lLoopStallTime           = 120000;
    lEmMaxTime               = 200000;
    iHashListSize            = 1024;
    iCreationCodeTimer       = -1;
    pTempDir                 = str_dup( "../tmp/" );
    pMUDCommFilename         = str_dup( "../etc/mudcomm.hosts" );
    pShellProgramName        = str_dup( "/bin/sh" );
    pLogDir                  = str_dup( "../log/" );
    pPlayerDir               = str_dup( "../plr/" );
    pShellDir                = str_dup( "../ssh/" );
    pLoginScreenFilename     = str_dup( "login.jpg" );
    pXWordFilename           = str_dup( "../db/xwords.bad" );
    pGreetingFilename        = str_dup( "../db/greeting.txt" );
    pGoodbyeFilename         = str_dup( "../db/goodbye.txt" );
    pMOTDFilename            = str_dup( "../db/motd.txt" );
    pEmDir                   = str_dup( "../db/em_obj/" );
    pImageDir                = str_dup( "../db/images/" );
    pHelpDir                 = str_dup( "../db/helps/" );
    pNPCDir                  = str_dup( "../db/npcs/" );
    pObjectDir               = str_dup( "../db/objects/" );
    pRoomDir                 = str_dup( "../db/rooms/" );
    pZoneDir                 = str_dup( "../db/zones/" );
    pQuestDataDir            = str_dup( "../db/quest_data/" );
    pEmUsageFilename         = str_dup( "../sav/emfunc.txt" );
    pMUDName                 = str_dup( "Sapphire" );
    uDefaultRoom.iNumber     = 1000;
    iMaxShowCount            = 7;

    bEmFuncUsage             = FALSE;
    iEmInterpStackSize       = 64;
    iEmVarStackSize          = 64;

    for ( ; ; )
    {
        bFoundMatch          = FALSE;
        c[0]                 = fget_letter( pFile );

        if ( c[0] == '/' )
        {
            if ( getc( pFile ) != '/' )
                sap_fatal( "Unknown symbol `%s'.", c );

            while ( c[0] != '\n' )
            {
                if ( feof( pFile ) != 0 )
                    sap_fatal( "Read file: Unexpected EOF." );

                c[0]         = getc( pFile );
            }
        }
        else if ( c[0] == '$' )
        {
            pBuf             = ( feof( pFile ) ? "END"
                               : sfget_word( pFile ) );

            if ( str_compare( pBuf, "END" ) == TRUE )
                break;

            KEY(   "TELNET_PORT",   iTelnetPort,   fget_number( pFile ) );
            KEY(   "BINARY_PORT",   iBinaryPort,   fget_number( pFile ) );
            KEY(   "MUD_COMM_PORT", iMUDCommPort,  fget_number( pFile ) );
            KEY(   "MAX_PLAYERS",   iMaxPlayers,   fget_number( pFile ) );
            KEY(   "STRING_BUFFERS",iMaxStrBuffers,
              fget_number( pFile ) );
            KEY(   "STRING_SPACE",  lStringSpace,  fget_number( pFile ) );
            KEY(   "MAX_FRAGMENTS", iMaxFragments, fget_number( pFile ) );
            KEY(   "LOOP_STALL_TIME",
              lLoopStallTime, fget_number( pFile ) );
            KEY(   "HASH_LIST_SIZE",
              iHashListSize, fget_number( pFile ) );
            SDKEY( "XWORD_FILE",    pXWordFilename,
              sfget_string( pFile ) );
            SDKEY( "GREETING_FILE", pGreetingFilename,
              sfget_string( pFile ) );
            SDKEY( "GOODBYE_FILE",  pGoodbyeFilename,
              sfget_string( pFile ) );
            SDKEY( "LOGIN_SCREEN",  pLoginScreenFilename,
              sfget_string( pFile ) );
            SDKEY( "MUD_COMM_FILE", pMUDCommFilename,
              sfget_string( pFile ) );
            SDKEY( "MOTD_FILE",     pMOTDFilename, sfget_string( pFile ) );
            SDKEY( "TEMP_DIR",      pTempDir,      sfget_string( pFile ) );
            SDKEY( "IMAGE_DIR",     pImageDir,     sfget_string( pFile ) );
            SDKEY( "HELP_DIR",      pHelpDir,      sfget_string( pFile ) );
            SDKEY( "NPC_DIR",       pNPCDir,       sfget_string( pFile ) );
            SDKEY( "OBJECT_DIR",    pObjectDir,    sfget_string( pFile ) );
            SDKEY( "ROOM_DIR",      pRoomDir,      sfget_string( pFile ) );
            SDKEY( "ZONE_DIR",      pZoneDir,      sfget_string( pFile ) );
            SDKEY( "QUEST_DATA_DIR",pQuestDataDir, sfget_string( pFile ) );
            SDKEY( "PLAYER_DIR",    pPlayerDir,    sfget_string( pFile ) );
            SDKEY( "LOG_DIR",       pLogDir,       sfget_string( pFile ) );
            SDKEY( "SHELL_DIR",     pShellDir,     sfget_string( pFile ) );
            SDKEY( "SHELL_PROGRAM", pShellProgramName,
              sfget_string( pFile ) );
            SDKEY( "MUD_NAME",      pMUDName,      sfget_string( pFile ) );
            KEY(   "DEFAULT_ROOM",  uDefaultRoom.iNumber,
              fget_number( pFile ) );
            KEY(   "MAX_SHOW_COUNT",iMaxShowCount, fget_number( pFile ) );

            SDKEY( "EM_DIR",               pEmDir,
              sfget_string( pFile ) );
            KEY(   "EM_INTERP_STACK_SIZE", iEmInterpStackSize,
              fget_number( pFile ) );
            KEY(   "EM_VAR_STACK_SIZE",    iEmVarStackSize,
              fget_number( pFile ) );
            KEY(   "EM_MAX_TIME",          lEmMaxTime,
              fget_number( pFile ) );
            SDKEY( "EM_USAGE_FILE",        pEmUsageFilename,
              sfget_string( pFile ) );

            if ( str_compare( pBuf, "EM_REPORT_USAGE" ) == TRUE )
            {
                bFoundMatch              = TRUE;
                pBuf                     = sfget_word( pFile );

                if ( str_compare( pBuf, "TRUE" ) == TRUE )
                    bEmFuncUsage         = TRUE;
                else
                    bEmFuncUsage         = FALSE;
            }

            if ( str_compare( pBuf, "GAME_MODE" ) == TRUE )
            {
                bFoundMatch              = TRUE;
                pBuf                     = sfget_word( pFile );

                if ( str_compare( pBuf, "GENERIC" ) == TRUE )
                    iMode                = MODE_GENERIC;
                else if ( str_compare( pBuf, "RPG" ) == TRUE )
                {
                    iMode                = MODE_RPG;
                    iCreationCodeTimer   = 0;
                }
                else
                    sap_error( "Unknown game mode `%s'.", pBuf );
            }

            if ( str_compare( pBuf, "MEMORY_MODE" ) == TRUE )
            {
                bFoundMatch              = TRUE;
                pBuf                     = sfget_word( pFile );

                if ( str_compare( pBuf, "DISK" ) == TRUE )
                    iMemoryMode          = MEMORY_MODE_DISK;
                else if ( str_compare( pBuf, "COMPACT" ) == TRUE )
                    iMemoryMode          = MEMORY_MODE_COMPACT;
                else if ( str_compare( pBuf, "NORMAL" ) == TRUE )
                    iMemoryMode          = MEMORY_MODE_NORMAL;
                else if ( str_compare( pBuf, "LARGE" ) == TRUE )
                    iMemoryMode          = MEMORY_MODE_LARGE;
                else
                    sap_error( "Unknown memory mode `%s'.", pBuf );
            }

            if ( bFoundMatch == FALSE )
                sap_error( "No match for `%s'.", pBuf );
        }
        else
            sap_fatal( "Unknown symbol `%c'.", c[0] );
    }

    fclose( pFile );

    if ( iMaxFragments <= 0 )
    {
        sap_warning( "Invalid maximum string fragments (%d).",
          iMaxFragments );
        iMaxFragments                    = 1;
    }

    /*
     * See if the shell program name given is valid.
     */
    if ( ( pFile2 = fopen( pShellProgramName, "r" ) ) == NULL )
    {
        sap_warning( "Invalid shell program (`%s').",
          pShellProgramName );

        if ( pShellProgramName[0] != '\0' )
            free_mem( (void **) &pShellProgramName );
    }

    fclose( pFile2 );

    /*
     * See if the login image file is a known format.
     */
    if ( ( i = get_file_type( pLoginScreenFilename ) ) != FILE_TYPE_GIF
      && i != FILE_TYPE_JPEG )
    {
        sap_warning( "Unknown image file format." );

        str_free( pLoginScreenFilename );
        pLoginScreenFilename = EMPTY_STRING;
    }

    /*
     * Check Emerald memory variables.
     */
    if ( iEmVarStackSize < 1 )
    {
        sap_warning( "Invalid Emerald variable stack size (%d).",
          iEmVarStackSize );
        iEmVarStackSize                  = 1;
    }

    if ( iEmInterpStackSize < 1 )
    {
        sap_warning( "Invalid Emerald interpreter stack size (%d).",
          iEmInterpStackSize );
        iEmInterpStackSize               = 1;
    }
}


void load_mudcomm_file( char *pFilename )
{
    FILE *pFile;
    char *pHostname;
    char *pBuf;
    char c[2];
    int iPort;

    if ( ( pFile = open_file( pFilename, "r", FALSE ) ) == NULL )
        return;

    for ( ; ; )
    {
        if ( feof( pFile ) != 0 )
            break;

        c[0]          = fget_letter( pFile );

        if ( feof( pFile ) != 0 )
            break;

        if ( c[0] == '#' )
        {
            do
            {
                if ( feof( pFile ) != 0 )
                    sap_fatal( "Read file: Unexpected EOF." );

                c[0]  = getc( pFile );
            }
            while ( c[0] != '\n' );
        }
        else
        {
            ungetc( c[0], pFile );

            pBuf      = fget_word( pFile );
            pHostname = fget_word( pFile );
            iPort     = fget_number( pFile );

            if ( make_mudcomm_connection( pBuf, pHostname, iPort ) < 0 )
                lprintf( "\n  [MUD-Comm] Connection failed to: %s %d",
                  pHostname, iPort );

        }

        free_buffers( );
    }

    close_file( pFile );
}


/*
 * Deals with reading in all the database files.
 */
void load_database( void )
{
    FILE *pFile;
    int i;

    bStrComparing         = TRUE;

    /*
     * Load XWords.
     */
    {
        XWORD_DATA *pXWord;
        char *pBuf;

        pFile             = open_file( pXWordFilename, "r", TRUE );

        for ( ; ; )
        {
            pBuf          = fget_word( pFile );

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

            if ( feof( pFile ) != 0 )
                sap_fatal( "Read file: Unexpected EOF." );

            pXWord        = alloc_mem( sizeof( *pXWord ) );
            pXWord->pWord = str_dup( pBuf );
            pXWord->pNext = pXWordList;
            pXWordList    = pXWord;
        }

        close_file( pFile );
    }

    str_free( pXWordFilename );

    /*
     * Load the greeting, goodbye and MOTD.
     */
    {
        pFile             = open_file( pGreetingFilename, "r", TRUE );
        pGreeting         = str_dup( fget_string( pFile ) );
        str_free( pGreetingFilename );
        close_file( pFile );

        pFile             = open_file( pGoodbyeFilename, "r", TRUE );
        pGoodbye          = str_dup( fget_string( pFile ) );
        str_free( pGoodbyeFilename );
        close_file( pFile );

        pFile             = open_file( pMOTDFilename, "r", TRUE );
        pMOTD             = str_dup( fget_string( pFile ) );
        str_free( pMOTDFilename );
        close_file( pFile );
    }

    load_em_objects( pEmDir );
    error_check( );
    em_translate( );
    str_free( pEmUsageFilename );

    if ( ( i = em_find_func( "main" ) ) >= 0 )
    {
        stack_init( );
        em_call_func( i );
        stack_free( );
    }

    ppNPCIndexList        = alloc_mem( sizeof( *ppNPCIndexList )
                              * iHashListSize );
    ppObjIndexList        = alloc_mem( sizeof( *ppObjIndexList )
                              * iHashListSize );
    ppRoomIndexList       = alloc_mem( sizeof( *ppRoomIndexList )
                              * iHashListSize );
    ppCharList            = alloc_mem( sizeof( *ppCharList )
                              * iHashListSize );
    ppObjList             = alloc_mem( sizeof( *ppObjList )
                              * iHashListSize );

    /*
     * Create the system objects which numbers 1-5 are reserved for.
     */
    {
        OBJ_INDEX_DATA *pObjIndex;

        pObjIndex             = alloc_mem( sizeof( *pObjIndex ) );
        pObjIndex->iNumber    = OBJECT_NUMBER_GOLD;
        pObjIndex->iItemType  = NUMBER_ITEM_GOLD;
        pObjIndex->iLevel     = 1;
        pObjIndex->iSize      = NUMBER_SIZE_MEDIUM;
        pObjIndex->iCondition = 100;
        pObjIndex->iMaterial  = NUMBER_MATERIAL_UNDEFINED;

        i                     = ( OBJECT_NUMBER_GOLD % iHashListSize );
        pObjIndex->pNext      = ppObjIndexList[i];
        ppObjIndexList[i]     = pObjIndex;

        pObjIndex             = alloc_mem( sizeof( *pObjIndex ) );
        pObjIndex->iNumber    = OBJECT_NUMBER_CORPSE;
        pObjIndex->iItemType  = NUMBER_ITEM_CORPSE;
        pObjIndex->iLevel     = 1;
        pObjIndex->iSize      = NUMBER_SIZE_MEDIUM;
        pObjIndex->iCondition = 100;
        pObjIndex->iMaterial  = NUMBER_MATERIAL_UNDEFINED;

        i                     = ( OBJECT_NUMBER_CORPSE % iHashListSize );
        pObjIndex->pNext      = ppObjIndexList[i];
        ppObjIndexList[i]     = pObjIndex;
    }

    load_quest_data( pQuestDataDir );

    load_helps( pHelpDir );
    load_npcs( pNPCDir );
    load_objects( pObjectDir );
    load_rooms( pRoomDir );
    load_zones( pZoneDir );
#if 0
    str_free( pHelpDir ); /* OLC needs this strings :) */
    str_free( pNPCDir );
    str_free( pObjectDir );
    str_free( pRoomDir );
    str_free( pZoneDir );
    str_free( pQuestDataDir );
#endif
    translate_numbers( );

    uDefaultRoom.pRoom = get_room_index( ( i = uDefaultRoom.iNumber ) );

    if ( uDefaultRoom.pRoom == NULL )
        sap_fatal( "No such room with the number `%d' for default.", i );

    bStrComparing         = FALSE;
}


/*
 * Load Emerald object files.
 */
void load_em_objects( char *pDirName )
{
    struct dirent *pDirEntry;
    DIR *pDir;
    DIR *pDir2;
    char cBuf[1024];

    if ( ( pDir = opendir( pDirName ) ) == NULL )
        sap_fatal( "Open directory: %s: %s.", pDirName,
          strerror( errno ) );

    if ( readdir( pDir ) == NULL )
        sap_fatal( "Open directory: %s: Not a directory.", pDirName );

    readdir( pDir );

    while ( ( pDirEntry = readdir( pDir ) ) != NULL )
    {
        strcpy( cBuf, pDirName );
        strcat( cBuf, pDirEntry->d_name );

        /*
         * If this is a directory, recurse into it.
         */
        if ( ( pDir2 = opendir( cBuf ) ) != NULL )
        {
            if ( readdir( pDir2 ) != NULL )
            {
                strcat( cBuf, "/" );
                load_em_objects( cBuf );
                continue;
            }

            closedir( pDir2 );
        }

        if ( em_load_obj_file( cBuf, FALSE ) < 0 )
        {
            print_error( );
            /* exit( 1 ); */
        }
    }

    closedir( pDir );
}


/*
 * Load in all the help pages.
 */
void load_helps( char *pDirName )
{
    struct dirent *pDirEntry;
    HELP_DATA *pNewHelp;
    FILE *pFile;
    DIR *pDir;
    DIR *pDir2;
    char cBuf[1024];
    char *pBuf;
    char c;
    int i;

    if ( ( pDir = opendir( pDirName ) ) == NULL )
        sap_fatal( "Open directory: %s: %s.", pDirName,
          strerror( errno ) );

    if ( readdir( pDir ) == NULL )
        sap_fatal( "Open directory: %s: Not a directory.", pDirName );

    readdir( pDir );

    while ( ( pDirEntry = readdir( pDir ) ) != NULL )
    {
        strcpy( cBuf, pDirName );
        strcat( cBuf, pDirEntry->d_name );

        /*
         * If this is a directory, recurse into it.
         */
        if ( ( pDir2 = opendir( cBuf ) ) != NULL )
        {
            if ( readdir( pDir2 ) != NULL )
            {
                strcat( cBuf, "/" );
                load_helps( cBuf );
                continue;
            }

            closedir( pDir2 );
        }

        pFile                      = open_file( cBuf, "r", TRUE );

        /*
         * Don't bother with helps that have no keywords.
         */
        if ( ( c = fget_letter( pFile ) ) == '#' )
        {
            close_file( pFile );
            continue;
        }
        else
            ungetc( c, pFile );

        i                          = 0;
        pNewHelp                   = alloc_mem( sizeof( *pNewHelp ) );

        for ( i = 0; ; i++ )
        {
            if ( ( c = fget_letter( pFile ) ) == '#' )
                break;
            else
                ungetc( c, pFile );

            pBuf                   = fget_string( pFile );

            if ( get_help( pBuf ) != NULL )
                sap_fatal( "Help keyword `%s' duplicated.", pBuf );

            pNewHelp->pKeywords    = realloc_mem( pNewHelp->pKeywords,
                                       ( sizeof( string ) * ( i + 2 ) ) );
            pNewHelp->pKeywords[i] = save_string( pBuf );
            pNewHelp->pKeywords[i + 1] = NULL;
        }

        pNewHelp->iLevel           = fget_number( pFile );
        pNewHelp->sText            = save_string( fget_string( pFile ) );

        fget_string_2( pFile, '[', ']' ); /* temp */

        pNewHelp->pNext            = pHelpList;
        pHelpList                  = pNewHelp;
        close_file( pFile );
        free_buffers( );
    }

    closedir( pDir );
}


/*
 * Load in all the NPCs (Non-Player Characters).
 */
void load_npcs( char *pDirName )
{
    struct dirent *pDirEntry;
    NPC_INDEX_DATA *pNewNPCIndex;
    FILE *pFile;
    DIR *pDir;
    DIR *pDir2;
    char cBuf[1024];
    char *pBuf;
    int iNumber                    = 0;
    int i, i2;
    bool bFoundMatch;

    if ( ( pDir = opendir( pDirName ) ) == NULL )
        sap_fatal( "Open directory: %s: %s.", pDirName,
          strerror( errno ) );

    if ( readdir( pDir ) == NULL )
        sap_fatal( "Open directory: %s: Not a directory.", pDirName );

    readdir( pDir );

    while ( ( pDirEntry = readdir( pDir ) ) != NULL )
    {
        strcpy( cBuf, pDirName );
        strcat( cBuf, pDirEntry->d_name );

        /*
         * If this is a directory, recurse into it.
         */
        if ( ( pDir2 = opendir( cBuf ) ) != NULL )
        {
            if ( readdir( pDir2 ) != NULL )
            {
                strcat( cBuf, "/" );
                load_npcs( cBuf );
                continue;
            }

            closedir( pDir2 );
        }

        pFile                      = open_file( cBuf, "r", TRUE );

        if ( fget_letter( pFile ) != '#' )
            sap_fatal( "Syntax error in file." );

        pBuf                       = fget_word( pFile );

        if ( is_number( pBuf ) == TRUE )
        {
            iNumber                = atoi( pBuf );

            if ( iNumber <= 5 || iNumber > MAX_INDEX_NUMBER )
                sap_fatal( "Illegal NPC number `%d'.", iNumber );

            if ( get_npc_index( iNumber ) != NULL )
                sap_fatal( "More then one NPC with the number `%d'.",
                  iNumber );
        }
        else if ( strcmp( pBuf, "COMMENT" ) == 0 )
        {
            close_file( pFile );
            continue;
        }
        else
            sap_fatal( "Unknown symbol `%s'.", pBuf );

        pNewNPCIndex               = alloc_mem( sizeof( *pNewNPCIndex ) );
        zero_out( pNewNPCIndex, sizeof( *pNewNPCIndex ) );

        pNewNPCIndex->iNumber      = iNumber;
        pNewNPCIndex->pNameList    = alloc_mem( sizeof( string ) * 2 );
        pNewNPCIndex->pNameList[0] = EMPTY_STRING;
        pNewNPCIndex->pNameList[1] = NULL;
        pNewNPCIndex->sShortDesc = save_string( "(no short description)" );

        for ( i = 0; i < 5; i++ )
            pNewNPCIndex->sDescs[i] = save_string( "(no description)" );

        pNewNPCIndex->sLongDesc  = save_string( "(no long description)" );
        pNewNPCIndex->iLevel       = 1;
        pNewNPCIndex->iRace        = rRaceTable[0].iNumber;
        pNewNPCIndex->fActFlags    = rRaceTable[0].fActFlags;
        pNewNPCIndex->iSex         = snSexTable[0].iNumber;
        pNewNPCIndex->iHairColor   = rRaceTable[0].iHairColors[0];
        pNewNPCIndex->iEyeColor    = rRaceTable[0].iEyeColors[0];
        pNewNPCIndex->iHeight      = rRaceTable[0].iUpperLowerHeights[1];
        pNewNPCIndex->iWeight      = rRaceTable[0].iUpperLowerWeights[1];
        pNewNPCIndex->iPosition    = NUMBER_POSITION_STANDING;

        for ( i = 0; i < 3; i++ )
        {
            pNewNPCIndex->VarStats.iStatStr[i] = 0;
            pNewNPCIndex->VarStats.iStatInt[i] = 0;
            pNewNPCIndex->VarStats.iStatWis[i] = 0;
            pNewNPCIndex->VarStats.iStatDex[i] = 0;
            pNewNPCIndex->VarStats.iStatCon[i] = 0;
            pNewNPCIndex->VarStats.iStatCha[i] = 0;
            pNewNPCIndex->VarStats.iStatLuc[i] = 0;
            pNewNPCIndex->VarStats.iStatHP[i]  = 0;
            pNewNPCIndex->VarStats.iStatMP[i]  = 0;
            pNewNPCIndex->VarStats.iStatMV[i]  = 0;
        }

        for ( ; ; )
        {
            bFoundMatch                = FALSE;
            pBuf                       = fget_word_2( pFile );

            if ( pBuf[0] == '\0' )
                sap_fatal( "Error in file." );

            if ( pBuf[0] == '*' )
            {
                char c;

                do
                {
                    if ( feof( pFile ) != 0 )
                        sap_fatal( "Read file: Unexpected EOF." );

                    c                  = getc( pFile );

                    if ( c == '\n' )
                        lCurrentLine++;
                }
                while ( c != '\n' );

                continue;
            }

            if ( str_compare( pBuf, "END" ) == TRUE )
                break;

            for ( i = 0; npclLoadTable[i].pField[0] != '\0'; i++ )
            {
                if ( str_compare( pBuf, npclLoadTable[i].pField ) == TRUE )
                {
                    ( *npclLoadTable[i].pFunc ) ( pNewNPCIndex, pFile );
                    bFoundMatch        = TRUE;
                }
            }

            if ( bFoundMatch == FALSE )
                sap_fatal( "NPC %d: No match for `%s'.", iNumber, pBuf );

            if ( fget_letter( pFile ) != ';' )
                sap_fatal( "NPC %d: Missing semi-colon for `%s'.",
                  iNumber, pBuf );
        }

        iNumber                       %= iHashListSize;
        pNewNPCIndex->pNext            = ppNPCIndexList[iNumber];
        ppNPCIndexList[iNumber]        = pNewNPCIndex;

        i = get_race_index( pNewNPCIndex->iRace );

        for ( i2 = 0; rRaceTable[i].iHairColors[i2] != 0; i2++ )
        {
            if ( rRaceTable[i].iHairColors[i2]
              == pNewNPCIndex->iHairColor )
                break;
        }

        if ( rRaceTable[i].iHairColors[i2] == 0 )
        {
            sap_warning( "NPC %d: Invalid hair color for race.",
              pNewNPCIndex->iNumber );
            pNewNPCIndex->iHairColor   = rRaceTable[i].iHairColors[0];
        }

        for ( i2 = 0; rRaceTable[i].iEyeColors[i2] != 0; i2++ )
        {
            if ( rRaceTable[i].iEyeColors[i2] == pNewNPCIndex->iEyeColor )
                break;
        }

        if ( rRaceTable[i].iEyeColors[i2] == 0 )
        {
            sap_warning( "NPC %d: Invalid eye color for race.",
              pNewNPCIndex->iNumber );
            pNewNPCIndex->iEyeColor    = rRaceTable[i].iEyeColors[0];
        }

        if ( pNewNPCIndex->iHeight > rRaceTable[i].iUpperLowerHeights[0] )
        {
            sap_warning( "NPC %d: Height greater then race limit.",
              pNewNPCIndex->iNumber );
            pNewNPCIndex->iHeight = rRaceTable[i].iUpperLowerHeights[0];
        }
        else if ( pNewNPCIndex->iHeight
          < rRaceTable[i].iUpperLowerHeights[1] )
        {
            sap_warning( "NPC %d: Height less then race limit.",
              pNewNPCIndex->iNumber );
            pNewNPCIndex->iHeight = rRaceTable[i].iUpperLowerHeights[1];
        }

        if ( pNewNPCIndex->iWeight > rRaceTable[i].iUpperLowerWeights[0] )
        {
            sap_warning( "NPC %d: Weight greater then race limit.",
              pNewNPCIndex->iNumber );
            pNewNPCIndex->iWeight = rRaceTable[i].iUpperLowerWeights[0];
        }
        else if ( pNewNPCIndex->iWeight
          < rRaceTable[i].iUpperLowerWeights[1] )
        {
            sap_warning( "NPC %d: Weight less then race limit.",
              pNewNPCIndex->iNumber );
            pNewNPCIndex->iWeight = rRaceTable[i].iUpperLowerWeights[1];
        }

        close_file( pFile );
        free_buffers( );
    }

    closedir( pDir );
}


/*
 * Load in all the objects/items.
 */
void load_objects( char *pDirName )
{
    struct dirent *pDirEntry;
    OBJ_INDEX_DATA *pNewObjIndex;
    FILE *pFile;
    DIR *pDir;
    DIR *pDir2;
    char cBuf[1025];
    char *pBuf;
    int iNumber                        = 0;
    int i;
    bool bFoundMatch;

    if ( ( pDir = opendir( pDirName ) ) == NULL )
        sap_fatal( "Open directory: %s: %s.", pDirName,
          strerror( errno ) );

    if ( readdir( pDir ) == NULL )
        sap_fatal( "Open directory: %s: Not a directory.", pDirName );

    readdir( pDir );

    while ( ( pDirEntry = readdir( pDir ) ) != NULL )
    {
        strcpy( cBuf, pDirName );
        strcat( cBuf, pDirEntry->d_name );

        /*
         * If this is a directory, recurse into it.
         */
        if ( ( pDir2 = opendir( cBuf ) ) != NULL )
        {
            if ( readdir( pDir2 ) != NULL )
            {
                strcat( cBuf, "/" );
                load_objects( cBuf );
                continue;
            }

            closedir( pDir2 );
        }

        pFile                          = open_file( cBuf, "r", TRUE );

        if ( fget_letter( pFile ) != '#' )
            sap_fatal( "Syntax error in file." );

        pBuf                           = fget_word( pFile );

        if ( is_number( pBuf ) == TRUE )
        {
            iNumber                    = atoi( pBuf );

            if ( iNumber <= 5 || iNumber > MAX_INDEX_NUMBER )
                sap_fatal( "Illegal object number `%d'.", iNumber );

            if ( get_object_index( iNumber ) != NULL )
                sap_fatal( "More then one object with the number `%d'.",
                  iNumber );
        }
        else if ( strcmp( pBuf, "COMMENT" ) == 0 )
        {
            close_file( pFile );
            continue;
        }
        else
            sap_fatal( "Unknown symbol `%s'.", pBuf );

        pNewObjIndex               = alloc_mem( sizeof( *pNewObjIndex ) );
        zero_out( pNewObjIndex, sizeof( *pNewObjIndex ) );

        pNewObjIndex->iNumber      = iNumber;
        pNewObjIndex->pNameList    = alloc_mem( sizeof( string ) * 2 );
        pNewObjIndex->pNameList[0] = EMPTY_STRING;
        pNewObjIndex->pNameList[1] = NULL;
        pNewObjIndex->sShortDesc = save_string( "(no short description)" );
        pNewObjIndex->sDesc      = save_string( "(no description)" );
        pNewObjIndex->sLongDesc  = save_string( "(no long description)" );
        pNewObjIndex->iItemType    = snItemTypeTable[0].iNumber;
        pNewObjIndex->iLevel       = 1;
        pNewObjIndex->iSize        = NUMBER_SIZE_MEDIUM;
        pNewObjIndex->iCondition   = 100;
        pNewObjIndex->iMaterial    = snMaterialTable[0].iNumber;
        pNewObjIndex->sValue       = EMPTY_STRING;

        for ( ; ; )
        {
            bFoundMatch            = FALSE;
            pBuf                   = fget_word_2( pFile );

            if ( pBuf[0] == '\0' )
                sap_fatal( "Error in file." );

            if ( pBuf[0] == '*' )
            {
                char c;

                do
                {
                    if ( feof( pFile ) != 0 )
                        sap_fatal( "Read file: Unexpected EOF." );

                    c                  = getc( pFile );

                    if ( c == '\n' )
                        lCurrentLine++;
                }
                while ( c != '\n' );

                continue;
            }

            if ( str_compare( pBuf, "END" ) == TRUE )
                break;

            for ( i = 0; olLoadTable[i].pField[0] != '\0'; i++ )
            {
                if ( str_compare( pBuf, olLoadTable[i].pField ) == TRUE )
                {
                    ( *olLoadTable[i].pFunc ) ( pNewObjIndex, pFile );
                    bFoundMatch    = TRUE;
                }
            }

            if ( bFoundMatch == FALSE )
                sap_fatal( "Object %d: No match for `%s'.", iNumber,
                  pBuf );

            if ( fget_letter( pFile ) != ';' )
                sap_fatal( "Object %d: Missing semi-colon for `%s'.",
                  iNumber, pBuf );
        }

        if ( pNewObjIndex->iItemType != NUMBER_ITEM_LIGHT
          && IS_SET( pNewObjIndex->fObjFlags, FLAG_OBJECT_LIT ) )
        {
            sap_warning( "Object %d: Lit flag set on object that is "
              "not of type `Light'.", pNewObjIndex->iNumber );
            REMOVE_FLAG( pNewObjIndex->fObjFlags, FLAG_OBJECT_LIT );
        }

        iNumber                   %= iHashListSize;
        pNewObjIndex->pNext        = ppObjIndexList[iNumber];
        ppObjIndexList[iNumber]    = pNewObjIndex;

        close_file( pFile );
        free_buffers( );
    }

    closedir( pDir );
}


/*
 * Load in all the rooms.
 */
void load_rooms( char *pDirName )
{
    struct dirent *pDirEntry;
    ROOM_INDEX_DATA *pNewRoomIndex;
    FILE *pFile;
    DIR *pDir;
    DIR *pDir2;
    char cBuf[1025];
    char *pBuf;
    int iNumber                        = 0;
    int i;
    bool bFoundMatch;

    if ( ( pDir = opendir( pDirName ) ) == NULL )
        sap_fatal( "Open directory: %s: %s.", pDirName,
          strerror( errno ) );

    if ( readdir( pDir ) == NULL )
        sap_fatal( "Open directory: %s: Not a directory.", pDirName );

    readdir( pDir );

    while ( ( pDirEntry = readdir( pDir ) ) != NULL )
    {
        strcpy( cBuf, pDirName );
        strcat( cBuf, pDirEntry->d_name );

        /*
         * If this is a directory, recurse into it.
         */
        if ( ( pDir2 = opendir( cBuf ) ) != NULL )
        {
            if ( readdir( pDir2 ) != NULL )
            {
                strcat( cBuf, "/" );
                load_rooms( cBuf );
                continue;
            }

            closedir( pDir2 );
        }

        pFile                          = open_file( cBuf, "r", TRUE );

        if ( fget_letter( pFile ) != '#' )
            sap_fatal( "Syntax error in file." );

        pBuf                           = fget_word( pFile );

        if ( is_number( pBuf ) == TRUE )
        {
            iNumber                    = atoi( pBuf );

            if ( iNumber <= 5 || iNumber > MAX_INDEX_NUMBER )
                sap_fatal( "Illegal room number `%d'.", iNumber );

            if ( get_room_index( iNumber ) != NULL )
                sap_fatal( "More then one room with the number `%d'.",
                  iNumber );
        }
        else if ( strcmp( pBuf, "COMMENT" ) == 0 )
        {
            close_file( pFile );
            continue;
        }
        else
            sap_fatal( "Unknown symbol `%s'.", pBuf );

        pNewRoomIndex          = alloc_mem( sizeof( *pNewRoomIndex ) );
        zero_out( pNewRoomIndex, sizeof( *pNewRoomIndex ) );

        pNewRoomIndex->iNumber = iNumber;

        for ( i = 0; i < 10; i++ )
        {
            pNewRoomIndex->sDirDescs[i]            = EMPTY_STRING;
            pNewRoomIndex->eExits[i].pDoorNames    = alloc_mem( ( sizeof(
                                                       string ) * 2 ) );
            pNewRoomIndex->eExits[i].pDoorNames[0] = EMPTY_STRING;
            pNewRoomIndex->eExits[i].pDoorNames[1] = NULL;
            pNewRoomIndex->eExits[i].sFound        = EMPTY_STRING;
        }

        pNewRoomIndex->sImageFilename   = EMPTY_STRING;
        pNewRoomIndex->sTitle         = save_string( "(no title)" );
        pNewRoomIndex->sDesc          = save_string( "(no description)" );
        pNewRoomIndex->iSectorType      = snSectorTable[0].iNumber;
        pNewRoomIndex->iTemperature     = 60;

        for ( ; ; )
        {
            bFoundMatch                 = FALSE;
            pBuf                        = fget_word_2( pFile );

            if ( pBuf[0] == '\0' )
                sap_fatal( "Error in file." );

            if ( pBuf[0] == '*' )
            {
                char c;

                do
                {
                    if ( feof( pFile ) != 0 )
                        sap_fatal( "Read file: Unexpected EOF." );

                    c                   = getc( pFile );

                    if ( c == '\n' )
                        lCurrentLine++;
                }
                while ( c != '\n' );

                continue;
            }

            if ( str_compare( pBuf, "END" ) == TRUE )
                break;

            for ( i = 0; rlLoadTable[i].pField[0] != '\0'; i++ )
            {
                if ( str_compare( pBuf, rlLoadTable[i].pField ) == TRUE )
                {
                    ( *rlLoadTable[i].pFunc ) ( pNewRoomIndex, pFile );
                    bFoundMatch         = TRUE;
                }
            }

            if ( bFoundMatch == FALSE )
                sap_fatal( "Room %d: No match for `%s'.", iNumber,
                  pBuf );

            if ( fget_letter( pFile ) != ';' )
                sap_fatal( "Room %d: Missing semi-colon for `%s'.",
                  iNumber, pBuf );
        }

        iNumber                        %= iHashListSize;
        pNewRoomIndex->pNext            = ppRoomIndexList[iNumber];
        ppRoomIndexList[iNumber]        = pNewRoomIndex;

        close_file( pFile );
        free_buffers( );
    }

    closedir( pDir );
}


/*
 * Load in all the zone data.
 */
void load_zones( char *pDirName )
{
    struct dirent *pDirEntry;
    ZONE_DATA *pNewZone;
    FILE *pFile;
    DIR *pDir;
    DIR *pDir2;
    char cBuf[1025];
    char *pBuf;
    int i;
    bool bFoundMatch;

    if ( ( pDir = opendir( pDirName ) ) == NULL )
        sap_fatal( "Open directory: %s: %s.", pDirName,
          strerror( errno ) );

    if ( readdir( pDir ) == NULL )
        sap_fatal( "Open directory: %s: Not a directory.", pDirName );

    readdir( pDir );

    while ( ( pDirEntry = readdir( pDir ) ) != NULL )
    {
        strcpy( cBuf, pDirName );
        strcat( cBuf, pDirEntry->d_name );

        /*
         * If this is a directory, recurse into it.
         */
        if ( ( pDir2 = opendir( cBuf ) ) != NULL )
        {
            if ( readdir( pDir2 ) != NULL )
            {
                strcat( cBuf, "/" );
                load_zones( cBuf );
                continue;
            }

            closedir( pDir2 );
        }

        pFile             = open_file( cBuf, "r", TRUE );

        if ( fget_letter( pFile ) != '#' )
            sap_fatal( "Syntax error in file." );

        pBuf              = fget_string( pFile );

        pNewZone          = alloc_mem( sizeof( *pNewZone ) );
        zero_out( pNewZone, sizeof( *pNewZone ) );

        pNewZone->sName   = save_string( pBuf );
        pNewZone->sAuthor = EMPTY_STRING;

        for ( ; ; )
        {
            bFoundMatch   = FALSE;
            pBuf          = fget_word_2( pFile );

            if ( pBuf[0] == '\0' )
                sap_fatal( "Error in file." );

            if ( pBuf[0] == '*' )
            {
                char c;

                do
                {
                    if ( feof( pFile ) != 0 )
                        sap_fatal( "Read file: Unexpected EOF." );

                    c     = getc( pFile );

                    if ( c == '\n' )
                        lCurrentLine++;
                }
                while ( c != '\n' );

                continue;
            }

            if ( str_compare( pBuf, "END" ) == TRUE )
                break;

            for ( i = 0; zlLoadTable[i].pField[0] != '\0'; i++ )
            {
                if ( str_compare( pBuf, zlLoadTable[i].pField ) == TRUE )
                {
                    ( *zlLoadTable[i].pFunc ) ( pNewZone, pFile );
                    bFoundMatch = TRUE;
                }
            }

            if ( bFoundMatch == FALSE )
                sap_fatal( "Zone `%s': No match for `%s'.",
                  pNewZone->sName, pBuf );

            if ( fget_letter( pFile ) != ';' )
                sap_fatal( "Zone `%s': Missing semi-colon for `%s'.",
                  pNewZone->sName, pBuf );
        }

        pNewZone->pNext   = pZoneList;
        pZoneList         = pNewZone;

        close_file( pFile );
        free_buffers( );
    }

    closedir( pDir );
}


/*
 * Load in all the quest data.
 */
void load_quest_data( char *pDirName )
{
    struct dirent *pDirEntry;
    QUEST_DATA *pNewQuestData;
    QUEST_VAR_DATA *pVar;
    QUEST_VAR_DATA *pVar2;
    FILE *pFile;
    DIR *pDir;
    DIR *pDir2;
    char cBuf[1025];
    char *pBuf;
    char *pBuf2;
    int i;

    if ( ( pDir = opendir( pDirName ) ) == NULL )
        sap_fatal( "Open directory: %s: %s.", pDirName,
          strerror( errno ) );

    if ( readdir( pDir ) == NULL )
        sap_fatal( "Open directory: %s: Not a directory.", pDirName );

    readdir( pDir );

    while ( ( pDirEntry = readdir( pDir ) ) != NULL )
    {
        strcpy( cBuf, pDirName );
        strcat( cBuf, pDirEntry->d_name );

        /*
         * If this is a directory, recurse into it.
         */
        if ( ( pDir2 = opendir( cBuf ) ) != NULL )
        {
            if ( readdir( pDir2 ) != NULL )
            {
                strcat( cBuf, "/" );
                load_zones( cBuf );
                continue;
            }

            closedir( pDir2 );
        }

        pFile                = open_file( cBuf, "r", TRUE );

        pBuf                 = fget_word( pFile );

        if ( fget_letter( pFile ) != '{' )
            sap_fatal( "Syntax error in file." );

        if ( !isalpha( pBuf[0] ) && pBuf[0] != '_' )
            sap_fatal( "Illegal quest data name `%s'.", pBuf );

        for ( i = 0; pBuf[i] != '\0'; i++ )
        {
            if ( !isalnum( pBuf[i] ) && pBuf[i] != '_' )
                sap_fatal( "Illegal quest data name `%s'.", pBuf );
        }

        for ( pNewQuestData = pQuestDataList; pNewQuestData != NULL;
          pNewQuestData = pNewQuestData->pNext )
        {
            if ( strcmp( pNewQuestData->sName, pBuf ) == 0 )
                sap_fatal( "Quest data `%s' multiply defined.", pBuf );
        }

        pNewQuestData        = alloc_mem( sizeof( *pNewQuestData ) );
        zero_out( pNewQuestData, sizeof( *pNewQuestData ) );

        pNewQuestData->sName = save_string( pBuf );

        for ( ; ; )
        {
            pBuf             = fget_word_2( pFile );

            if ( pBuf[0] == '\0' )
                sap_fatal( "Error in file." );

            if ( pBuf[0] == '*' )
            {
                char c;

                do
                {
                    if ( feof( pFile ) != 0 )
                        sap_fatal( "Read file: Unexpected EOF." );

                    c        = getc( pFile );

                    if ( c == '\n' )
                        lCurrentLine++;
                }
                while ( c != '\n' );

                continue;
            }

            if ( str_compare( pBuf, "}" ) == TRUE )
                break;

            pVar             = alloc_mem( sizeof( *pVar ) );
            zero_out( pVar, sizeof( *pVar ) );

            if ( str_compare( pBuf, "number" ) == TRUE )
                pVar->iType  = NUMBER_VAR_NUMBER;
            else if ( str_compare( pBuf, "string" ) == TRUE )
                pVar->iType  = NUMBER_VAR_STRING;
            else if ( str_compare( pBuf, "character_pointer" ) == TRUE )
                pVar->iType  = NUMBER_VAR_POINTER_CHAR;
            else if ( str_compare( pBuf, "object_pointer" ) == TRUE )
                pVar->iType  = NUMBER_VAR_POINTER_OBJ;
            else if ( str_compare( pBuf, "room_pointer" ) == TRUE )
                pVar->iType  = NUMBER_VAR_POINTER_ROOM;
            else
                sap_fatal( "Quest data `%s': Unknown variable type `%s'.",
                  pNewQuestData->sName, pBuf );

            pBuf2                = fget_word_2( pFile );

            if ( pBuf2[0] == '\0' || ( !isalpha( pBuf2[0] )
              && pBuf2[0] != '_' ) )
            {
                free_mem( (void **) &pVar );
                sap_error(
                  "Quest data `%s': Illegal variable name for `%s'.",
                  pNewQuestData->sName, pBuf );
                continue;
            }

            for ( i = 0; pBuf2[i] != '\0'; i++ )
            {
                if ( !isalnum( pBuf2[i] ) && pBuf2[i] != '_' )
                {
                    free_mem( (void **) &pVar );
                    sap_error(
                      "Quest data `%s': Illegal variable name for `%s'.",
                      pNewQuestData->sName, pBuf );
                    break;
                }
            }

            if ( pBuf2[i] != '\0' )
                continue;

            for ( pVar2 = pNewQuestData->pVars; pVar2 != NULL;
              pVar2 = pVar2->pNext )
            {
                if ( strcmp( pVar2->sName, pBuf2 ) == 0 )
                {
                    free_mem( (void **) &pVar );
                    sap_warning( "Quest data `%s': "
                      "More then one variable with the name `%s'.",
                      pNewQuestData->sName, pBuf );
                    break;
                }
            }

            if ( pVar2 != NULL )
                continue;

            pVar->sName          = save_string( pBuf2 );

            if ( pVar->iType == NUMBER_VAR_STRING )
                pVar->uData.s    = EMPTY_STRING;

            pVar->pNext          = pNewQuestData->pVars;
            pNewQuestData->pVars = pVar;

            if ( fget_letter( pFile ) != ';' )
                sap_fatal( "Quest data `%s': Missing semi-colon for `%s'.",
                  pNewQuestData->sName, pBuf );
        }

        pNewQuestData->pNext = pQuestDataList;
        pQuestDataList       = pNewQuestData;

        close_file( pFile );
        free_buffers( );
    }

    closedir( pDir );
}


/*
 * After all the NPCs, objects and rooms are loaded
 * this function is called to go through and translate
 * all the numbers that are referencing other NPCs,
 * objects or rooms into pointers to the actaul structures.
 * This function also performs inititial resets.
 */
static void translate_numbers( void )
{
    NPC_INDEX_DATA *pNPCIndex;
    OBJ_INDEX_DATA *pObjIndex;
    ROOM_INDEX_DATA *pRoom;
    NPC_RESET_DATA *pNPCReset;
    NPC_RESET_DATA *pPrevNPCReset;
    OBJ_RESET_DATA *pObjReset;
    OBJ_RESET_DATA *pPrevObjReset;
    CHAR_DATA *pChar;
    OBJ_DATA *pObj;
    OBJ_DATA *pTemp;
    OBJ_DATA *pPrev;
    int i;

    for ( i = 0; i < iHashListSize; i++ )
    {
        for ( pNPCIndex = ppNPCIndexList[i]; pNPCIndex != NULL;
          pNPCIndex = pNPCIndex->pNext )
            npc_inherit( pNPCIndex );
    }

    for ( i = 0; i < iHashListSize; i++ )
    {
        for ( pObjIndex = ppObjIndexList[i]; pObjIndex != NULL;
          pObjIndex = pObjIndex->pNext )
            obj_inherit( pObjIndex );
    }

    for ( i = 0; i < iHashListSize; i++ )
    {
        for ( pRoom = ppRoomIndexList[i]; pRoom; pRoom = pRoom->pNext )
        {
            int i2;

            for ( i2 = 0; i2 < 10; i2++ )
                fix_exit( pRoom, i2 );
        }
    }

    for ( i = 0; i < iHashListSize; i++ )
    {
        for ( pRoom = ppRoomIndexList[i]; pRoom; pRoom = pRoom->pNext )
        {
            pPrevNPCReset               = NULL;
            pPrevObjReset               = NULL;

            for ( pNPCReset = pRoom->pNPCResets; pNPCReset != NULL; )
            {
                if ( ( pNPCIndex =
                  get_npc_index( pNPCReset->u1.iNumber ) ) == NULL )
                    sap_fatal( "Room %d: No NPC with the number `%d'.",
                      pRoom->iNumber, pNPCReset->u1.iNumber );

                pNPCReset->u1.pNPCIndex = pNPCIndex;
                pNPCReset->bAlive       = TRUE;
                pNPCReset->iResetTimer  = -1;
                pChar                   = new_npc( pNPCIndex, NULL );
                pChar->pNPCData->pReset = pNPCReset;

                pChar->pInven           = translate_obj_resets(
                                            pNPCReset->pInvenResets,
                                            pRoom );

                for ( pPrev = NULL, pTemp = pChar->pInven; pTemp != NULL;
                  pPrev = pTemp, pTemp = pTemp->pNextContent );

                pTemp                   = translate_obj_resets(
                                            pNPCReset->pEqResets, pRoom );

                if ( pPrev == NULL )
                    pChar->pInven       = pTemp;
                else
                    pPrev->pNextContent = pTemp;

                for ( pTemp = pChar->pInven; pTemp != NULL;
                  pTemp = pTemp->pNextContent )
                    pTemp->pCarriedBy   = pChar;

                char_to_room( pChar, pRoom );
#if 0
                /*
                 * Delete resets that have reset times lower then zero.
                 * ...
                 * We don't delete one-time only resets anymore because
                 * we need them for OLC saving.
                 */
                if ( pNPCReset->iResetTime < 0 )
                {
                    if ( pPrevNPCReset == NULL )
                    {
                        pRoom->pNPCResets    = pNPCReset->pNext;
                        free_obj_reset_list( &pNPCReset->pInvenResets );
                        free_obj_reset_list( &pNPCReset->pEqResets );
                        free_mem( (void **) &pNPCReset );
                        pNPCReset       = pRoom->pNPCResets;
                    }
                    else
                    {
                        pPrevNPCReset->pNext = pNPCReset->pNext;
                        free_obj_reset_list( &pNPCReset->pInvenResets );
                        free_obj_reset_list( &pNPCReset->pEqResets );
                        free_mem( (void **) &pNPCReset );
                        pNPCReset       = pPrevNPCReset->pNext;
                    }

                    pChar->pNPCData->pReset  = NULL;
                    pPrevNPCReset       = pNPCReset;
                }
                else
#endif
                {
                    pPrevNPCReset       = pNPCReset;
                    pNPCReset           = pNPCReset->pNext;
                }
            }

            for ( pObjReset = pRoom->pObjResets; pObjReset != NULL; )
            {
                if ( ( pObjIndex =
                  get_object_index( pObjReset->u1.iNumber ) ) == NULL )
                    sap_fatal(
                      "Room %d: No object with the number `%d'.",
                      pRoom->iNumber, pObjReset->u1.iNumber );

                pObjReset->u1.pObjIndex = pObjIndex;
                pObjReset->bAlive       = TRUE;
                pObjReset->iResetTimer  = -1;

                if ( pObjIndex->iItemType != NUMBER_ITEM_CONTAINER
                  && pObjReset->pContentResets != NULL )
                {
                    sap_warning(
                      "Room %d: Object placed in non-container object.",
                      pRoom->iNumber );
                    free_obj_reset_list( &pObjReset->pContentResets );
                }

                pObj                    = new_object( pObjIndex, NULL );
                pObj->pReset            = pObjReset;

                pObj->pContains         = translate_obj_resets(
                                            pObjReset->pContentResets,
                                            pRoom );

                for ( pTemp = pObj->pContains; pTemp != NULL;
                  pTemp = pTemp->pNextContent )
                    pTemp->pInObj       = pObj;

                obj_to_room( pObj, pRoom );

#if 0
                /*
                 * Delete resets that have reset times lower then zero.
                 * This makes one-time only objects possible :)
                 * ...
                 * We don't delete one-time only resets anymore because
                 * we need them for OLC saving.
                 */
                if ( pObjReset->iResetTime < 0 )
                {
                    if ( pPrevObjReset == NULL )
                    {
                        pRoom->pObjResets    = pObjReset->pNext;
                        free_obj_reset_list( &pObjReset->pContentResets );
                        free_mem( (void **) &pObjReset );
                        pObjReset       = pRoom->pObjResets;
                    }
                    else
                    {
                        pPrevObjReset->pNext = pObjReset->pNext;
                        free_obj_reset_list( &pObjReset->pContentResets );
                        free_mem( (void **) &pObjReset );
                        pObjReset       = pPrevObjReset->pNext;
                    }

                    pObj->pReset        = NULL;
                    pPrevObjReset       = pObjReset;
                }
                else
#endif
                {
                    pPrevObjReset       = pObjReset;
                    pObjReset           = pObjReset->pNext;
                }
            }
        }
    }
}


static OBJ_DATA *translate_obj_resets( OBJ_RESET_DATA *pObjResets,
                                       ROOM_INDEX_DATA *pRoom )
{
    OBJ_INDEX_DATA *pObjIndex;
    OBJ_RESET_DATA *pObjReset     = pObjResets;
    OBJ_RESET_DATA *pPrevObjReset = NULL;
    OBJ_DATA *pNewObjects         = NULL;
    OBJ_DATA *pObj;
    OBJ_DATA *pTemp;

    while ( pObjReset != NULL )
    {
        if ( ( pObjIndex = get_object_index( pObjReset->u1.iNumber ) )
          == NULL )
            sap_fatal( "Room %d: No object with the number `%d'.",
              pRoom->iNumber, pObjReset->u1.iNumber );

        pObjReset->u1.pObjIndex   = pObjIndex;
        pObjReset->bAlive         = TRUE;
        pObjReset->iResetTimer    = -1;

        if ( pObjIndex->iItemType != NUMBER_ITEM_CONTAINER
          && pObjReset->pContentResets != NULL )
        {
            sap_warning(
              "Room %d: Object placed in non-container object.",
              pRoom->iNumber );
            free_obj_reset_list( &pObjReset->pContentResets );
        }

        pObj                      = new_object( pObjIndex, NULL );
        pObj->pReset              = pObjReset;

        pObj->pContains           = translate_obj_resets(
                                      pObjReset->pContentResets,
                                      pRoom );

        for ( pTemp = pObj->pContains; pTemp; pTemp = pTemp->pNextContent )
            pTemp->pInObj         = pObj;

        pObj->pNextContent        = pNewObjects;
        pNewObjects               = pObj;
#if 0
        /*
         * Delete resets that have reset times lower then zero.
         * ...
         * We don't delete one-time only resets anymore because
         * we need them for OLC saving.
         */
        if ( pObjReset->iResetTime < 0 )
        {
            if ( pPrevObjReset == NULL )
            {
                pRoom->pObjResets = pObjReset->pNext;
                free_obj_reset_list( &pObjReset->pContentResets );
                free_mem( (void **) &pObjReset );
                pObjReset         = pObjResets;
            }
            else
            {
                pPrevObjReset->pNext = pObjReset->pNext;
                free_obj_reset_list( &pObjReset->pContentResets );
                free_mem( (void **) &pObjReset );
                pObjReset         = pPrevObjReset->pNext;
            }

            pObj->pReset          = NULL;
            pPrevObjReset         = pObjReset;
        }
        else
#endif
        {
            pPrevObjReset         = pObjReset;
            pObjReset             = pObjReset->pNext;
        }
    }

    return ( pNewObjects );
}


/*
 * Frees a linked list of object resets.
 */
static void free_obj_reset_list( OBJ_RESET_DATA **ppObjReset )
{
    OBJ_RESET_DATA *pObjReset;
    OBJ_RESET_DATA *pObjResetNext;

    for ( pObjReset = *ppObjReset; pObjReset; pObjReset = pObjResetNext )
    {
        pObjResetNext = pObjReset->pNext;
        free_obj_reset_list( &pObjReset->pContentResets );
        free_mem( (void **) &pObjReset );
    }

    *ppObjReset       = NULL;
}


/*
 * Do an NPC inheritence.
 */
static int npc_inherit( NPC_INDEX_DATA *pNPCIndex )
{
    NPC_INDEX_DATA *pNPCIndexBuf;
    int i;

    if ( pNPCIndex->iInherit <= 0 )
        return ( -1 );

    pNPCIndexBuf               = get_npc_index( pNPCIndex->iInherit );

    if ( pNPCIndexBuf == NULL )
        sap_fatal( "NPC %d: No NPC with number `%d' to inherit from.",
          pNPCIndex->iNumber, pNPCIndex->iInherit );

    if ( pNPCIndexBuf->iInherit > 0 )
        sap_fatal( "NPC %d: Inheriting from inherited NPC.",
          pNPCIndex->iNumber );

    if ( pNPCIndex->pProgram == NULL && pNPCIndexBuf->pProgram != NULL )
    {
        GENERIC_DATA *pGen;
        GENERIC_DATA *pGen2;
        QUEST_VAR_DATA *pVar;
        QUEST_VAR_DATA *pVar2;
        PROGRAM_DATA *pProgram;
        SCRIPT_DATA *pScript;
        SCRIPT_DATA *pScript2;
        TRIGGER_DATA *pTrigger;
        TRIGGER_DATA *pTrigger2;
        TRIGGER_ARG_DATA *pTriggerArg;
        TRIGGER_ARG_DATA *pTriggerArg2;

        pProgram            = alloc_mem( sizeof( *pProgram ) );

        pNPCIndex->pProgram = pProgram;

        for ( pGen = pNPCIndexBuf->pProgram->pQuestDataList; pGen != NULL;
          pGen = pGen->pNext )
        {
            pGen2           = alloc_mem( sizeof( *pGen2 ) );

            pGen2->pData    = pGen->pData;

            pGen2->pNext             = pProgram->pQuestDataList;
            pProgram->pQuestDataList = pGen2;
        }

        for ( pVar = pNPCIndexBuf->pProgram->pQuestVars; pVar != NULL;
          pVar = pVar->pNext )
        {
            pVar2                = alloc_mem( sizeof( *pVar2 ) );

            pVar2->iType         = pVar->iType;
            pVar2->sName         = save_string( pVar->sName );

            if ( pVar2->iType == NUMBER_VAR_STRING )
                pVar2->uData.s   = EMPTY_STRING;

            pVar2->pNext         = pProgram->pQuestVars;
            pProgram->pQuestVars = pVar2;
        }

        for ( pScript = pNPCIndexBuf->pProgram->pScripts; pScript != NULL;
          pScript = pScript->pNext )
        {
            pScript2             = alloc_mem( sizeof( *pScript2 ) );

            for ( pTrigger = pScript->pTriggers; pTrigger != NULL;
              pTrigger = pTrigger->pNext )
            {
                pTrigger2        = alloc_mem( sizeof( *pTrigger2 ) );

                for ( pTriggerArg = pTrigger->pArgs; pTriggerArg != NULL;
                  pTriggerArg = pTriggerArg->pNext )
                {
                    pTriggerArg2 = alloc_mem( sizeof( *pTriggerArg2 ) );

                    pTriggerArg2->iType       = pTriggerArg->iType;

                    switch ( pTrigger->iTrigger )
                    {
                      case NUMBER_NPC_TRIGGER_RANDOM:
                          pTriggerArg2->pArg  = alloc_mem( 1 );
                          *pTriggerArg2->pArg = *pTriggerArg->pArg;
                          break;

                      default                       :
                          pTriggerArg2->pArg  = str_dup(
                                                  pTriggerArg->pArg );
                          break;
                    }

                    pTriggerArg2->pNext       = pTrigger2->pArgs;
                    pTrigger2->pArgs          = pTriggerArg2;
                }

                pTrigger2->iTrigger           = pTrigger->iTrigger;

                pTrigger2->pNext              = pScript2->pTriggers;
                pScript2->pTriggers           = pTrigger2;
            }

            pScript2->pScript    = alloc_mem( pScript->iCodeSize );
            memcpy( pScript2->pScript,
              pScript->pScript, pScript->iCodeSize );
            pScript2->iCodeSize  = pScript->iCodeSize;
            pScript2->pName      = str_dup( pScript->pName );

            pScript2->bDisabled  = pScript->bDisabled;

            pScript2->pNext      = pProgram->pScripts;
            pProgram->pScripts   = pScript2;
        }
    }

    if ( pNPCIndex->pNameList[0] == EMPTY_STRING )
    {
        for ( i = 0; pNPCIndexBuf->pNameList[i] != NULL; i++ );

        pNPCIndex->pNameList        = alloc_mem( sizeof( string )
                                        * ( i + 1 ) );

        for ( i = 0; pNPCIndexBuf->pNameList[i] != NULL; i++ )
            pNPCIndex->pNameList[i] = save_string(
                                        pNPCIndexBuf->pNameList[i] );
    }

    if ( pNPCIndex->sShortDesc == EMPTY_STRING )
        pNPCIndex->sShortDesc  = pNPCIndexBuf->sShortDesc;

    for ( i = 0; i < 5; i++ )
    {
        if ( pNPCIndex->sDescs[i] == EMPTY_STRING )
            pNPCIndex->sDescs[i]   = pNPCIndexBuf->sDescs[i];
    }

    if ( pNPCIndex->sLongDesc == EMPTY_STRING )
        pNPCIndex->sLongDesc   = pNPCIndexBuf->sLongDesc;

    if ( pNPCIndex->fNPCFlags == 0 )
        pNPCIndex->fNPCFlags   = pNPCIndexBuf->fNPCFlags;

    if ( pNPCIndex->fPartFlags == 0 )
        pNPCIndex->fPartFlags   = pNPCIndexBuf->fPartFlags;

    if ( pNPCIndex->fActFlags == 0 )
        pNPCIndex->fActFlags   = pNPCIndexBuf->fActFlags;

    if ( pNPCIndex->iLevel == 1 )
        pNPCIndex->iLevel      = pNPCIndexBuf->iLevel;

    if ( pNPCIndex->iAlignment == 0 )
        pNPCIndex->iAlignment  = pNPCIndexBuf->iAlignment;

    if ( pNPCIndex->iRace == rRaceTable[0].iNumber )
        pNPCIndex->iRace       = pNPCIndexBuf->iRace;

    i                          = get_race_index( pNPCIndex->iRace );

    if ( pNPCIndex->iSex == snSexTable[0].iNumber )
        pNPCIndex->iSex        = pNPCIndexBuf->iSex;

    if ( pNPCIndex->iHairColor == rRaceTable[i].iHairColors[0] )
        pNPCIndex->iHairColor  = pNPCIndexBuf->iHairColor;

    if ( pNPCIndex->iEyeColor == rRaceTable[i].iEyeColors[0] )
        pNPCIndex->iEyeColor   = pNPCIndexBuf->iEyeColor;

    if ( pNPCIndex->iHeight == 65 )
        pNPCIndex->iHeight     = pNPCIndexBuf->iHeight;

    if ( pNPCIndex->iWeight == 130 )
        pNPCIndex->iWeight     = pNPCIndexBuf->iWeight;

    if ( pNPCIndex->iMaxCarry == 0 )
        pNPCIndex->iMaxCarry   = pNPCIndexBuf->iMaxCarry;

    if ( pNPCIndex->iPosition == NUMBER_POSITION_STANDING )
        pNPCIndex->iPosition   = pNPCIndexBuf->iPosition;

    if ( pNPCIndex->iGold == 0 )
        pNPCIndex->iGold       = pNPCIndexBuf->iGold;

    for ( i = 0; i < 3; i++ )
    {
        if ( pNPCIndex->VarStats.iStatStr[1] == 0 )
            pNPCIndex->VarStats.iStatStr[i]
              = pNPCIndexBuf->VarStats.iStatStr[i];

        if ( pNPCIndex->VarStats.iStatInt[1] == 0 )
            pNPCIndex->VarStats.iStatInt[i]
              = pNPCIndexBuf->VarStats.iStatInt[i];

        if ( pNPCIndex->VarStats.iStatWis[1] == 0 )
            pNPCIndex->VarStats.iStatWis[i]
              = pNPCIndexBuf->VarStats.iStatWis[i];

        if ( pNPCIndex->VarStats.iStatDex[1] == 0 )
            pNPCIndex->VarStats.iStatDex[i]
              = pNPCIndexBuf->VarStats.iStatDex[i];

        if ( pNPCIndex->VarStats.iStatCon[1] == 0 )
            pNPCIndex->VarStats.iStatCon[i]
              = pNPCIndexBuf->VarStats.iStatCon[i];

        if ( pNPCIndex->VarStats.iStatCha[1] == 0 )
            pNPCIndex->VarStats.iStatCha[i]
              = pNPCIndexBuf->VarStats.iStatCha[i];

        if ( pNPCIndex->VarStats.iStatLuc[1] == 0 )
            pNPCIndex->VarStats.iStatLuc[i]
              = pNPCIndexBuf->VarStats.iStatLuc[i];

        if ( pNPCIndex->VarStats.iStatHP[1] == 0 )
            pNPCIndex->VarStats.iStatHP[i]
              = pNPCIndexBuf->VarStats.iStatHP[i];

        if ( pNPCIndex->VarStats.iStatMP[1] == 0 )
            pNPCIndex->VarStats.iStatMP[i]
              = pNPCIndexBuf->VarStats.iStatMP[i];

        if ( pNPCIndex->VarStats.iStatMV[1] == 0 )
            pNPCIndex->VarStats.iStatMV[i]
              = pNPCIndexBuf->VarStats.iStatMV[i];

        if ( pNPCIndex->iExp[1] == 0 )
            pNPCIndex->iExp[i] = pNPCIndexBuf->iExp[i];
    }

    return ( 0 );
}


/*
 * Do an object inheritence.
 */
static int obj_inherit( OBJ_INDEX_DATA *pObjIndex )
{
    OBJ_INDEX_DATA *pObjIndexBuf;
    int i;

    if ( pObjIndex->iInherit <= 0 )
        return ( -1 );

    pObjIndexBuf              = get_object_index( pObjIndex->iInherit );

    if ( pObjIndexBuf == NULL )
        sap_fatal( "Object %d: No object with number `%d' to inherit.",
          pObjIndex->iNumber, pObjIndex->iInherit );

    if ( pObjIndexBuf->iInherit > 0 )
        sap_fatal( "Object %d: Inheriting from inherited object.",
          pObjIndex->iNumber );

    if ( pObjIndex->pProgram == NULL && pObjIndexBuf->pProgram != NULL )
    {
        GENERIC_DATA *pGen;
        GENERIC_DATA *pGen2;
        QUEST_VAR_DATA *pVar;
        QUEST_VAR_DATA *pVar2;
        PROGRAM_DATA *pProgram;
        SCRIPT_DATA *pScript;
        SCRIPT_DATA *pScript2;
        TRIGGER_DATA *pTrigger;
        TRIGGER_DATA *pTrigger2;
        TRIGGER_ARG_DATA *pTriggerArg;
        TRIGGER_ARG_DATA *pTriggerArg2;

        pProgram            = alloc_mem( sizeof( *pProgram ) );

        pObjIndex->pProgram = pProgram;

        for ( pGen = pObjIndexBuf->pProgram->pQuestDataList; pGen != NULL;
          pGen = pGen->pNext )
        {
            pGen2           = alloc_mem( sizeof( *pGen2 ) );

            pGen2->pData    = pGen->pData;

            pGen2->pNext             = pProgram->pQuestDataList;
            pProgram->pQuestDataList = pGen2;
        }

        for ( pVar = pObjIndexBuf->pProgram->pQuestVars; pVar != NULL;
          pVar = pVar->pNext )
        {
            pVar2                = alloc_mem( sizeof( *pVar2 ) );

            pVar2->iType         = pVar->iType;
            pVar2->sName         = save_string( pVar->sName );

            if ( pVar2->iType == NUMBER_VAR_STRING )
                pVar2->uData.s   = EMPTY_STRING;

            pVar2->pNext         = pProgram->pQuestVars;
            pProgram->pQuestVars = pVar2;
        }

        for ( pScript = pObjIndexBuf->pProgram->pScripts; pScript != NULL;
          pScript = pScript->pNext )
        {
            pScript2             = alloc_mem( sizeof( *pScript2 ) );

            for ( pTrigger = pScript->pTriggers; pTrigger != NULL;
              pTrigger = pTrigger->pNext )
            {
                pTrigger2        = alloc_mem( sizeof( *pTrigger2 ) );

                for ( pTriggerArg = pTrigger->pArgs; pTriggerArg != NULL;
                  pTriggerArg = pTriggerArg->pNext )
                {
                    pTriggerArg2 = alloc_mem( sizeof( *pTriggerArg2 ) );

                    pTriggerArg2->iType       = pTriggerArg->iType;

                    switch ( pTrigger->iTrigger )
                    {
                      case NUMBER_OBJ_TRIGGER_RANDOM:
                          pTriggerArg2->pArg  = alloc_mem( 1 );
                          *pTriggerArg2->pArg = *pTriggerArg->pArg;
                          break;

                      default                       :
                          pTriggerArg2->pArg  = str_dup(
                                                  pTriggerArg->pArg );
                          break;
                    }

                    pTriggerArg2->pNext       = pTrigger2->pArgs;
                    pTrigger2->pArgs          = pTriggerArg2;
                }

                pTrigger2->iTrigger           = pTrigger->iTrigger;

                pTrigger2->pNext              = pScript2->pTriggers;
                pScript2->pTriggers           = pTrigger2;
            }

            pScript2->pScript    = alloc_mem( pScript->iCodeSize );
            memcpy( pScript2->pScript,
              pScript->pScript, pScript->iCodeSize );
            pScript2->iCodeSize  = pScript->iCodeSize;
            pScript2->pName      = str_dup( pScript->pName );

            pScript2->bDisabled  = pScript->bDisabled;

            pScript2->pNext      = pProgram->pScripts;
            pProgram->pScripts   = pScript2;
        }
    }

    if ( pObjIndex->pNameList[0] == EMPTY_STRING )
    {
        for ( i = 0; pObjIndexBuf->pNameList[i] != NULL; i++ );

        pObjIndex->pNameList        = alloc_mem( sizeof( string )
                                        * ( i + 1 ) );

        for ( i = 0; pObjIndexBuf->pNameList[i] != NULL; i++ )
            pObjIndex->pNameList[i] = save_string(
                                        pObjIndexBuf->pNameList[i] );
    }

    if ( pObjIndex->sShortDesc == EMPTY_STRING )
        pObjIndex->sShortDesc = pObjIndexBuf->sShortDesc;

    if ( pObjIndex->sDesc == EMPTY_STRING )
        pObjIndex->sDesc      = pObjIndexBuf->sDesc;

    if ( pObjIndex->sLongDesc == EMPTY_STRING )
        pObjIndex->sLongDesc  = pObjIndexBuf->sLongDesc;

    if ( pObjIndex->fObjFlags == 0 )
        pObjIndex->fObjFlags  = pObjIndexBuf->fObjFlags;

    if ( pObjIndex->iItemType == snItemTypeTable[0].iNumber )
        pObjIndex->iItemType  = pObjIndexBuf->iItemType;

    if ( pObjIndex->iLevel == 1 )
        pObjIndex->iLevel     = pObjIndexBuf->iLevel;

    if ( pObjIndex->iWeight == 0 )
        pObjIndex->iWeight    = pObjIndexBuf->iWeight;

    if ( pObjIndex->iSize == NUMBER_SIZE_MEDIUM )
        pObjIndex->iSize      = pObjIndexBuf->iSize;

    if ( pObjIndex->iCost == 0 )
        pObjIndex->iCost      = pObjIndexBuf->iCost;

    if ( pObjIndex->iCondition == 1 )
        pObjIndex->iCondition = pObjIndexBuf->iCondition;

    if ( pObjIndex->iMaterial == snMaterialTable[0].iNumber )
        pObjIndex->iMaterial  = pObjIndexBuf->iMaterial;

    for ( i = 0; i < 6; i++ )
    {
        if ( pObjIndex->iValues[i] == 0 )
            pObjIndex->iValues[i]    = pObjIndexBuf->iValues[i];
    }

    if ( pObjIndex->sValue == EMPTY_STRING )
        pObjIndex->sValue     = pObjIndexBuf->sValue;

    return ( 0 );
}


static int fix_exit( ROOM_INDEX_DATA *pRoom, int i )
{
    ROOM_INDEX_DATA *pToRoom;
    int iNumber;
    int iRev;

    if ( pRoom->eExits[i].uRoom.iNumber == 0 )
        return ( -1 );

    if ( ( pToRoom = get_room_index(
      pRoom->eExits[i].uRoom.iNumber ) ) == NULL )
        sap_fatal( "Room %d: No room with the number `%d' for exit.",
          pRoom->iNumber, pRoom->eExits[i].uRoom.iNumber );

    pRoom->eExits[i].uRoom.pRoom = pToRoom;

    if ( pRoom->eExits[i].bShared == TRUE )
    {
        switch ( i )
        {
          case 0 : iRev              = 1; break;
          case 1 : iRev              = 0; break;
          case 2 : iRev              = 3; break;
          case 3 : iRev              = 2; break;
          case 4 : iRev              = 5; break;
          case 5 : iRev              = 4; break;
          case 6 : iRev              = 7; break;
          case 7 : iRev              = 6; break;
          case 8 : iRev              = 9; break;
          case 9 : iRev              = 8; break;
                      /* This should never happen. */
          default: iRev              = 0; break;
        }

        if ( pToRoom->eExits[iRev].uRoom.iNumber == 0 )
            sap_fatal( "Room %d: Cannot share from non-existent exit.",
              pRoom->iNumber );

        if ( pToRoom->eExits[iRev].bShared == TRUE )
            sap_fatal( "Room %d: Sharing from shared exit.",
              pRoom->iNumber );

        pRoom->eExits[i].uKey.iNumber = pToRoom->eExits[iRev].uKey.iNumber;
        pRoom->eExits[i].iExitType    = pToRoom->eExits[iRev].iExitType;
        pRoom->eExits[i].iResetTime   = pToRoom->eExits[iRev].iResetTime;
        pRoom->eExits[i].fOrigFlags   = pToRoom->eExits[iRev].fOrigFlags;
        pRoom->eExits[i].fCurrFlags   = pToRoom->eExits[iRev].fCurrFlags;
        pRoom->eExits[i].iLevel       = pToRoom->eExits[iRev].iLevel;
    }

    /*
     * Keys.
     */
    if ( pRoom->eExits[i].uRoom.pRoom && pRoom->eExits[i].uKey.iNumber )
    {
        if ( ( pRoom->eExits[i].uKey.pObjKey = get_object_index(
          ( iNumber = pRoom->eExits[i].uKey.iNumber ) ) ) == NULL )
            sap_fatal(
              "Room %d: No object with the number `%d' for door key.",
              pRoom->iNumber, iNumber );

        if ( pRoom->eExits[i].uKey.pObjKey->iItemType != NUMBER_ITEM_KEY )
            sap_warning( "Room %d: Object used for door key is not of "
              "item type `Key'.", pRoom->iNumber );
    }

    return ( 0 );
}


/*
 * End of main.c
 */