RaM Fire Updated/
/*
 * RAM $Id: olc_save.c 81 2009-01-14 06:16:31Z ghasatta $
 */

/**************************************************************************
 *  File: olc_save.c                                                       *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 *                                                                         *
 *  This code was freely distributed with the The Isles 1.1 source code,   *
 *  and has been used here for OLC - OLC would not be what it is without   *
 *  all the previous coders who released their source code.                *
 *                                                                         *
 ***************************************************************************/
/* OLC_SAVE.C
 * This takes care of saving all the .are information.
 * Notes:
 * -If a good syntax checker is used for setting vnum ranges of areas
 *  then it would become possible to just cycle through vnums instead
 *  of using the iHash stuff and checking that the room or reset or
 *  mob etc is part of that area.
 */

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "merc.h"
#include "tables.h"
#include "db.h"
#include "interp.h"
#include "special.h"
#include "strings.h"
#include "act.h"
#include "olc.h"

#define DIF(a,b) (~((~a)|(b)))

/*
 *  Verbose writes reset data in plain english into the comments
 *  section of the resets.  It makes areas considerably larger but
 *  may aid in debugging.
 */

#define VERBOSE

/*****************************************************************************
 Name:                fix_string
 Purpose:        Returns a string without \r and ~.
 ****************************************************************************/
char                   *fix_string( const char *str )
{
    static char             strfix[MAX_STRING_LENGTH * 2] = "\0\0\0\0\0\0\0";
    int                     i = 0;
    int                     o = 0;

    if ( str == NULL )
        return '\0';

    for ( o = i = 0; str[i + o] != '\0'; i++ )
    {
        if ( str[i + o] == '\r' || str[i + o] == '~' )
            o++;
        strfix[i] = str[i + o];
    }
    strfix[i] = '\0';
    return strfix;
}

/*****************************************************************************
 Name:                save_area_list
 Purpose:        Saves the listing of files to be loaded at startup.
 Called by:        do_asave(olc_save.c).
 ****************************************************************************/
void save_area_list(  )
{
    FILE                   *fp = NULL;
    AREA_DATA              *pArea = NULL;
    HELP_AREA              *ha = NULL;

    if ( ( fp = fopen( "area.lst", "w" ) ) == NULL )
    {
        log_error( "Save_area_list: fopen" );
        perror( "area.lst" );
    }
    else
    {
        /*
         * Add any help files that need to be loaded at
         * startup to this section.
         */
        fprintf( fp, "social.are\n" );                 /* ROM OLC */

        for ( ha = had_list; ha; ha = ha->next )
            if ( ha->area == NULL )
                fprintf( fp, "%s\n", ha->filename );

        for ( pArea = area_first; pArea; pArea = pArea->next )
        {
            fprintf( fp, "%s\n", pArea->file_name );
        }

        fprintf( fp, "$\n" );
        fclose( fp );
    }

    return;
}

/*
 * ROM OLC
 * Used in save_mobile and save_object below.  Writes
 * flags on the form fread_flag reads.
 * 
 * buf[] must hold at least 32+1 characters.
 *
 * -- Hugin
 */
char                   *fwrite_flag( int flags, char buf[] )
{
    char                    offset = '\0';
    char                   *cp = NULL;

    buf[0] = '\0';

    if ( flags == 0 )
    {
        strcpy( buf, "0" );
        return buf;
    }

    /*
     * 32 -- number of bits in an int
     */

    for ( offset = 0, cp = buf; offset < 32; offset++ )
        if ( flags & ( ( int ) 1 << offset ) )
        {
            if ( offset <= 'Z' - 'A' )
                *( cp++ ) = 'A' + offset;
            else
                *( cp++ ) = 'a' + offset - ( 'Z' - 'A' + 1 );
        }

    *cp = '\0';

    return buf;
}

void save_mobprogs( FILE * fp, AREA_DATA *pArea )
{
    MPROG_CODE             *pMprog = NULL;
    int                     i = 0;

    fprintf( fp, "#MOBPROGS\n" );

    for ( i = pArea->min_vnum; i <= pArea->max_vnum; i++ )
    {
        if ( ( pMprog = get_mprog_index( i ) ) != NULL )
        {
            fprintf( fp, "#%d\n", i );
            fprintf( fp, "%s~\n", fix_string( pMprog->code ) );
        }
    }

    fprintf( fp, "#0\n\n" );
    return;
}

/*****************************************************************************
 Name:                save_mobile
 Purpose:        Save one mobile to file, new format -- Hugin
 Called by:        save_mobiles (below).
 ****************************************************************************/
