/
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include "sapphire.h"


/*
 * Defines
 */
#define STRING_HEADER_SIZE        ( sizeof( struct string_header ) - 1 )


/*
 * Structures
 */
struct string_header
{
    struct string_header *                                        pNext;
    unsigned short int                                         usiCount;
    unsigned short int                                           usiLen;
    char                                                        cStr[5];
};


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

static int                                                  iMappedFile;

int                                                       iMaxFragments;
static int                                                  iFragmented;
static struct string_header *                              pFirstHeader;
static struct string_header *                               pHeaderFree;

bool                                                      bStrComparing;


/*
 * Functions
 */
void mem_init( void )
{
    /*
     * Thanks to Furey (Developer of Merc and Envy) help with
     * mmap().
     */
    if ( iMemoryMode == MEMORY_MODE_DISK )
    {
        char cBuf[MAX_STRING];
        char c[MAX_STRING + 2];
        int i, iTemp;

        if ( lStringSpace <= 0 )
            sap_fatal( "Cannot map 0 bytes from file for strings." );

        if ( iMaxStrBuffers <= 0 )
            sap_fatal( "Cannot map 0 string buffers from file." );

        strcpy( cBuf, pTempDir );
        strcat( cBuf, MEMORY_FILE );

        if ( ( iMappedFile = open( cBuf, ( O_RDWR | O_CREAT ),
          ( S_IRUSR | S_IWUSR ) ) ) < 0 )
            sap_fatal( "Open file: Map file: %s.", strerror( errno ) );

        zero_out( c, ( MAX_STRING + 2 ) );

        iTemp    = ( ( lStringSpace / 512 ) + 1 );

        for ( i = 0; i < iTemp; i++ )
            write( iMappedFile, c, 512 );

        for ( i = 0; i < iMaxStrBuffers; i++ )
            write( iMappedFile, c, MAX_STRING );

        pStrings = mmap( NULL, ( lStringSpace + ( MAX_STRING
                     * iMaxStrBuffers ) ), ( PROT_READ | PROT_WRITE ),
                     MAP_SHARED, iMappedFile, 0 );

        if ( pStrings < 0 )
            sap_fatal( "Map: %s.", strerror( errno ) );

        pStringsEnd      = ( pStrings + lStringSpace - 1 );
        pStringsPos      = pStrings;
        pStrBuffers      = ( pStrings + lStringSpace );
        pStrBuffersBegin = pStrBuffers;
    }
    else
    {
        if ( lStringSpace <= 0 )
            sap_fatal( "%s: Cannot allocate 0 bytes for strings." );

        pStrings         = calloc( lStringSpace, 1 );

        if ( pStrings == NULL )
            sap_fatal(
              "Cannot allocate %ld bytes for strings.", lStringSpace );

        pStringsEnd      = ( pStrings + lStringSpace - 1 );
        pStringsPos      = pStrings;

        if ( iMaxStrBuffers <= 0 )
            sap_fatal( "Cannot allocate 0 string buffers." );

        pStrBuffers      = calloc( iMaxStrBuffers, MAX_STRING );

        if ( pStrBuffers == NULL )
            sap_fatal( "Cannot allocate %d string buffers.",
              iMaxStrBuffers );

        pStrBuffersBegin = pStrBuffers;
    }

    pFirstHeader         = (struct string_header *) pStrings;
    pHeaderFree          = pFirstHeader;
    pFirstHeader->usiLen = ( lStringSpace - STRING_HEADER_SIZE );
}


void mem_clean_up( void )
{
    if ( iMemoryMode == MEMORY_MODE_DISK )
    {
        char cBuf[MAX_STRING];

        munmap( pStrings, ( lStringSpace + ( MAX_STRING
          * iMaxStrBuffers ) ) );
        close( iMappedFile );

        strcpy( cBuf, pTempDir );
        strcat( cBuf, MEMORY_FILE );
        unlink( cBuf );
    }
    else
    {
        free_mem( (void **) &pStrBuffers );
        free_mem( (void **) &pStrings );
    }
}


/*
 * Allocates some memory and checks for failure.
 */
