view urogue/dictutil.c @ 296:000b1c5b8d63

UltraRogue: fix inventory collision after save and restore. Inventory letters are based on "identifiers" stored in objects' o_ident field. Identifiers are allocated by get_ident(), which keeps a list of objects that have them, to avoid giving the same identifier to multiple objects. The list is not stored in the savefile, so after restore, get_ident() was not aware of existing identifiers. This resulted in picked-up objects having the same inventory letters as objects restored from the file. The restore code now adds all objects with identifiers to the list.
author John "Elwin" Edwards
date Mon, 15 Jan 2018 20:20:35 -0500
parents c4b12d2d1dcd
children 13b482bd9e66
line wrap: on
line source

/*
    dictutil.c

    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
    Copyright (C) 1995 Herb Chong
    All rights reserved.

    See the file LICENSE.TXT for full copyright and licensing information.
*/

/*************************************************************************
**   Utilities for Dictionary Maintenence Functions
*************************************************************************/

static char sccsid[] = "%W% %G%";

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#if !defined(OS2) && !defined(_WIN32)
   #include <unistd.h>
#else
   #include <io.h>
   #include <fcntl.h>
#endif

#include "dict.h"
#include "dictutil.h"
#include "rogue.h"

int   dict_trace;
FILE  *ft;


/***********
**  Read 'count' characters into 'buffer' at 'offset' in a binary file
**  Return 0 on success; -1 on failure;
***********/

int block_read( FILE *fi , char *buffer , size_t count , long offset )
{
   if ( fseek(fi,offset,SEEK_SET) == -1 )
      return( -1 );

   if ( fread(buffer,1,count,fi) != count )
      return( -1 );
   return( 0 );
}

/***********
**  Write 'count' characters from 'buffer' to a binary file.
**  Return -1 on failure; 0 on success.
***********/

int block_write( FILE *fo , char *buffer , size_t count )
{
   if ( fwrite(buffer,1,count,fo) != count )
      return( -1 );
   return( 0 );
}

/***********
**  Load a dictionary table entry with id TOC_id into memory pointed to by block.
**  Update the dictionary TOC.
**  If *block=NULL, allocate the block of memory.
**  Return 0 on success; -1 on failure.
**  Set dt_entry->ptr to where the block is stored.
***********/

void *dict_load_block( DICTIONARY *dict , char *toc_id ,
                          FILE *fi , void *block )
{  DICT_TOC_ENTRY  *dt_entry;
   static void     *ptr;
   int             index, ret_code;

   index = dict_toc_index( dict , toc_id );
   if ( index != -1 ) {  /* Found the id */
      dt_entry = &(dict->toc[index]);
   } else {
      signal_error( "dict_load_block: could not find TOC_id" , toc_id , 1 );
      return( NULL );
   } /* endif */

   if ( block == NULL ) {
      ptr = malloc( dt_entry->size );
      if ( dict_trace > 3 ) {
         fprintf( ft , "\ndict_load_block allocates %lx bytes at location %p\n" ,
                   dt_entry->size , ptr );
      } /* endif */
   } else {
      ptr = block;
      if ( dict_trace > 3 ) {
         fprintf( ft , "\ndict_load_block uses memory at location %p\n" , ptr );
      } /* endif */
   } /* endif */
   if ( ptr == NULL ) {
      signal_error( "dict_load_block: alloc failed " , toc_id , 1 );
      return( NULL );
   } /* endif */

   ret_code = block_read( fi ,
                          (char*)ptr ,
                          dt_entry->size ,
                          dt_entry->offset );
   if ( ret_code == -1 )
      return( NULL );

   if ( dt_entry->checksum !=
        compute_checksum( dt_entry->size , (char*)ptr ) ) {
      signal_error( "dict_load_block: invalid checksum ", toc_id, 1);
      return( NULL );
   } /* endif */

   dt_entry->ptr = ptr;

   if ( dict_trace > 3 ) {
      fprintf( ft , "\nLoaded block\nTOC entry:    id:%s  offset:%lx  size:%lx  ptr:%p  checksum:%lx  type:%d\n" ,
               dict->toc[index].id , dict->toc[index].offset ,
               dict->toc[index].size , dict->toc[index].ptr ,
               dict->toc[index].checksum , dict->toc[index].type );
   } /* endif */

   return( ptr );
}

/***********
**  Save a dictionary table entry.
**  Update the dictionary TOC entry offset and checksum fields.
**  Return 0 on success, -1 on failure.
**  Note: It is assumed that the size and pointer fields in TOC entry are
**        already up to date; i.e., that they are consistent with the current
**        location and size of the block being written. This is essential
**        because the table of contents must have already been written
**        into the file.
***********/