void save_mobile( FILE * fp, MOB_INDEX_DATA *pMobIndex )
{
    int                     race = pMobIndex->race;
    MPROG_LIST             *pMprog = NULL;
    char                    buf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    int                     temp = 0;

    fprintf( fp, "#%d\n", pMobIndex->vnum );
    fprintf( fp, "%s~\n", pMobIndex->player_name );
    fprintf( fp, "%s~\n", pMobIndex->short_descr );
    fprintf( fp, "%s~\n", fix_string( pMobIndex->long_descr ) );
    fprintf( fp, "%s~\n", fix_string( pMobIndex->description ) );
    fprintf( fp, "%s~\n", race_table[race].name );
    fprintf( fp, "%s ", fwrite_flag( pMobIndex->act, buf ) );
    fprintf( fp, "%s ", fwrite_flag( pMobIndex->affected_by, buf ) );
    fprintf( fp, "%d %d\n", pMobIndex->alignment, pMobIndex->group );
    fprintf( fp, "%d ", pMobIndex->level );
    fprintf( fp, "%d ", pMobIndex->hitroll );
    fprintf( fp, "%dd%d+%d ", pMobIndex->hit[DICE_NUMBER],
             pMobIndex->hit[DICE_TYPE], pMobIndex->hit[DICE_BONUS] );
    fprintf( fp, "%dd%d+%d ", pMobIndex->mana[DICE_NUMBER],
             pMobIndex->mana[DICE_TYPE], pMobIndex->mana[DICE_BONUS] );
    fprintf( fp, "%dd%d+%d ", pMobIndex->damage[DICE_NUMBER],
             pMobIndex->damage[DICE_TYPE], pMobIndex->damage[DICE_BONUS] );
    fprintf( fp, "%s\n", attack_table[pMobIndex->dam_type].name );
    fprintf( fp, "%d %d %d %d\n",
             pMobIndex->ac[AC_PIERCE] / 10,
             pMobIndex->ac[AC_BASH] / 10,
             pMobIndex->ac[AC_SLASH] / 10, pMobIndex->ac[AC_EXOTIC] / 10 );
    fprintf( fp, "%s ", fwrite_flag( pMobIndex->off_flags, buf ) );
    fprintf( fp, "%s ", fwrite_flag( pMobIndex->imm_flags, buf ) );
    fprintf( fp, "%s ", fwrite_flag( pMobIndex->res_flags, buf ) );
    fprintf( fp, "%s\n", fwrite_flag( pMobIndex->vuln_flags, buf ) );
    fprintf( fp, "%s %s %s %d\n",
             position_table[pMobIndex->start_pos].short_name,
             position_table[pMobIndex->default_pos].short_name,
             sex_table[pMobIndex->sex].name, pMobIndex->wealth );
    fprintf( fp, "%s ", fwrite_flag( pMobIndex->form, buf ) );
    fprintf( fp, "%s ", fwrite_flag( pMobIndex->parts, buf ) );

    fprintf( fp, "%s ", size_table[pMobIndex->size].name );
    fprintf( fp, "%s\n",
             IS_NULLSTR( pMobIndex->material ) ? pMobIndex->material : "unknown" );

    if ( ( temp = DIF( race_table[race].act, pMobIndex->act ) ) )
        fprintf( fp, "F act %s\n", fwrite_flag( temp, buf ) );

    if ( ( temp = DIF( race_table[race].aff, pMobIndex->affected_by ) ) )
        fprintf( fp, "F aff %s\n", fwrite_flag( temp, buf ) );

    if ( ( temp = DIF( race_table[race].off, pMobIndex->off_flags ) ) )
        fprintf( fp, "F off %s\n", fwrite_flag( temp, buf ) );

    if ( ( temp = DIF( race_table[race].imm, pMobIndex->imm_flags ) ) )
        fprintf( fp, "F imm %s\n", fwrite_flag( temp, buf ) );

    if ( ( temp = DIF( race_table[race].res, pMobIndex->res_flags ) ) )
        fprintf( fp, "F res %s\n", fwrite_flag( temp, buf ) );

    if ( ( temp = DIF( race_table[race].vuln, pMobIndex->vuln_flags ) ) )
        fprintf( fp, "F vul %s\n", fwrite_flag( temp, buf ) );

    if ( ( temp = DIF( race_table[race].form, pMobIndex->form ) ) )
        fprintf( fp, "F for %s\n", fwrite_flag( temp, buf ) );

    if ( ( temp = DIF( race_table[race].parts, pMobIndex->parts ) ) )
        fprintf( fp, "F par %s\n", fwrite_flag( temp, buf ) );

    for ( pMprog = pMobIndex->mprogs; pMprog; pMprog = pMprog->next )
    {
        fprintf( fp, "M %s %d %s~\n",
                 mprog_type_to_name( pMprog->trig_type ), pMprog->vnum,
                 pMprog->trig_phrase );
    }

    return;
}

/*****************************************************************************
 Name:                save_mobiles
 Purpose:        Save #MOBILES secion of an area file.
 Called by:        save_area(olc_save.c).
 Notes:         Changed for ROM OLC.
 ****************************************************************************/
void save_mobiles( FILE * fp, AREA_DATA *pArea )
{
    int                     i = 0;
    MOB_INDEX_DATA         *pMob = NULL;

    fprintf( fp, "#MOBILES\n" );

    for ( i = pArea->min_vnum; i <= pArea->max_vnum; i++ )
    {
        if ( ( pMob = get_mob_index( i ) ) )
            save_mobile( fp, pMob );
    }

    fprintf( fp, "#0\n\n\n\n" );
    return;
}