void *alloc_mem( register size_t rsMem )
{
    register void *pMem;

    if ( ( pMem = calloc( 1, rsMem ) ) == NULL )
    {
        lprintf( "**** [SYSTEM]:\n  "
          "Cannot allocate %ld bytes of memory.\n", (long) rsMem );
#ifdef DEBUG
        abort( );
#else
        exit( 1 );
#endif
    }

    return ( pMem );
}


/*
 * Reallocates some memory and checks for failure.
 */
void *realloc_mem( register void *pMem, register size_t rsMem )
{
    if ( ( pMem = realloc( pMem, rsMem ) ) == NULL )
    {
        lprintf( "**** [SYSTEM]:\n  "
          "Cannot reallocate %ld bytes of memory.\n", (long) rsMem );
#ifdef DEBUG
        abort( );
#else
        exit( 1 );
#endif
    }

    return ( pMem );
}


/*
 * Free some memory and set the pointer to NULL.
 */
void free_mem( register void **ppMem )
{
    if ( ppMem != NULL && *ppMem != NULL )
    {
        free( *ppMem );
        *ppMem = NULL;
    }
}


/*
 * Initialize some memory.
 */
void zero_out( register void *pMem, register size_t rsMem )
{
    register byte *pBMem = pMem;
    register size_t rs   = 0;

    while ( rs < rsMem )
         pBMem[rs++]     = 0;
}


/*
 * Duplicates a string in memory.
 */
char *str_dup( register char *pStr )
{
    register int riLen;

    if ( pStr == NULL || pStr[0] == '\0' )
        return ( EMPTY_STRING );

    riLen     = strlen( pStr );

    if ( riLen >= MAX_STRING )
        riLen = ( MAX_STRING - 1 );

    return ( strncpy( alloc_mem( riLen + 1 ), pStr, riLen ) );
}


/*
 * Frees a string allocated by str_dup() and returns a buffered copy
 * of it.
 *
 * No longer used as of Sapphire v0.3
 */
#if 0
char *unstr_dup( register char **ppStr )
{
    register char *pOutput;
    register int riLen;

    if ( ppStr == NULL || *ppStr == NULL || *ppStr[0] == '\0' )
        return ( EMPTY_STRING );

    riLen     = strlen( *ppStr );

    if ( riLen >= MAX_STRING )
        riLen = ( MAX_STRING - 1 );

    pOutput   = strncpy( new_buffer( ), *ppStr, riLen );
    free_mem( (void **) ppStr );
    return ( pOutput );
}
#endif

/*
 * Allocates space for a string.  Better then str_dup() for most
 * things.
 */
string save_string( register char *pStr )
{
    return ( str_dup( pStr ) );

#if 0
    register struct string_header *p;
    register int riLen;

    if ( pStr == NULL || pStr[0] == '\0' )
        return ( EMPTY_STRING );

    if ( pStr > pStrings && pStr < pStringsEnd )
    {
        p       = (struct string_header *) ( pStr - STRING_HEADER_SIZE );
        p->usiCount++;
        return ( pStr );
    }

    if ( iFragmented == iMaxFragments )
        defrag_string_space( );

    if ( pHeaderFree == NULL )
        return ( str_dup( pStr ) );

    riLen       = ( strlen( pStr ) + 1 );

    if ( riLen >= MAX_STRING )
        riLen   = ( MAX_STRING - 1 );

    for ( p = pHeaderFree; p != NULL; p = p->pNext )
    {
        if ( p->usiCount == 0 && p->usiLen >= riLen )
            break;
    }

    if ( p == NULL )
        return ( str_dup( pStr ) );

    if ( ( p->usiLen - riLen ) > STRING_HEADER_SIZE ) 
    {
        register struct string_header *pTemp;

        pTemp   = (struct string_header *) ( (char *) p + riLen
                                           + STRING_HEADER_SIZE );
        pTemp->pNext    = p->pNext;
        pTemp->usiCount = 0;
        pTemp->usiLen   = p->usiLen - ( riLen + STRING_HEADER_SIZE );
        p->usiLen       = riLen;
        p->pNext        = pTemp;

        if ( p == pHeaderFree )
            pHeaderFree = pTemp;
    }
    else if ( p == pHeaderFree )
    {
        for ( pHeaderFree = pFirstHeader; pHeaderFree != NULL;
          pHeaderFree = pHeaderFree->pNext )
        {
            if ( pHeaderFree->usiCount == 0 )
                break;
        }
    }

    strncpy( p->cStr, pStr, riLen );
    p->usiCount++;
    return ( p->cStr );
#endif
}