BOOLEANC dict_save_block( DICTIONARY *dict , char *toc_id , FILE *fo )
{  DICT_TOC_ENTRY   *dt_entry;
   int              index, ret_code;
   char             *block;

   index = dict_toc_index( dict , toc_id );
   if ( index == -1 ) {
      signal_error( "dict_save_block: id not found " , toc_id , 1 );
      return( FALSE );
   } /* endif */
   dt_entry = &(dict->toc[index]);
   block = (char*)(dt_entry->ptr);

   if ( block == NULL ) {
      signal_error( "dict_save_block: NULL block " , toc_id , 1 );
      return( FALSE );
   } /* endif */

   /* dt_entry->offset = fseek( fo , 0 , SEEK_END ); */
   dt_entry->checksum = compute_checksum( dt_entry->size , block );
   ret_code = block_write( fo , dt_entry->ptr , dt_entry->size );
   if ( ret_code == -1 ) {
      signal_error( "dict_save_block: block_write failed " , toc_id , 1 );
      return( FALSE );
   } /* endif */

   if ( dict_trace > 3 ) {
      fprintf( ft , "\nStored block\nTOC entry:           id:%s  offset:%lx  size:%lx  ptr:%p  checksum:%lx  type:%d\n" ,
               dict->toc[index].id , dict->toc[index].offset ,
               dict->toc[index].size , dict->toc[index].ptr ,
               dict->toc[index].checksum , dict->toc[index].type );
   } /* endif */

   return( TRUE );
}

/***********
**  Look up and id in the table of contents.
**  Return its index (-1 on failure).
***********/

int dict_toc_index( DICTIONARY *dict , char *toc_id )
{  int index;

   for ( index = 0 ; index < dict->sig->toc_size ; index++ ) {
           if ( strcmp(dict->toc[index].id,toc_id) == 0 )
                   return( index );
   } /* endfor */

   return( -1 );
}

/***********
**  Compute a block checksum.
**  (Currently just returns 0.)
***********/

unsigned long compute_checksum( size_t size , char *block )
{
    NOOP(size);
    NOOP(block);
    return( 0 );
}

/***********
**  Create a dictionary paramter entry.
***********/

DICT_PARM_ENTRY *dict_make_parm_entry( char *id , unsigned long value )
{  static DICT_PARM_ENTRY  *entry;

   entry = (DICT_PARM_ENTRY *) malloc( sizeof(DICT_PARM_ENTRY) );
   if ( entry == NULL )
      return(NULL);

   strncpy( entry->id , id , 13 );
   entry->value = value;

   return( entry );
}

/***********
**  Look up and id in the parameter array.
**  Return its index (-1 on failure).
***********/

int dict_parm_index( DICTIONARY *dict , char *parm_id )
{  long index;

   for ( index = 0 ; index < dict->sig->nparms ; index++ ) {
           if ( strcmp( dict->parm[index].id , parm_id ) == 0 )
                   return( (int) index );
   } /* endfor */

   return( -1 );
}

/***********
**  Reset table of contents offsets and checksums
**  in preparation for dict_save().
***********/

BOOLEANC dict_reset_toc_offsets( DICTIONARY *dict )
{  int  i;
   long offset;

   offset = sizeof(DICT_SIG)
          + dict->sig->toc_size * sizeof(DICT_TOC_ENTRY);
   for ( i = 0 ; i < dict->sig->toc_size ; i++ ) {
      dict->toc[i].offset = offset;
      offset += dict->toc[i].size;
      dict->toc[i].checksum =
         compute_checksum( dict->toc[i].size , dict->toc[i].ptr );
   } /* endfor */

   return( TRUE );
}

/***********
**  Load the names of the dictionary parameters.
**  14 parms
***********/

BOOLEANC dict_set_parm_ids( DICTIONARY *dict )
{
   if ( dict==NULL || dict->sig == NULL ) {
      signal_error( "dict_set_parm_ids: Allocate dict and signature first." , "" , 0 );
      return( FALSE );
   }
   dict->sig->nparms = 14;
   strcpy( dict->parm[0].id , "FLAGS_______" );
   strcpy( dict->parm[1].id , "ENTRY_COUNT_" );
   strcpy( dict->parm[2].id , "ARRAY_SIZE__" );
   strcpy( dict->parm[3].id , "ARRAY_USED__" );
   strcpy( dict->parm[4].id , "ARR_GROW_CT_" );
   strcpy( dict->parm[5].id , "STRING_MAX__" );
   strcpy( dict->parm[6].id , "STR_GROW_CT_" );
   strcpy( dict->parm[7].id , "LONG_CHAIN__" );
   strcpy( dict->parm[8].id , "ALLOW_CHAIN_" );
   strcpy( dict->parm[9].id , "HASH_TAB_SIZ" );
   strcpy( dict->parm[10].id , "HASH_MASK___" );
   strcpy( dict->parm[11].id , "HASH_GROW_CT" );
   strcpy( dict->parm[12].id , "CHECK_VALUE_" );
   strcpy( dict->parm[13].id , "SCAN_STR_IX_" );

   return( TRUE );
}