/*****************************************************************************
 Name:                save_object
 Purpose:        Save one object to file.
                new ROM format saving -- Hugin
 Called by:        save_objects (below).
 ****************************************************************************/
void save_object( FILE * fp, OBJ_INDEX_DATA *pObjIndex )
{
    char                    letter = '\0';
    AFFECT_DATA            *pAf = NULL;
    EXTRA_DESCR_DATA       *pEd = NULL;
    char                    buf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";

    fprintf( fp, "#%d\n", pObjIndex->vnum );
    fprintf( fp, "%s~\n", pObjIndex->name );
    fprintf( fp, "%s~\n", pObjIndex->short_descr );
    fprintf( fp, "%s~\n", fix_string( pObjIndex->description ) );
    fprintf( fp, "%s~\n", pObjIndex->material );
    fprintf( fp, "%s ", item_name( pObjIndex->item_type ) );
    fprintf( fp, "%s ", fwrite_flag( pObjIndex->extra_flags, buf ) );
    fprintf( fp, "%s\n", fwrite_flag( pObjIndex->wear_flags, buf ) );

/*
 *  Using fwrite_flag to write most values gives a strange
 *  looking area file, consider making a case for each
 *  item type later.
 */

    switch ( pObjIndex->item_type )
    {
        default:
            fprintf( fp, "%s ", fwrite_flag( pObjIndex->value[0], buf ) );
            fprintf( fp, "%s ", fwrite_flag( pObjIndex->value[1], buf ) );
            fprintf( fp, "%s ", fwrite_flag( pObjIndex->value[2], buf ) );
            fprintf( fp, "%s ", fwrite_flag( pObjIndex->value[3], buf ) );
            fprintf( fp, "%s\n", fwrite_flag( pObjIndex->value[4], buf ) );
            break;

        case ITEM_DRINK_CON:
        case ITEM_FOUNTAIN:
            fprintf( fp, "%d %d '%s' %d %d\n",
                     pObjIndex->value[0],
                     pObjIndex->value[1],
                     liq_table[pObjIndex->value[2]].liq_name,
                     pObjIndex->value[3], pObjIndex->value[4] );
            break;

        case ITEM_CONTAINER:
            fprintf( fp, "%d %s %d %d %d\n",
                     pObjIndex->value[0],
                     fwrite_flag( pObjIndex->value[1], buf ),
                     pObjIndex->value[2], pObjIndex->value[3], pObjIndex->value[4] );
            break;

        case ITEM_WEAPON:
            fprintf( fp, "%s %d %d %s %s\n",
                     weapon_name( pObjIndex->value[0] ),
                     pObjIndex->value[1],
                     pObjIndex->value[2],
                     attack_table[pObjIndex->value[3]].name,
                     fwrite_flag( pObjIndex->value[4], buf ) );
            break;

        case ITEM_PILL:
        case ITEM_POTION:
        case ITEM_SCROLL:
            fprintf( fp, "%d '%s' '%s' '%s' '%s'\n",
                     pObjIndex->value[0] > 0 ? pObjIndex->value[0] : 0,
                     pObjIndex->value[1] !=
                     -1 ? skill_table[pObjIndex->value[1]].name : "",
                     pObjIndex->value[2] !=
                     -1 ? skill_table[pObjIndex->value[2]].name : "",
                     pObjIndex->value[3] !=
                     -1 ? skill_table[pObjIndex->value[3]].name : "",
                     pObjIndex->value[4] !=
                     -1 ? skill_table[pObjIndex->value[4]].name : "" );
            break;

        case ITEM_STAFF:
        case ITEM_WAND:
            fprintf( fp, "%d %d %d '%s' %d\n",
                     pObjIndex->value[0],
                     pObjIndex->value[1],
                     pObjIndex->value[2],
                     pObjIndex->value[3] != -1 ?
                     skill_table[pObjIndex->value[3]].name : "", pObjIndex->value[4] );
            break;
    }

    fprintf( fp, "%d ", pObjIndex->level );
    fprintf( fp, "%d ", pObjIndex->weight );
    fprintf( fp, "%d ", pObjIndex->cost );

    if ( pObjIndex->condition > 90 )
        letter = 'P';
    else if ( pObjIndex->condition > 75 )
        letter = 'G';
    else if ( pObjIndex->condition > 50 )
        letter = 'A';
    else if ( pObjIndex->condition > 25 )
        letter = 'W';
    else if ( pObjIndex->condition > 10 )
        letter = 'D';
    else if ( pObjIndex->condition > 0 )
        letter = 'B';
    else
        letter = 'R';

    fprintf( fp, "%c\n", letter );

    for ( pAf = pObjIndex->affected; pAf; pAf = pAf->next )
    {
        if ( pAf->where == TO_OBJECT || pAf->bitvector == 0 )
            fprintf( fp, "A\n%d %d\n", pAf->location, pAf->modifier );
        else
        {
            fprintf( fp, "F\n" );

            switch ( pAf->where )
            {
                case TO_AFFECTS:
                    fprintf( fp, "A " );
                    break;
                case TO_IMMUNE:
                    fprintf( fp, "I " );
                    break;
                case TO_RESIST:
                    fprintf( fp, "R " );
                    break;
                case TO_VULN:
                    fprintf( fp, "V " );
                    break;
                default:
                    log_error( "olc_save: Invalid Affect->where" );
                    break;
            }

            fprintf( fp, "%d %d %s\n", pAf->location, pAf->modifier,
                     fwrite_flag( pAf->bitvector, buf ) );
        }
    }

    for ( pEd = pObjIndex->extra_descr; pEd; pEd = pEd->next )
    {
        fprintf( fp, "E\n%s~\n%s~\n", pEd->keyword, fix_string( pEd->description ) );
    }

    return;
}