/*
 * Frees a string allocated by save_string().
 */
void free_string( register string *pStr )
{
    if ( pStr == NULL || *pStr == NULL || **pStr == '\0' )
        return;

    free_mem( (void **) pStr );

#if 0
    register struct string_header *p;

    if ( pStr == NULL || *pStr == NULL || **pStr == '\0' )
        return;

    if ( *pStr > pStrings && *pStr < pStringsEnd )
    {
        p = (struct string_header *) ( *pStr - STRING_HEADER_SIZE );

        if ( --p->usiCount > 0 )
             return;

        if ( pHeaderFree > p )
            pHeaderFree = p;

        if ( p->pNext != NULL && p->pNext->usiCount == 0 )
        {
            p->usiLen  += ( p->pNext->usiLen + STRING_HEADER_SIZE );
            p->pNext    = p->pNext->pNext;
        }

        iFragmented--;
        *pStr           = NULL;
    }
    else
        free_mem( (void **) pStr );
#endif
}


/*
 * Merges free blocks.  Usually only called if save_string() cannot
 * find a large enough free block.
 */
void defrag_string_space( void )
{
    register struct string_header *p;
    register struct string_header *pLast;

    pLast                   = NULL;
    pHeaderFree             = NULL;

    for ( p = pFirstHeader; p != NULL; p = p->pNext )
    {
        if ( p->usiCount > 0 )
        {
            pLast           = NULL;
            continue;
        }

        if ( pLast == NULL )
        {
            pLast           = p;

            if ( p > pHeaderFree )
                pHeaderFree = p; 

            continue;
        }

        pLast->usiLen      += ( p->usiLen + STRING_HEADER_SIZE );
        pLast->pNext        = p->pNext;
    }

    iFragmented             = 0;
}


/*
 * Clear all string buffers.
 */
void free_buffers( void )
{
    pStrBuffers    = pStrBuffersBegin;
    iNumStrBuffers = 0;
}


/*
 * Grab a new buffer.
 */
char *new_buffer( void )
{
    register char *pNewBuffer;

    if ( iNumStrBuffers == iMaxStrBuffers )
    {
        sap_warning( "No free string buffers; freeing." );
        free_buffers( );
    }

    pNewBuffer   = pStrBuffers;
    pStrBuffers += MAX_STRING;
    iNumStrBuffers++;
    *pNewBuffer  = '\0';

    return ( pNewBuffer );
}


/*
 * Create a new PC (Player Character).
 */
CHAR_DATA *new_pc( void )
{
    CHAR_DATA *pCharNew;

    if ( pCharFree == NULL )
    {
        pCharNew      = alloc_mem( sizeof( *pCharNew ) );
        zero_out( pCharNew, sizeof( *pCharNew ) );
    }
    else
    {
        pCharNew      = pCharFree;
        pCharFree     = pCharFree->pNext;
    }

    pCharNew->pPCData = alloc_mem( sizeof( *pCharNew->pPCData ) );
    zero_out( pCharNew->pPCData, sizeof( *pCharNew->pPCData ) );

    pCharNew->pStats  = alloc_mem( sizeof( *pCharNew->pStats ) );
    zero_out( pCharNew->pStats, sizeof( *pCharNew->pStats ) );

    pCharNew->iCharType          = CHAR_PC;
    pCharNew->sLongDesc          = EMPTY_STRING;
    pCharNew->pPCData->sPassword = EMPTY_STRING;
    pCharNew->pPCData->sName     = EMPTY_STRING;
    pCharNew->pPCData->sPrompt   = save_string( ">" );
    pCharNew->pPCData->sGuild    = EMPTY_STRING;

    char_to_list( pCharNew );

    return ( pCharNew );
}


/*
 * Create a new instance of an NPC (Non-Player Character).
 */