/***********
**  Set the dictionary parm structure from the values in the dict structure.
**  14 parms
***********/

BOOLEANC dict_set_parm_values( DICTIONARY *dict )
{  int  index;

   if ( (index=dict_parm_index(dict,"FLAGS_______")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->flags;

   if ( (index=dict_parm_index(dict,"ENTRY_COUNT_")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->entry_count;

   if ( (index=dict_parm_index(dict,"ARRAY_SIZE__")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->array_size;

   if ( (index=dict_parm_index(dict,"ARRAY_USED__")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->array_used;

   if ( (index=dict_parm_index(dict,"ARR_GROW_CT_")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->array_growth_count;

   if ( (index=dict_parm_index(dict,"STRING_MAX__")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->string_max;

   if ( (index=dict_parm_index(dict,"STR_GROW_CT_")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->string_growth_count;

   if ( (index=dict_parm_index(dict,"LONG_CHAIN__")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->longest_chain_length;

   if ( (index=dict_parm_index(dict,"ALLOW_CHAIN_")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->allowable_chain_length;

   if ( (index=dict_parm_index(dict,"HASH_TAB_SIZ")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->table_size;

   if ( (index=dict_parm_index(dict,"HASH_MASK___")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->hash_mask;

   if ( (index=dict_parm_index(dict,"HASH_GROW_CT")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->hash_growth_count;

   if ( (index=dict_parm_index(dict,"CHECK_VALUE_")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->check_value;

   if ( (index=dict_parm_index(dict,"SCAN_STR_IX_")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->scan_string_index;

   return( TRUE );
}


/***********
**  Set the values in the dict structure from the dictionary parm structure.
**  14 parms
***********/

BOOLEANC dict_set_parm_variables( DICTIONARY *dict )
{  int  index;

   if ( (index=dict_parm_index(dict,"FLAGS_______")) == -1 )
      return( FALSE );
   dict->flags = (unsigned long)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"ENTRY_COUNT_")) == -1 )
      return( FALSE );
   dict->entry_count = (long)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"ARRAY_SIZE__")) == -1 )
      return( FALSE );
   dict->array_size = (long)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"ARRAY_USED__")) == -1 )
      return( FALSE );
   dict->array_used = (long)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"ARR_GROW_CT_")) == -1 )
      return( FALSE );
   dict->array_growth_count = (int)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"STRING_MAX__")) == -1 )
      return( FALSE );
   dict->string_max = (long)dict->parm[index].value ;

   if ( (index=dict_parm_index(dict,"STR_GROW_CT_")) == -1 )
      return( FALSE );
   dict->string_growth_count = (int)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"LONG_CHAIN__")) == -1 )
      return( FALSE );
   dict->longest_chain_length = (int)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"ALLOW_CHAIN_")) == -1 )
      return( FALSE );
   dict->allowable_chain_length = (int)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"HASH_TAB_SIZ")) == -1 )
      return( FALSE );
   dict->table_size = (long)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"HASH_MASK___")) == -1 )
      return( FALSE );
   dict->hash_mask = (unsigned long)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"HASH_GROW_CT")) == -1 )
      return( FALSE );
   dict->hash_growth_count = (int)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"CHECK_VALUE_")) == -1 )
      return( FALSE );
   dict->check_value = (unsigned long)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"SCAN_STR_IX_")) == -1 )
      return( FALSE );
   dict->scan_string_index = (long)dict->parm[index].value;

   return( TRUE );
}

/***********
**  If dict_trace (global) > 0 , signal an error
**  If severity > 0 , abort
***********/

void signal_error( char *header , char *message , int severity )
{
  FILE *fpe;

  if ( dict_trace > 0 ) {
     printf( "%s: %s\n" , header , message );
     fpe = fopen( "ERROR.FIL" , "a" );
     fprintf( fpe , "\n%s: %s\n" , header , message );
     fclose( fpe );
  } /* endif */

  if ( severity > 0 )
     abort();
}