/*****************************************************************************
 Name:                save_objects
 Purpose:        Save #OBJECTS section of an area file.
 Called by:        save_area(olc_save.c).
 Notes:         Changed for ROM OLC.
 ****************************************************************************/
void save_objects( FILE * fp, AREA_DATA *pArea )
{
    int                     i = 0;
    OBJ_INDEX_DATA         *pObj = NULL;

    fprintf( fp, "#OBJECTS\n" );

    for ( i = pArea->min_vnum; i <= pArea->max_vnum; i++ )
    {
        if ( ( pObj = get_obj_index( i ) ) )
            save_object( fp, pObj );
    }

    fprintf( fp, "#0\n\n\n\n" );
    return;
}

/*****************************************************************************
 Name:                save_rooms
 Purpose:        Save #ROOMS section of an area file.
 Called by:        save_area(olc_save.c).
 ****************************************************************************/
void save_rooms( FILE * fp, AREA_DATA *pArea )
{
    ROOM_INDEX_DATA        *pRoomIndex = NULL;
    EXTRA_DESCR_DATA       *pEd = NULL;
    EXIT_DATA              *pExit = NULL;
    int                     iHash = 0;
    int                     door = 0;

    fprintf( fp, "#ROOMS\n" );
    for ( iHash = 0; iHash < MAX_KEY_HASH; iHash++ )
    {
        for ( pRoomIndex = room_index_hash[iHash]; pRoomIndex;
              pRoomIndex = pRoomIndex->next )
        {
            if ( pRoomIndex->area == pArea )
            {
                fprintf( fp, "#%d\n", pRoomIndex->vnum );
                fprintf( fp, "%s~\n", pRoomIndex->name );
                fprintf( fp, "%s~\n", fix_string( pRoomIndex->description ) );
                fprintf( fp, "0 " );
                fprintf( fp, "%d ", pRoomIndex->room_flags );
                fprintf( fp, "%d\n", pRoomIndex->sector_type );

                for ( pEd = pRoomIndex->extra_descr; pEd; pEd = pEd->next )
                {
                    fprintf( fp, "E\n%s~\n%s~\n", pEd->keyword,
                             fix_string( pEd->description ) );
                }
                for ( door = 0; door < MAX_DIR; door++ )        /* I hate this! */
                {
                    if ( ( pExit = pRoomIndex->exit[door] ) && pExit->u1.to_room )
                    {
                        int                     locks = 0;

                        /*
                         * HACK : TO PREVENT EX_LOCKED etc without EX_ISDOOR to stop
                         * booting the mud 
                         */
                        if ( IS_SET( pExit->rs_flags, EX_CLOSED )
                             || IS_SET( pExit->rs_flags, EX_LOCKED )
                             || IS_SET( pExit->rs_flags, EX_PICKPROOF )
                             || IS_SET( pExit->rs_flags, EX_NOPASS )
                             || IS_SET( pExit->rs_flags, EX_EASY )
                             || IS_SET( pExit->rs_flags, EX_HARD )
                             || IS_SET( pExit->rs_flags, EX_INFURIATING )
                             || IS_SET( pExit->rs_flags, EX_NOCLOSE )
                             || IS_SET( pExit->rs_flags, EX_NOLOCK ) )
                            SET_BIT( pExit->rs_flags, EX_ISDOOR );
                        else
                            REMOVE_BIT( pExit->rs_flags, EX_ISDOOR );

                        /*
                         * THIS SUCKS but it's backwards compatible 
                         */
                        /*
                         * NOTE THAT EX_NOCLOSE NOLOCK etc aren't being saved 
                         */
                        if ( IS_SET( pExit->rs_flags, EX_ISDOOR )
                             && ( !IS_SET( pExit->rs_flags, EX_PICKPROOF ) )
                             && ( !IS_SET( pExit->rs_flags, EX_NOPASS ) ) )
                            locks = 1;
                        if ( IS_SET( pExit->rs_flags, EX_ISDOOR )
                             && ( IS_SET( pExit->rs_flags, EX_PICKPROOF ) )
                             && ( !IS_SET( pExit->rs_flags, EX_NOPASS ) ) )
                            locks = 2;
                        if ( IS_SET( pExit->rs_flags, EX_ISDOOR )
                             && ( !IS_SET( pExit->rs_flags, EX_PICKPROOF ) )
                             && ( IS_SET( pExit->rs_flags, EX_NOPASS ) ) )
                            locks = 3;
                        if ( IS_SET( pExit->rs_flags, EX_ISDOOR )
                             && ( IS_SET( pExit->rs_flags, EX_PICKPROOF ) )
                             && ( IS_SET( pExit->rs_flags, EX_NOPASS ) ) )
                            locks = 4;

                        fprintf( fp, "D%d\n", pExit->orig_door );
                        fprintf( fp, "%s~\n", fix_string( pExit->description ) );
                        fprintf( fp, "%s~\n", pExit->keyword );
                        fprintf( fp, "%d %d %d\n", locks,
                                 pExit->key, pExit->u1.to_room->vnum );
                    }
                }
                if ( pRoomIndex->mana_rate != 100 || pRoomIndex->heal_rate != 100 )
                    fprintf( fp, "M %d H %d\n", pRoomIndex->mana_rate,
                             pRoomIndex->heal_rate );
                if ( pRoomIndex->clan > 0 )
                    fprintf( fp, "C %s~\n", clan_table[pRoomIndex->clan].name );

                if ( !IS_NULLSTR( pRoomIndex->owner ) )
                    fprintf( fp, "O %s~\n", pRoomIndex->owner );

                fprintf( fp, "S\n" );
            }
        }
    }
    fprintf( fp, "#0\n\n\n\n" );
    return;
}