CHAR_DATA *new_npc( NPC_INDEX_DATA *pNPCIndex, CHAR_DATA *pActor )
{
    CHAR_DATA *pNewNPC;
    PROGRAM_DATA *pProgram;
    SCRIPT_DATA *pScript;
    TRIGGER_DATA *pTrigger;
    int i;

    if ( pCharFree == NULL )
    {
        pNewNPC             = alloc_mem( sizeof( *pNewNPC ) );
        zero_out( pNewNPC, sizeof( *pNewNPC ) );
    }
    else
    {
        pNewNPC             = pCharFree;
        pCharFree           = pCharFree->pNext;
    }

    pNewNPC->pNPCData       = alloc_mem( sizeof( *pNewNPC->pNPCData ) );
    zero_out( pNewNPC->pNPCData, sizeof( *pNewNPC->pNPCData ) );

    pNewNPC->pStats         = alloc_mem( sizeof( *pNewNPC->pStats ) );
    pNewNPC->iCharType      = CHAR_NPC;
    pNewNPC->pNPCData->pNPCIndex = pNPCIndex;

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

        pProgram                     = alloc_mem( sizeof( *pProgram ) );

        pNewNPC->pNPCData->pProgram  = pProgram;

        for ( pGen = pNPCIndex->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 = pNPCIndex->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 = pNPCIndex->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;
        }
    }

    /*
     * Roll up this NPC's stats.
     */
    pNewNPC->pStats->iStatStr    = ( ( ( i = roll_dice(
                                     pNPCIndex->VarStats.iStatStr[0],
                                     pNPCIndex->VarStats.iStatStr[1] ) )
                                     + pNPCIndex->VarStats.iStatStr[2] )
                                     > 99 ? 99 : i );
    pNewNPC->pStats->iStatInt    = ( ( ( i = roll_dice(
                                     pNPCIndex->VarStats.iStatInt[0],
                                     pNPCIndex->VarStats.iStatInt[1] ) )
                                     + pNPCIndex->VarStats.iStatInt[2] )
                                     > 99 ? 99 : i );
    pNewNPC->pStats->iStatWis    = ( ( ( i = roll_dice(
                                     pNPCIndex->VarStats.iStatWis[0],
                                     pNPCIndex->VarStats.iStatWis[1] ) )
                                     + pNPCIndex->VarStats.iStatWis[2] )
                                     > 99 ? 99 : i );
    pNewNPC->pStats->iStatDex    = ( ( ( i = roll_dice(
                                     pNPCIndex->VarStats.iStatDex[0],
                                     pNPCIndex->VarStats.iStatDex[1] ) )
                                     + pNPCIndex->VarStats.iStatDex[2] )
                                     > 99 ? 99 : i );
    pNewNPC->pStats->iStatCon    = ( ( ( i = roll_dice(
                                     pNPCIndex->VarStats.iStatCon[0],
                                     pNPCIndex->VarStats.iStatCon[1] ) )
                                     + pNPCIndex->VarStats.iStatCon[2] )
                                     > 99 ? 99 : i );
    pNewNPC->pStats->iStatCha    = ( ( ( i = roll_dice(
                                     pNPCIndex->VarStats.iStatCha[0],
                                     pNPCIndex->VarStats.iStatCha[1] ) )
                                     + pNPCIndex->VarStats.iStatCha[2] )
                                     > 99 ? 99 : i );
    pNewNPC->pStats->iStatLuc    = ( ( ( i = roll_dice(
                                     pNPCIndex->VarStats.iStatLuc[0],
                                     pNPCIndex->VarStats.iStatLuc[1] ) )
                                     + pNPCIndex->VarStats.iStatLuc[2] )
                                     > 99 ? 99 : i );
    pNewNPC->pStats->iStatHP     = ( ( ( i = roll_dice(
                                     pNPCIndex->VarStats.iStatHP[0],
                                     pNPCIndex->VarStats.iStatHP[1] ) )
                                     + pNPCIndex->VarStats.iStatHP[2] )
                                     > 9999 ? 9999 : i );
    pNewNPC->pStats->iStatMP     = ( ( ( i = roll_dice(
                                     pNPCIndex->VarStats.iStatMP[0],
                                     pNPCIndex->VarStats.iStatMP[1] ) )
                                     + pNPCIndex->VarStats.iStatMP[2] )
                                     > 9999 ? 9999 : i );
    pNewNPC->pStats->iStatMV     = ( ( ( i = roll_dice(
                                     pNPCIndex->VarStats.iStatMV[0],
                                     pNPCIndex->VarStats.iStatMV[1] ) )
                                     + pNPCIndex->VarStats.iStatMV[2] )
                                     > 9999 ? 9999 : i );

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

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

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

    pNewNPC->pNPCData->sShortDesc     = save_string(
                                          pNPCIndex->sShortDesc );

    for ( i = 0; i < 5; i++ )
        pNewNPC->pNPCData->sDescs[i]  = save_string(
                                          pNPCIndex->sDescs[i] );

    for ( i = 0; i < 3; i++ )
        pNewNPC->pNPCData->iDamage[i] = pNPCIndex->iDamage[i];

    pNewNPC->pNPCData->iDamType       = pNPCIndex->iDamType;
    pNewNPC->pNPCData->iWander        = pNPCIndex->iWander;
    pNewNPC->pNPCData->fNPCFlags      = pNPCIndex->fNPCFlags;

    pNewNPC->sLongDesc      = save_string( pNPCIndex->sLongDesc );
    pNewNPC->fPartFlags     = pNPCIndex->fPartFlags;
    pNewNPC->fActFlags      = pNPCIndex->fActFlags;
    pNewNPC->iLevel         = pNPCIndex->iLevel;
    pNewNPC->iExp           = ( ( ( i = roll_dice( pNPCIndex->iExp[0],
                                pNPCIndex->iExp[1] ) )
                                + pNPCIndex->iExp[2] )
                                > 9999 ? 9999 : i );
    pNewNPC->iAlignment     = pNPCIndex->iAlignment;
    pNewNPC->iRace          = pNPCIndex->iRace;
    pNewNPC->iSex           = pNPCIndex->iSex;
    pNewNPC->iHairColor     = pNPCIndex->iHairColor;
    pNewNPC->iEyeColor      = pNPCIndex->iEyeColor;
    pNewNPC->iHeight        = pNPCIndex->iHeight;
    pNewNPC->iWeight        = pNPCIndex->iWeight;
    pNewNPC->iMaxCarry      = pNPCIndex->iMaxCarry;
    pNewNPC->iGold          = pNPCIndex->iGold;
    pNewNPC->iHP            = pNewNPC->pStats->iStatHP;
    pNewNPC->iMP            = pNewNPC->pStats->iStatMP;
    pNewNPC->iMV            = pNewNPC->pStats->iStatMV;
    pNewNPC->iPosition      = pNPCIndex->iPosition;

    char_to_list( pNewNPC );

    if ( ( pProgram = pNewNPC->pNPCData->pProgram ) != NULL )
    {
        for ( pScript = pProgram->pScripts; pScript != NULL;
          pScript = pScript->pNext )
        {
            if ( pScript->bDisabled == TRUE
              /* || pScript->bRunning == TRUE */ )
                continue;

            for ( pTrigger = pScript->pTriggers; pTrigger != NULL;
              pTrigger = pTrigger->pNext )
            {
                if ( pTrigger->iTrigger == NUMBER_NPC_TRIGGER_CREATED )
                {
                    execute_npc_script( pProgram, pScript, NULL, pNewNPC,
                      pActor, NULL );
                    break;
                }
            }
        }
    }

    return ( pNewNPC );
}