/*****************************************************************************
 Name:                save_specials
 Purpose:        Save #SPECIALS section of area file.
 Called by:        save_area(olc_save.c).
 ****************************************************************************/
void save_specials( FILE * fp, AREA_DATA *pArea )
{
    int                     iHash = 0;
    MOB_INDEX_DATA         *pMobIndex = NULL;

    fprintf( fp, "#SPECIALS\n" );

    for ( iHash = 0; iHash < MAX_KEY_HASH; iHash++ )
    {
        for ( pMobIndex = mob_index_hash[iHash]; pMobIndex; pMobIndex = pMobIndex->next )
        {
            if ( pMobIndex && pMobIndex->area == pArea && pMobIndex->spec_fun )
            {
#if defined( VERBOSE )
                fprintf( fp, "M %d %s Load to: %s\n", pMobIndex->vnum,
                         spec_name( pMobIndex->spec_fun ), pMobIndex->short_descr );
#else
                fprintf( fp, "M %d %s\n", pMobIndex->vnum,
                         spec_name( pMobIndex->spec_fun ) );
#endif
            }
        }
    }

    fprintf( fp, "S\n\n\n\n" );
    return;
}

/*
 * This function is obsolete.  It it not needed but has been left here
 * for historical reasons.  It is used currently for the same reason.
 *
 * I don't think it's obsolete in ROM -- Hugin.
 */
void save_door_resets( FILE * fp, AREA_DATA *pArea )
{
    int                     iHash = 0;
    ROOM_INDEX_DATA        *pRoomIndex = NULL;
    EXIT_DATA              *pExit = NULL;
    int                     door = 0;

    for ( iHash = 0; iHash < MAX_KEY_HASH; iHash++ )
    {
        for ( pRoomIndex = room_index_hash[iHash]; pRoomIndex;
              pRoomIndex = pRoomIndex->next )
        {
            if ( pRoomIndex->area == pArea )
            {
                for ( door = 0; door < MAX_DIR; door++ )
                {
                    if ( ( pExit = pRoomIndex->exit[door] )
                         && pExit->u1.to_room
                         && ( IS_SET( pExit->rs_flags, EX_CLOSED )
                              || IS_SET( pExit->rs_flags, EX_LOCKED ) ) )
#if defined( VERBOSE )
                        fprintf( fp, "D 0 %d %d %d The %s door of %s is %s\n",
                                 pRoomIndex->vnum,
                                 pExit->orig_door,
                                 IS_SET( pExit->rs_flags, EX_LOCKED ) ? 2 : 1,
                                 dir_name[pExit->orig_door],
                                 pRoomIndex->name,
                                 IS_SET( pExit->rs_flags,
                                         EX_LOCKED ) ? "closed and locked" : "closed" );
#endif
#if !defined( VERBOSE )
                    fprintf( fp, "D 0 %d %d %d\n",
                             pRoomIndex->vnum,
                             pExit->orig_door,
                             IS_SET( pExit->rs_flags, EX_LOCKED ) ? 2 : 1 );
#endif
                }
            }
        }
    }
    return;
}

/*****************************************************************************
 Name:                save_resets
 Purpose:        Saves the #RESETS section of an area file.
 Called by:        save_area(olc_save.c)
 ****************************************************************************/
void save_resets( FILE * fp, AREA_DATA *pArea )
{
    RESET_DATA             *pReset = NULL;
    MOB_INDEX_DATA         *pLastMob = NULL;
    OBJ_INDEX_DATA         *pLastObj = NULL;
    ROOM_INDEX_DATA        *pRoom = NULL;
    int                     iHash = 0;

    fprintf( fp, "#RESETS\n" );

    save_door_resets( fp, pArea );

    for ( iHash = 0; iHash < MAX_KEY_HASH; iHash++ )
    {
        for ( pRoom = room_index_hash[iHash]; pRoom; pRoom = pRoom->next )
        {
            if ( pRoom->area == pArea )
            {
                for ( pReset = pRoom->reset_first; pReset; pReset = pReset->next )
                {
                    switch ( pReset->command )
                    {
                        default:
                            log_error( "Save_resets: bad command %c.", pReset->command );
                            break;

#if defined( VERBOSE )
                        case 'M':
                            pLastMob = get_mob_index( pReset->arg1 );
                            fprintf( fp, "M 0 %d %d %d %d Load %s\n",
                                     pReset->arg1,
                                     pReset->arg2,
                                     pReset->arg3, pReset->arg4, pLastMob->short_descr );
                            break;

                        case 'O':
                            pLastObj = get_obj_index( pReset->arg1 );
                            pRoom = get_room_index( pReset->arg3 );
                            fprintf( fp, "O 0 %d 0 %d %s loaded to %s\n",
                                     pReset->arg1,
                                     pReset->arg3,
                                     capitalize( pLastObj->short_descr ), pRoom->name );
                            break;

                        case 'P':
                            pLastObj = get_obj_index( pReset->arg1 );
                            fprintf( fp, "P 0 %d %d %d %d %s put inside %s\n",
                                     pReset->arg1,
                                     pReset->arg2,
                                     pReset->arg3,
                                     pReset->arg4,
                                     capitalize( get_obj_index( pReset->arg1 )->
                                                 short_descr ), pLastObj->short_descr );
                            break;

                        case 'G':
                            fprintf( fp, "G 0 %d 0 %s is given to %s\n",
                                     pReset->arg1,
                                     capitalize( get_obj_index( pReset->arg1 )->
                                                 short_descr ),
                                     pLastMob ? pLastMob->short_descr : "!NO_MOB!" );
                            if ( !pLastMob )
                            {
                                log_error( "Save_resets: !NO_MOB! in [%s]",
                                           pArea->file_name );
                            }
                            break;

                        case 'E':
                            fprintf( fp, "E 0 %d 0 %d %s is loaded %s of %s\n",
                                     pReset->arg1,
                                     pReset->arg3,
                                     capitalize( get_obj_index( pReset->arg1 )->
                                                 short_descr ),
                                     flag_string( wear_loc_strings, pReset->arg3 ),
                                     pLastMob ? pLastMob->short_descr : "!NO_MOB!" );
                            if ( !pLastMob )
                            {
                                log_error( "Save_resets: !NO_MOB! in [%s]",
                                           pArea->file_name );
                            }
                            break;

                        case 'D':
                            break;

                        case 'R':
                            pRoom = get_room_index( pReset->arg1 );
                            fprintf( fp, "R 0 %d %d Randomize %s\n",
                                     pReset->arg1, pReset->arg2, pRoom->name );
                            break;
#else
                        case 'M':
                            pLastMob = get_mob_index( pReset->arg1 );
                            fprintf( fp, "M 0 %d %d %d %d\n",
                                     pReset->arg1,
                                     pReset->arg2, pReset->arg3, pReset->arg4 );
                            break;

                        case 'O':
                            pLastObj = get_obj_index( pReset->arg1 );
                            pRoom = get_room_index( pReset->arg3 );
                            fprintf( fp, "O 0 %d 0 %d\n", pReset->arg1, pReset->arg3 );
                            break;

                        case 'P':
                            pLastObj = get_obj_index( pReset->arg1 );
                            fprintf( fp, "P 0 %d %d %d %d\n",
                                     pReset->arg1,
                                     pReset->arg2, pReset->arg3, pReset->arg4 );
                            break;

                        case 'G':
                            fprintf( fp, "G 0 %d 0\n", pReset->arg1 );
                            if ( !pLastMob )
                            {
                                log_error( "Save_resets: !NO_MOB! in [%s]",
                                           pArea->file_name );
                            }
                            break;

                        case 'E':
                            fprintf( fp, "E 0 %d 0 %d\n", pReset->arg1, pReset->arg3 );
                            if ( !pLastMob )
                            {
                                log_error( "Save_resets: !NO_MOB! in [%s]",
                                           pArea->file_name );
                            }
                            break;

                        case 'D':
                            break;

                        case 'R':
                            pRoom = get_room_index( pReset->arg1 );
                            fprintf( fp, "R 0 %d %d\n", pReset->arg1, pReset->arg2 );
                            break;
#endif
                    }
                }
            }                                          /* End if correct area */
        }                                              /* End for pRoom */
    }                                                  /* End for iHash */
    fprintf( fp, "S\n\n\n\n" );
    return;
}