/*
 * Create a new object.
 */
OBJ_DATA *new_object( OBJ_INDEX_DATA *pObjIndex, CHAR_DATA *pActor )
{
    OBJ_DATA *pNewObj;
    int i;

    if ( pObjFree == NULL )
        pNewObj               = alloc_mem( sizeof( *pNewObj ) );
    else
    {
        pNewObj               = pObjFree;
        pObjFree              = pObjFree->pNext;
    }

    pNewObj->pObjIndex        = pObjIndex;

    if ( pObjIndex->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 ) );

        pNewObj->pProgram   = pProgram;

        for ( pGen = pObjIndex->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 = pObjIndex->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 = pObjIndex->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;
        }
    }

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

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

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

    pNewObj->sShortDesc       = save_string( pObjIndex->sShortDesc );
    pNewObj->sDesc            = save_string( pObjIndex->sDesc );
    pNewObj->sLongDesc        = save_string( pObjIndex->sLongDesc );
    pNewObj->fObjFlags        = pObjIndex->fObjFlags;
    pNewObj->iItemType        = pObjIndex->iItemType;
    pNewObj->iLevel           = pObjIndex->iLevel;
    pNewObj->iWeight          = pObjIndex->iWeight;
    pNewObj->iSize            = pObjIndex->iSize;
    pNewObj->iCost            = pObjIndex->iCost;
    pNewObj->iCondition       = pObjIndex->iCondition;
    pNewObj->iMaterial        = pObjIndex->iMaterial;

    for ( i = 0; i < 6; i++ )
        pNewObj->iValues[i]   = pObjIndex->iValues[i];

    pNewObj->sValue           = pObjIndex->sValue;

    obj_to_list( pNewObj );

    return ( pNewObj );
}


/*
 * Free one character.
 */
void free_char( CHAR_DATA **ppChar )
{
    CHAR_DATA *pChar = *ppChar;
    OBJ_DATA *pObj;
    OBJ_DATA *pObjNext;
    QUEST_DATA *pQuestData;
    QUEST_VAR_DATA *pVar;
    RUNNING_SCRIPT_DATA *pRunning;
    RUNNING_SCRIPT_DATA *pRunningNext;
    int i;

    char_from_list( pChar );

    if ( pChar->pInRoom != NULL )
        char_from_room( *ppChar );

    if ( pChar->iCharType == CHAR_PC )
    {
        KNOWN_PERSON_DATA *pKnownPerson;
        KNOWN_PERSON_DATA *pKnownPersonNext;
        KNOWN_EXIT_DATA *pKnownExit;
        KNOWN_EXIT_DATA *pKnownExitNext;

        free_string( &pChar->pPCData->sPassword );
        free_string( &pChar->pPCData->sName );
        free_string( &pChar->pPCData->sPrompt );

        for ( pKnownPerson = pChar->pPCData->pKnownPeople; pKnownPerson;
          pKnownPerson = pKnownPersonNext )
        {
            pKnownPersonNext = pKnownPerson->pNext;
            free_string( &pKnownPerson->sName );
            free_mem( (void **) &pKnownPerson );
        }

        for ( pKnownExit = pChar->pPCData->pKnownExits; pKnownExit;
          pKnownExit = pKnownExitNext )
        {
            pKnownExitNext = pKnownExit->pNext;
            free_mem( (void **) &pKnownExit );
        }
    }
    else if ( pChar->iCharType == CHAR_NPC )
    {
        PROGRAM_DATA *pProgram;

        if ( pChar->pNPCData->pReset != NULL )
            pChar->pNPCData->pReset->bAlive = FALSE;

        if ( ( pProgram = pChar->pNPCData->pProgram ) != NULL )
        {
            GENERIC_DATA *pGen;
            GENERIC_DATA *pGenNext;
            SCRIPT_DATA *pScript;
            SCRIPT_DATA *pScriptNext;
            TRIGGER_DATA *pTrigger;
            TRIGGER_DATA *pTriggerNext;
            TRIGGER_ARG_DATA *pTriggerArg;
            TRIGGER_ARG_DATA *pTriggerArgNext;
            QUEST_VAR_DATA *pVarNext;

            for ( pGen = pProgram->pQuestDataList; pGen; pGen = pGenNext )
            {
                pGenNext                = pGen->pNext;
                free_mem( (void **) &pGen );
            }

            for ( pVar = pProgram->pQuestVars; pVar; pVar = pVarNext )
            {
                pVarNext                = pVar->pNext;

                free_string( &pVar->sName );

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

                free_mem( (void **) &pVar );
            }

            for ( pScript = pProgram->pScripts; pScript != NULL;
              pScript = pScriptNext )
            {
                pScriptNext             = pScript->pNext;

                for ( pTrigger = pScript->pTriggers; pTrigger != NULL;
                  pTrigger = pTriggerNext )
                {
                    pTriggerNext        = pTrigger->pNext;

                    for ( pTriggerArg = pTrigger->pArgs; pTriggerArg;
                      pTriggerArg = pTriggerArgNext )
                    {
                        pTriggerArgNext = pTriggerArg->pNext;

                        switch ( pTrigger->iTrigger )
                        {
                          case NUMBER_NPC_TRIGGER_RANDOM:
                              free_mem( (void **) &pTriggerArg->pArg );
                              break;

                          default                       :
                              str_free( pTriggerArg->pArg );
                              break;
                        }
                    }

                    free_mem( (void **) &pTrigger );
                }

                str_free( pScript->pName );
                free_mem( (void **) &pScript->pScript );
                free_mem( (void **) &pScript );
            }

            free_mem( (void **) &pChar->pNPCData->pProgram );
        }

        for ( i = 0; pChar->pNPCData->pNameList[i] != NULL; i++ )
            free_string( &pChar->pNPCData->pNameList[i] );

        free_mem( (void **) &pChar->pNPCData->pNameList );
        free_string( &pChar->pNPCData->sShortDesc );

        for ( i = 0; i < 5; i++ )
            free_string( &pChar->pNPCData->sDescs[i] );
    }

    free_mem( (void **) &pChar->pStats );

    for ( pObj = pChar->pInven; pObj; pObj = pObjNext )
    {
        pObjNext     = pObj->pNextContent;
        free_obj( &pObj );
    }

    free_string( &pChar->sLongDesc );
    zero_out( pChar, sizeof( *pChar ) );

    /*
     * ... This is a bit messy but we need to make sure scripts that
     * are paused don't restart with bad pointers.
     */
    for ( pQuestData = pQuestDataList; pQuestData != NULL;
      pQuestData = pQuestData->pNext )
    {
        for ( pVar = pQuestData->pVars; pVar != NULL; pVar = pVar->pNext )
        {
            if ( pVar->iType == NUMBER_VAR_POINTER_CHAR
              && pVar->uData.p == pChar )
                pVar->uData.p       = NULL;
        }
    }

    for ( pRunning = pRunningScriptList; pRunning != NULL;
      pRunning = pRunningNext )
    {
        pRunningNext = pRunning->pNext;

        if ( pRunning->iThisType == NUMBER_VAR_POINTER_CHAR
          && pChar == pRunning->pThis )
        {
            if ( pRunning == pRunningScriptList )
                pRunningScriptList   = pRunning->pNext;
            else
            {
                RUNNING_SCRIPT_DATA *pPrev;

                for ( pPrev = pRunningScriptList; pPrev != NULL;
                  pPrev = pPrev->pNext )
                {
                    if ( pPrev->pNext == pRunning )
                    {
                        pPrev->pNext = pRunning->pNext;
                        break;
                    }
                }
#ifdef DEBUG
                if ( pPrev == NULL )
                    wcdebug( "Running script not found." );
#endif
            }

            free_mem( (void **) &pRunning );
            continue;
        }

        for ( pVar = pRunning->pProgram->pQuestVars; pVar != NULL;
          pVar = pVar->pNext )
        {
            if ( pVar->iType == NUMBER_VAR_POINTER_CHAR
              && pVar->uData.p == pChar )
                pVar->uData.p       = NULL;
        }

        if ( pChar == pRunning->pActor )
            pRunning->pActor = NULL;

        if ( pChar == pRunning->pThird )
            pRunning->pThird = NULL;
    }

    pChar->pNext     = pCharFree;
    pCharFree        = pChar;

    *ppChar          = NULL;
}