/*****************************************************************************
 Name:                save_shops
 Purpose:        Saves the #SHOPS section of an area file.
 Called by:        save_area(olc_save.c)
 ****************************************************************************/
void save_shops( FILE * fp, AREA_DATA *pArea )
{
    SHOP_DATA              *pShopIndex = NULL;
    MOB_INDEX_DATA         *pMobIndex = NULL;
    int                     iTrade = 0;
    int                     iHash = 0;

    fprintf( fp, "#SHOPS\n" );

    for ( iHash = 0; iHash < MAX_KEY_HASH; iHash++ )
    {
        for ( pMobIndex = mob_index_hash[iHash]; pMobIndex; pMobIndex = pMobIndex->next )
        {
            if ( pMobIndex && pMobIndex->area == pArea && pMobIndex->pShop )
            {
                pShopIndex = pMobIndex->pShop;

                fprintf( fp, "%d ", pShopIndex->keeper );
                for ( iTrade = 0; iTrade < MAX_TRADE; iTrade++ )
                {
                    if ( pShopIndex->buy_type[iTrade] != 0 )
                    {
                        fprintf( fp, "%d ", pShopIndex->buy_type[iTrade] );
                    }
                    else
                        fprintf( fp, "0 " );
                }
                fprintf( fp, "%d %d ", pShopIndex->profit_buy, pShopIndex->profit_sell );
                fprintf( fp, "%d %d\n", pShopIndex->open_hour, pShopIndex->close_hour );
            }
        }
    }

    fprintf( fp, "0\n\n\n\n" );
    return;
}

void save_helps( FILE * fp, HELP_AREA *ha )
{
    HELP_DATA              *help = ha->first;

    fprintf( fp, "#HELPS\n" );

    for ( ; help; help = help->next_area )
    {
        fprintf( fp, "%d %s~\n", help->level, help->keyword.c_str());
	fprintf( fp, "%s~\n\n", fix_string( help->text.c_str() ) );
    }

    fprintf( fp, "-1 $~\n\n" );

    ha->changed = false;

    return;
}

void save_other_helps( CHAR_DATA *ch )
{
    HELP_AREA              *ha = NULL;
    FILE                   *fp = NULL;

    for ( ha = had_list; ha; ha = ha->next )
        if ( ha->changed == true )
        {
            fp = fopen( ha->filename, "w" );

            if ( !fp )
            {
                perror( ha->filename );
                return;
            }

            save_helps( fp, ha );

            if ( ch )
                ch_printf( ch, "%s\r\n", ha->filename );

            fprintf( fp, "#$\n" );
            fclose( fp );
        }

    return;
}

/*****************************************************************************
 Name:                save_area
 Purpose:        Save an area, note that this format is new.
 Called by:        do_asave(olc_save.c).
 ****************************************************************************/
void save_area( AREA_DATA *pArea )
{
    FILE                   *fp = NULL;

    if ( !( fp = fopen( pArea->file_name, "w" ) ) )
    {
        log_error( "Open_area: fopen" );
        perror( pArea->file_name );
    }

    fprintf( fp, "#AREADATA\n" );
    fprintf( fp, "Name %s~\n", pArea->name );
    fprintf( fp, "Builders %s~\n", fix_string( pArea->builders ) );
    fprintf( fp, "VNUMs %d %d\n", pArea->min_vnum, pArea->max_vnum );
    fprintf( fp, "Credits %s~\n", pArea->credits );
    fprintf( fp, "Security %d\n", pArea->security );
    fprintf( fp, "End\n\n\n\n" );

    save_mobiles( fp, pArea );
    save_objects( fp, pArea );
    save_rooms( fp, pArea );
    save_specials( fp, pArea );
    save_resets( fp, pArea );
    save_shops( fp, pArea );
    save_mobprogs( fp, pArea );

    if ( pArea->helps && pArea->helps->first )
        save_helps( fp, pArea->helps );

    fprintf( fp, "#$\n" );

    fclose( fp );
    return;
}

/*****************************************************************************
 Name:                do_asave
 Purpose:        Entry point for saving area data.
 Called by:        interpreter(interp.c)
 ****************************************************************************/