/*
 * Free one object.
 */
void free_obj( OBJ_DATA **ppObj )
{
    OBJ_DATA *pObj  = *ppObj;
    OBJ_DATA *pObjContent;
    OBJ_DATA *pObjNext;
    PROGRAM_DATA *pProgram;
    RUNNING_SCRIPT_DATA *pRunning;
    RUNNING_SCRIPT_DATA *pRunningNext;
    QUEST_DATA *pQuestData;
    QUEST_VAR_DATA *pVar;
    int i;

    obj_from_list( pObj );

    if ( pObj->pInRoom != NULL )
        obj_from_room( pObj );
    else if ( pObj->pCarriedBy != NULL )
        obj_from_char( pObj );
    else if ( pObj->pInObj != NULL )
        obj_from_obj( pObj );

    if ( pObj->pReset != NULL )
        pObj->pReset->bAlive = FALSE;

    if ( ( pProgram = pObj->pProgram ) != NULL )
    {
        GENERIC_DATA *pGen;
        GENERIC_DATA *pGenNext;
        SCRIPT_DATA *pScript;
        SCRIPT_DATA *pScriptNext;
        TRIGGER_DATA *pTrigger;
        TRIGGER_DATA *pTriggerNext;
        TRIGGER_ARG_DATA *pTriggerArg;
        TRIGGER_ARG_DATA *pTriggerArgNext;
        QUEST_VAR_DATA *pVarNext;

        for ( pGen = pProgram->pQuestDataList; pGen; pGen = pGenNext )
        {
            pGenNext                = pGen->pNext;
            free_mem( (void **) &pGen );
        }

        for ( pVar = pProgram->pQuestVars; pVar; pVar = pVarNext )
        {
            pVarNext                = pVar->pNext;

            free_string( &pVar->sName );

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

            free_mem( (void **) &pVar );
        }

        for ( pScript = pProgram->pScripts; pScript != NULL;
          pScript = pScriptNext )
        {
            pScriptNext             = pScript->pNext;

            for ( pTrigger = pScript->pTriggers; pTrigger != NULL;
              pTrigger = pTriggerNext )
            {
                pTriggerNext        = pTrigger->pNext;

                for ( pTriggerArg = pTrigger->pArgs; pTriggerArg;
                  pTriggerArg = pTriggerArgNext )
                {
                    pTriggerArgNext = pTriggerArg->pNext;

                    switch ( pTrigger->iTrigger )
                    {
                      case NUMBER_OBJ_TRIGGER_RANDOM:
                          free_mem( (void **) &pTriggerArg->pArg );
                          break;

                      default                       :
                          str_free( pTriggerArg->pArg );
                          break;
                    }
                }

                free_mem( (void **) &pTrigger );
            }

            str_free( pScript->pName );
            free_mem( (void **) &pScript->pScript );
            free_mem( (void **) &pScript );
        }

        free_mem( (void **) &pObj->pProgram );
    }

    for ( pObjContent = pObj->pContains; pObjContent != NULL;
      pObjContent = pObjNext )
    {
        pObjNext    = pObjContent->pNextContent;
        free_obj( &pObjContent );
    }

    for ( i = 0; pObj->pNameList[i] != NULL; i++ )
        free_string( &pObj->pNameList[i] );

    free_mem( (void **) &pObj->pNameList );
    free_string( &pObj->sShortDesc );
    free_string( &pObj->sDesc );
    free_string( &pObj->sLongDesc );
    zero_out( pObj, sizeof( *pObj ) );

    /*
     * ... This is a bit messy but we need to make sure scripts that
     * are paused don't restart with bad pointers.
     */
    for ( pQuestData = pQuestDataList; pQuestData != NULL;
      pQuestData = pQuestData->pNext )
    {
        for ( pVar = pQuestData->pVars; pVar; pVar = pVar->pNext )
        {
            if ( pVar->iType == NUMBER_VAR_POINTER_OBJ
              && pVar->uData.p == pObj )
                pVar->uData.p       = NULL;
        }
    }

    for ( pRunning = pRunningScriptList; pRunning != NULL;
      pRunning = pRunningNext )
    {
        pRunningNext = pRunning->pNext;

        for ( pVar = pRunning->pProgram->pQuestVars; pVar != NULL;
          pVar = pVar->pNext )
        {
            if ( pVar->iType == NUMBER_VAR_POINTER_OBJ
              && pVar->uData.p == pObj )
                pVar->uData.p       = NULL;
        }
    }

    pObj->pNext     = pObjFree;
    pObjFree        = pObj;

    *ppObj          = NULL;
}


/*
 * End of memory.c
 */