void do_asave( CHAR_DATA *ch, const char *argument )
{
    char                    arg1[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
    char                    local_argument[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
    AREA_DATA              *pArea = NULL;
    int                     value = 0;
    int                     sec = 0;

    if ( !ch )                                         /* Do an autosave */
        sec = 9;
    else if ( !IS_NPC( ch ) )
        sec = ch->pcdata->security;
    else
        sec = 0;

#if 0
    {
        save_area_list(  );
        for ( pArea = area_first; pArea; pArea = pArea->next )
        {
            save_area( pArea );
            REMOVE_BIT( pArea->area_flags, AREA_CHANGED );
        }
        return;
    }
#endif

    strcpy( local_argument, argument );
    smash_tilde( local_argument );
    strcpy( arg1, local_argument );

    if ( arg1[0] == '\0' )
    {
        if ( ch )
        {
            ch_printf( ch, "Syntax:\r\n" );
            ch_printf( ch, "  asave <vnum>   - saves a particular area\r\n" );
            ch_printf( ch, "  asave list     - saves the area.lst file\r\n" );
            ch_printf( ch, "  asave area     - saves the area being edited\r\n" );
            ch_printf( ch, "  asave changed  - saves all changed zones\r\n" );
            ch_printf( ch, "  asave world    - saves the world! (db dump)\r\n" );
            ch_printf( ch, "\r\n" );
        }

        return;
    }

    /*
     * Snarf the value (which need not be numeric). 
     */
    value = atoi( arg1 );
    if ( !( pArea = get_area_data( value ) ) && is_number( arg1 ) )
    {
        if ( ch )
            ch_printf( ch, "That area does not exist.\r\n" );
        return;
    }

    /*
     * Save area of given vnum. 
     */
    if ( is_number( arg1 ) )
    {
        if ( ch && !IS_BUILDER( ch, pArea ) )
        {
            ch_printf( ch, "You are not a builder for this area.\r\n" );
            return;
        }

        save_area_list(  );
        save_area( pArea );

        return;
    }

    /*
     * Save the world, only authorized areas. 
     */
    if ( !str_cmp( "world", arg1 ) )
    {
        save_area_list(  );
        for ( pArea = area_first; pArea; pArea = pArea->next )
        {
            /*
             * Builder must be assigned this area. 
             */
            if ( ch && !IS_BUILDER( ch, pArea ) )
                continue;

            save_area( pArea );
            REMOVE_BIT( pArea->area_flags, AREA_CHANGED );
        }

        if ( ch )
            ch_printf( ch, "You saved the world.\r\n" );

        save_other_helps( NULL );

        return;
    }

    /*
     * Save changed areas, only authorized areas. 
     */
    if ( !str_cmp( "changed", arg1 ) )
    {
        char                    buf[MAX_INPUT_LENGTH];

        save_area_list(  );

        if ( ch )
            ch_printf( ch, "Saved zones:\r\n" );
        else
            log_string( "Saved zones:" );

        sprintf( buf, "None.\r\n" );

        for ( pArea = area_first; pArea; pArea = pArea->next )
        {
            /*
             * Builder must be assigned this area. 
             */
            if ( ch && !IS_BUILDER( ch, pArea ) )
                continue;

            /*
             * Save changed areas. 
             */
            if ( IS_SET( pArea->area_flags, AREA_CHANGED ) )
            {
                save_area( pArea );
                sprintf( buf, "%24s - '%s'", pArea->name, pArea->file_name );
                if ( ch )
                {
                    send_to_char( buf, ch );
                    send_to_char( "\r\n", ch );
                }
                else
                    log_string( buf );
                REMOVE_BIT( pArea->area_flags, AREA_CHANGED );
            }
        }

        save_other_helps( ch );

        if ( !str_cmp( buf, "None.\r\n" ) )
        {
            if ( ch )
                ch_printf( ch, "%s", buf );
            else
                log_string( "None." );
        }
        return;
    }

    /*
     * Save the area.lst file. 
     */
    if ( !str_cmp( arg1, "list" ) )
    {
        save_area_list(  );
        return;
    }

    /*
     * Save area being edited, if authorized. 
     */
    if ( !str_cmp( arg1, "area" ) )
    {
        if ( !ch || !ch->desc )
            return;

        /*
         * Is character currently editing. 
         */
        if ( ch->desc->editor == ED_NONE )
        {
            ch_printf( ch, "You are not editing an area, "
                       "therefore an area vnum is required.\r\n" );
            return;
        }

        /*
         * Find the area to save. 
         */
        switch ( ch->desc->editor )
        {
            case ED_AREA:
                pArea = ( AREA_DATA * ) ch->desc->pEdit;
                break;
            case ED_ROOM:
                pArea = ch->in_room->area;
                break;
            case ED_OBJECT:
                pArea = ( ( OBJ_INDEX_DATA * ) ch->desc->pEdit )->area;
                break;
            case ED_MOBILE:
                pArea = ( ( MOB_INDEX_DATA * ) ch->desc->pEdit )->area;
                break;
            case ED_HELP:
                ch_printf( ch, "Recording area : " );
                save_other_helps( ch );
                return;
            default:
                pArea = ch->in_room->area;
                break;
        }

        if ( !IS_BUILDER( ch, pArea ) )
        {
            ch_printf( ch, "You are not a builder for this area.\r\n" );
            return;
        }

        save_area_list(  );
        save_area( pArea );
        REMOVE_BIT( pArea->area_flags, AREA_CHANGED );
        ch_printf( ch, "Area saved.\r\n" );
        return;
    }

    /*
     * Show correct syntax. 
     */
    if ( ch )
        do_asave( ch, "" );

    return;
}